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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +128 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/git-stash-commit +17 -0
- data/bin/rake +17 -0
- data/bin/setup +8 -0
- data/exe/git-stash-commit +3 -0
- data/git-stash-commit.gemspec +35 -0
- data/lib/git/stash/commit.rb +726 -0
- data/lib/git/stash/commit/version.rb +7 -0
- data/lib/git/stash/sclib/branch.rb +164 -0
- data/lib/git/stash/sclib/command.rb +227 -0
- data/lib/git/stash/sclib/define.rb +7 -0
- metadata +91 -0
@@ -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
|
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: []
|