git-stash-commit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ module Git
2
+ module Stash
3
+ module Commit
4
+ VERSION = "1.0.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,164 @@
1
+ # ブランチ操作クラス
2
+ # 通常/detachedを等価的に扱えるラッパークラスを提供する
3
+
4
+ $:.unshift File.dirname(__FILE__)
5
+ require 'command.rb' # namespace cmd
6
+
7
+ module BranchFactory
8
+ extend self
9
+
10
+ # ブランチ名をもとに、通常/detachedブランチどちらかを判断し、返す
11
+ def find(name)
12
+ if Cmd::branchExist? name
13
+ Branch.new name
14
+ elsif Cmd::branchRefExist? name
15
+ DetachBranch.new name
16
+ else
17
+ nil
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ module BranchCommon
24
+ module CommitMode
25
+ ALL = '--all'
26
+ PATCH = '--patch'
27
+ end
28
+
29
+ def initialize(name, maketarget='')
30
+ @name = name
31
+ if exist?
32
+ # no-op
33
+ elsif maketarget != ''
34
+ make maketarget
35
+ else
36
+ raise 'Branch instanced failed'
37
+ end
38
+ end
39
+
40
+ def name
41
+ @name
42
+ end
43
+
44
+ def cherryPickNoCommit(target)
45
+ checkout
46
+ puts "[#{@name}]: git cherry-pick --no-commit \"#{target}\""
47
+ Cmd::exec "git cherry-pick --no-commit \"#{target}\""
48
+ end
49
+
50
+ attr_reader :name
51
+ end
52
+
53
+ # 通常ブランチ
54
+ class Branch
55
+ include BranchCommon
56
+
57
+ def checkout
58
+ Cmd::execQuiet "git checkout \"#{@name}\""
59
+ end
60
+ def delete(deletedBranch='', force=false)
61
+ if deletedBranch != ''
62
+ Cmd::execQuiet "git checkout \"#{deletedBranch}\""
63
+ end
64
+ opt = (force == true) ? '-D' : '-d'
65
+ revision = Cmd::revision @name
66
+ Cmd::execQuiet "git branch #{opt} \"#{@name}\""
67
+ puts "Deleted detached branch #{@name} (was #{revision})"
68
+ end
69
+ def rebase(upstream)
70
+ puts "[#{@name}]: git rebase \"#{upstream}\""
71
+ Cmd::execForRebase @name, "git rebase \"#{upstream}\" \"#{@name}\""
72
+ end
73
+ def rebaseOnto(newbase, upstream)
74
+ puts "[#{@name}]: git rebase --onto \"#{newbase}\" \"#{upstream}\" <SELF>"
75
+ Cmd::execForRebase @name, "git rebase --onto \"#{newbase}\" \"#{upstream}\" \"#{@name}\""
76
+ end
77
+ def reset(target='')
78
+ if target != ''
79
+ checkout
80
+ puts "[#{@name}]: git reset \"#{target}\""
81
+ Cmd::exec "git reset \"#{target}\""
82
+ else
83
+ puts "[#{@name}]: git reset"
84
+ Cmd::exec 'git reset'
85
+ end
86
+ end
87
+ def commit(mode=CommitMode::ALL, _msg='', &onFail)
88
+ msg = (_msg != '') ? "-m \"#{_msg}\"" : ''
89
+ begin
90
+ checkout
91
+ puts "[#{@name}]: git commit #{mode} ..."
92
+ Cmd::exec "git commit #{mode} #{msg}"
93
+ rescue => e
94
+ onFail.call if block_given?
95
+ raise e
96
+ end
97
+ end
98
+
99
+ private
100
+ def exist?
101
+ Cmd::branchExist? @name
102
+ end
103
+ def make(target)
104
+ Cmd::execQuiet "git branch \"#{@name}\" \"#{target}\""
105
+ puts "Maked branch #{@name} (was #{Cmd::revision @name})"
106
+ end
107
+ end
108
+
109
+ # detachedブランチ
110
+ class DetachBranch
111
+ include BranchCommon
112
+
113
+ def checkout
114
+ Cmd::execQuiet "git checkout --detach \"#{@name}\""
115
+ end
116
+ def delete(deletedBranch='', dummy=false)
117
+ if deletedBranch != ''
118
+ Cmd::execQuiet "git checkout \"#{deletedBranch}\""
119
+ end
120
+ revision = Cmd::revision @name
121
+ Cmd::execQuiet "git update-ref -d refs/#{@name}"
122
+ puts "Deleted detached branch #{@name} (was #{revision})"
123
+ end
124
+ def rebase(upstream)
125
+ puts "[#{@name}]: git rebase \"#{upstream}\""
126
+ Cmd::execForRebase @name, "git rebase \"#{upstream}\" \"#{@name}\" --exec \"git update-ref refs/#{@name} HEAD\""
127
+ end
128
+ def rebaseOnto(newbase, upstream)
129
+ puts "[#{@name}]: git rebase --onto \"#{newbase}\" \"#{upstream}\" <SELF>"
130
+ Cmd::execForRebase @name, "git rebase --onto \"#{newbase}\" \"#{upstream}\" \"#{@name}\" --exec \"git update-ref refs/#{@name} HEAD\""
131
+ end
132
+ def reset(target='')
133
+ if target != ''
134
+ checkout
135
+ puts "[#{@name}]: git reset \"#{target}\""
136
+ Cmd::exec "git reset \"#{target}\""
137
+ Cmd::execQuiet "git update-ref refs/#{@name} HEAD"
138
+ else
139
+ puts "[#{@name}]: git reset"
140
+ Cmd::exec 'git reset'
141
+ end
142
+ end
143
+ def commit(mode=CommitMode::ALL, _msg='', &onFail)
144
+ msg = (_msg != '') ? "-m \"#{_msg}\"" : ''
145
+ begin
146
+ checkout
147
+ puts "[#{@name}]: git commit #{mode} ..."
148
+ Cmd::exec "git commit #{mode} #{msg}"
149
+ Cmd::execQuiet "git update-ref refs/#{@name} HEAD"
150
+ rescue => e
151
+ onFail.call if block_given?
152
+ raise e
153
+ end
154
+ end
155
+
156
+ private
157
+ def exist?
158
+ Cmd::branchRefExist? @name
159
+ end
160
+ def make(target)
161
+ Cmd::execQuiet "git update-ref refs/#{@name} \"#{target}\""
162
+ puts "Maked detached branch #{@name} (was #{Cmd::revision @name})"
163
+ end
164
+ end
@@ -0,0 +1,227 @@
1
+ # ラッパーコマンド群
2
+
3
+ $:.unshift File.dirname(__FILE__)
4
+ require 'define.rb'
5
+ require 'open3'
6
+
7
+ module Cmd
8
+ extend self
9
+
10
+ # 成否を戻り値か例外で知らせる
11
+ def exec(cmd)
12
+ raise "failed, cmd:#{cmd}" if !execRet cmd
13
+ end
14
+
15
+ def execRet(cmd)
16
+ Kernel.system(cmd)
17
+ $?.success?
18
+ end
19
+
20
+ def execQuiet(cmd)
21
+ raise "failed, cmd:#{cmd}" if !execRetQuiet cmd
22
+ end
23
+
24
+ def execRetQuiet(cmd)
25
+ execRet "#{cmd} > /dev/null 2>&1"
26
+ end
27
+
28
+ # 他のコマンド
29
+
30
+ def gitdirExist?
31
+ execRetQuiet 'git rev-parse --git-dir'
32
+ end
33
+ private
34
+ _memo_gitdir = nil
35
+ def gitdir
36
+ @_memo_gitdir = @_memo_gitdir || `git rev-parse --git-dir`.chomp
37
+ end
38
+ public
39
+
40
+ def revision(target='HEAD')
41
+ `git rev-parse --short #{target}`.chomp
42
+ end
43
+ def title
44
+ `git log -1 --pretty=format:\"%s\"`
45
+ end
46
+ def branchName
47
+ `git branch`.each_line do |line|
48
+ return line[1..-1].strip if line[0] == '*'
49
+ end
50
+ end
51
+
52
+ # 引数のブランチは存在してるか?
53
+ def branchExist?(branch)
54
+ `git branch`.each_line do |_line|
55
+ line = _line
56
+ line = line[1..-1] if line[0] == '*'
57
+ line = line.strip
58
+ return true if line == branch
59
+ end
60
+
61
+ return false
62
+ end
63
+ # 引数のdetached branchは存在してるか?
64
+ def branchRefExist?(branch)
65
+ execRetQuiet "test -f \"#{gitdir}/refs/#{branch}\""
66
+ end
67
+ # ----------------
68
+ # stash-commit ---
69
+ # stash-commmitのbranch一覧
70
+ def listup(_branchName, all)
71
+ preCmd = "git branch | sed -E 's/^\\*/ /' | awk '{print $1}' | grep -E '^#{PREFIX}/'"
72
+ if all
73
+ print `#{preCmd}`
74
+ else
75
+ # グループ表示
76
+ rootBranch = _branchName.match(/^(#{PREFIX}\/)?(.+?)(@.+)?$/)[2]
77
+ print `#{preCmd} | grep "#{rootBranch}"`
78
+ end
79
+ end
80
+ def getTmp
81
+ findFirstCommitStashRef(){|line| line.match(/#{TMP_SUFFIX}$/)}
82
+ end
83
+ def getPatchRemain
84
+ findFirstCommitStashRef(){|line| line.match(/#{PATCH_REMAIN_SUFFIX}$/)}
85
+ end
86
+ def getBackup
87
+ findFirstCommitStashRef(){|line| line.match(/#{BACKUP_SUFFIX}$/)}
88
+ end
89
+ private
90
+ def findFirstCommitStashRef(&pred)
91
+ `find #{gitdir}/refs/#{PREFIX} -type f 2> /dev/null`.each_line do |_line|
92
+ line = _line.strip
93
+ line = line.sub(/^.*\.git\/refs\//, '')
94
+ return line if pred.call line
95
+ end
96
+ return ''
97
+ end
98
+ public
99
+ def stashName(branch, no)
100
+ "#{PREFIX}/#{branch}@#{no}"
101
+ end
102
+ def stashCommitRename(renameOld, renameNew)
103
+ # NOTE : 利用頻度低いので未tuning
104
+
105
+ # 名前被りチェック
106
+ preCmd = "#{stashCommitListAllString} | sed -E 's/^#{PREFIX}\\/(.+)@.+$/\\1/g' | sort | uniq"
107
+
108
+ # renameOldの存在チェック
109
+ if `#{preCmd} | grep -w \"#{renameOld}\" | wc -l | tr -d '\n'` == '0'
110
+ puts "'#{renameOld}' name is not found"
111
+ return false
112
+ end
113
+
114
+ beforeCount = `#{preCmd} | wc -l | tr -d '\n'`
115
+ afterCount = `#{preCmd} | sed 's/#{renameOld}/#{renameNew}/' | sort | uniq | wc -l | tr -d '\n'`
116
+
117
+ # 数が減っている(= 名前が被ってる)
118
+ if beforeCount != afterCount
119
+ puts 'name is overlap'
120
+ return false
121
+ end
122
+
123
+ # ここまでくれば安心
124
+ # rename処理
125
+ execRet <<-EOS
126
+ #{stashCommitListAllString} | \
127
+ grep -E "^.+#{renameOld}@.+$" | \
128
+ awk '{old=$0; new=$0; sub("#{renameOld}", "#{renameNew}", new); print old; print new;}' | \
129
+ xargs -L 2 git branch -m
130
+ EOS
131
+ end
132
+ private
133
+ def stashCommitListAllString
134
+ "git branch | sed -E 's/^\\*/ /' | awk '{print $1}' | grep -E '^#{PREFIX}/'"
135
+ end
136
+ public
137
+ # ----------------
138
+
139
+ # trackedファイルの変更数
140
+ def changesCount
141
+ count = 0
142
+ `git status --untracked-files=no --short`.each_line {count += 1}
143
+ "#{count}"
144
+ end
145
+ # rebase中?
146
+ # http://stackoverflow.com/questions/3921409/how-to-know-if-there-is-a-git-rebase-in-progress
147
+ # rebase-apply : rebase
148
+ # rebase-merge : rebase -i
149
+ def rebaseInProgress?
150
+ git_dir = gitdir
151
+ execRetQuiet "test -d \"#{git_dir}/rebase-merge\" -o -d \"#{git_dir}/rebase-apply\""
152
+ end
153
+ # 引数のブランチは親子?
154
+ def parentChildBranch?(a, b='HEAD')
155
+ hashs = `git rev-parse \"#{a}\" \"#{b}\" \"#{a}~\" \"#{b}~\"`.split
156
+
157
+ hash_a_parent = hashs[0] || ''
158
+ hash_b_parent = hashs[1] || ''
159
+ hash_a_child = hashs[2] || ''
160
+ hash_b_child = hashs[3] || ''
161
+
162
+ if hash_a_parent == ''
163
+ puts 'illegal branch'
164
+ return false
165
+ end
166
+ if hash_b_parent == ''
167
+ puts 'illegal branch'
168
+ return false
169
+ end
170
+
171
+ hash_a_parent == hash_b_child or hash_b_parent == hash_a_child
172
+ end
173
+ # 引数のブランチは同じ?
174
+ def sameBranch?(a, b='HEAD')
175
+ hashs = `git rev-parse \"#{a}\" \"#{b}\"`.split
176
+
177
+ hash_a = hashs[0] || ''
178
+ hash_b = hashs[1] || ''
179
+
180
+ if hash_a == ''
181
+ puts 'illegal branch'
182
+ return false
183
+ end
184
+ if hash_b == ''
185
+ puts 'illegal branch'
186
+ return false
187
+ end
188
+
189
+ hash_a == hash_b
190
+ end
191
+ # 2つのブランチの交差点をcommit hashで返す
192
+ def mergeBaseHash(a, b)
193
+ `git show-branch --merge-base \"#{a}\" \"#{b}\"`.chomp[0...7] # [0...7]: --short
194
+ end
195
+
196
+ # ----------
197
+
198
+ # rebase専用コマンド
199
+ def execForRebase(name, rebaseCmd)
200
+ o, e, s = Open3::capture3 rebaseCmd
201
+ puts o if o != ''
202
+ if !s.success?
203
+ # NOTE : コンフリクト時、
204
+ # 標準出力にコンフリクトメッセージ
205
+ # 標準エラー出力に--continue | --skip | --abortについて
206
+ if o.match('CONFLICT') and o.match('Merge conflict')
207
+ STDERR.puts <<-EOS
208
+ error: could not apply #{revision name}...
209
+
210
+ problem resolved, run "git stash-commit --continue".
211
+ skip this patch, run "git stash-commit --skip".
212
+ cancel this time, run "git stash-commit --abort".
213
+ EOS
214
+ else
215
+ STDERR.puts e
216
+ end
217
+ raise "failed, cmd:#{rebaseCmd}"
218
+ end
219
+ end
220
+
221
+ # ----------
222
+
223
+ def tuneLimit
224
+ # 一番軽いと思われる外部コマンド
225
+ `echo tuneLimit`
226
+ end
227
+ end
@@ -0,0 +1,7 @@
1
+ # 定数
2
+
3
+ PREFIX = 'stash-commit'
4
+ TMP_SUFFIX = 'progresstmp'
5
+ PATCH_REMAIN_SUFFIX = 'patch-remain'
6
+ BACKUP_SUFFIX = 'backup'
7
+
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: git-stash-commit
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - wordi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-11-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: when have the change files, commit to 'stash-commit' branch, and restore
42
+ it. this command is instead of the 'git stash'
43
+ email:
44
+ - wordijp@gmail.com
45
+ executables:
46
+ - git-stash-commit
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".gitignore"
51
+ - CODE_OF_CONDUCT.md
52
+ - Gemfile
53
+ - LICENSE.txt
54
+ - README.md
55
+ - Rakefile
56
+ - bin/console
57
+ - bin/git-stash-commit
58
+ - bin/rake
59
+ - bin/setup
60
+ - exe/git-stash-commit
61
+ - git-stash-commit.gemspec
62
+ - lib/git/stash/commit.rb
63
+ - lib/git/stash/commit/version.rb
64
+ - lib/git/stash/sclib/branch.rb
65
+ - lib/git/stash/sclib/command.rb
66
+ - lib/git/stash/sclib/define.rb
67
+ homepage: https://github.com/wordijp/git-stash-commit
68
+ licenses:
69
+ - MIT
70
+ metadata: {}
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 2.5.1
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: git sub command, stash change files as commit of branch
91
+ test_files: []