-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathgit.thor
278 lines (244 loc) · 6.95 KB
/
git.thor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# module: git
class Git < Thor
class NoRepositoryError < RuntimeError; end
class GitError < RuntimeError; end
class GitRebaseError < GitError; end
class GitBranchDeleteError < GitError; end
desc "open [NAME]", "Create a new branch off master, named NAME"
def open(name=nil)
newbranch = name if name && !name.empty?
newbranch ||= begin
require "readline"
print "* Name your branch: "
Readline.readline.chomp
end
branch = git_branch
if git_branches.include?(newbranch)
if newbranch == branch
puts "* Already on branch \"#{newbranch}\""
else
puts "* Switching to existing branch \"#{newbranch}\""
git_checkout(newbranch, :silent => true)
system "git log --pretty=oneline --abbrev-commit master..HEAD"
end
exit(0)
end
unless branch == "master"
puts "* Switching to master"
git_checkout("master")
end
git_checkout(newbranch, :create_branch => true)
unless $?.exitstatus.zero?
puts "* Couldn't create branch #{newbranch}, switching back to #{branch}"
git_checkout(branch)
exit(1)
end
exit(0)
end
desc "close [NAME]", "Delete the current branch and switch back to master"
def close(name=nil)
branch = name if name && !name.empty?
branch ||= git_branch
current = git_branch
if branch == "master"
$stderr.puts "* Cannot delete master branch"
exit(1)
end
if current == branch
puts "* Switching to master"
git_checkout("master")
end
puts "* Deleting branch #{branch}"
git_branch(branch, :delete => true)
if $?.exitstatus == 1
$stderr.puts "* Branch #{branch} isn't a strict subset of master, quitting"
git_checkout(current)
exit(1)
end
git_checkout(current) unless current == branch
exit(0)
end
desc "fold", "Merge the current branch into the master branch."
def fold
branch = git_branch
if branch == "master"
$stderr.puts "* Cannot fold master branch"
exit(1)
end
puts "* Switching to master"
git_checkout("master")
puts "* Merging #{branch}"
@merge_flags ||= {}
git_merge(branch, @merge_flags)
if $?.exitstatus == 1
$stderr.puts "* Merge had errors -- see to your friend"
exit(1)
end
puts "* Switching to #{branch}"
git_checkout(branch)
end
desc "ify", "Converts an existing Subversion Repo into a Git Repository"
method_options :stdlayout => :boolean
def ify(opts)
unless File.directory?("./.svn")
$stderr.puts "This task can only be executed in an existing working copy! (No .svn-Folder found)"
exit(1)
end
svnurl = `svn info`.grep(/^URL:/).first.gsub("URL: ", "").chomp
# Remove "trunk" from the svnurl if we use the stdlayout option
svnurl.slice!(-5, 5) if opts[:stdlayout] && svnurl =~ /trunk$/
project = "../#{File.basename(Dir.pwd)}.git"
puts(cmd = "git svn clone #{"--stdlayout" if opts[:stdlayout]} #{svnurl} #{project}")
`#{cmd}`
end
desc "push", "Push local commits into the remote repository"
def push
git_stash do
puts "* Pushing changes..."
git_push
branch = git_branch
if branch != "master"
git_checkout("master")
puts "* Porting changes into master"
git_rebase
git_checkout(branch)
end
end
end
desc "squash", "Squash the current branch into the master branch."
def squash
@merge_flags = {:squash => true}
fold
end
desc "update", "Pull new commits from the repository"
def update
git_stash do
branch = git_branch
if branch == "master"
switch = false
else
switch = true
git_checkout("master")
puts "* Switching back to master..."
end
puts "* Pulling in new commits..."
git_fetch
git_rebase
if switch
puts "* Porting changes into #{branch}..."
git_checkout(branch)
git_rebase(branch)
end
end
end
desc "all", "Update all branches"
def all
git_stash do
branch = git_branch
switch = true
git_branches.each do |b|
puts "* Updating branch #{b}"
begin
git_rebase(b)
rescue GitRebaseError => e
puts "* Couldn't rebase #{b}, aborting so you can clean it up"
switch = false
break
end
end
git_checkout(branch) if switch
end
end
private
def git_branch(what = nil, opts = {})
# If no name is given, return the name of the current branch
return `git branch`.grep(/^\*/).first.strip[(2..-1)] if what.nil?
delete = opts[:delete] ? "-d" : ""
force_delete = opts[:force_delete] ? "-D" : ""
`git branch #{delete} #{force_delete} #{what}`
assert_branch_delete_succeeded(what)
end
def git_branches
`git branch`.to_a.map { |b| b[(2..-1)].chomp }
end
def git_merge(what, opts = {})
squash = opts[:squash] ? "--squash" : ""
`git merge #{squash} #{what}`
end
def git?
`git status`
$?.exitstatus != 128
end
def git_stash
`git diff-files --quiet --ignore-submodules`
if $?.exitstatus == 1
stash = true
clear = (`git stash list`.scan("\n").size == 0)
puts "* Saving changes..."
`git stash save`
else
stash = false
end
begin
yield
rescue => e
puts "* Encountered an error (#{e}), backing out..."
ensure
if stash
puts "* Applying changes..."
`git stash apply`
`git stash clear` if clear
end
end
end
def git_checkout(what = nil, opts = {})
silent = opts[:silent] ? "-q" : ""
create_branch = opts[:create_branch] ? "-b" : ""
branch = git_branch
`git checkout #{silent} #{create_branch} #{what}` if branch != what
if block_given?
yield
`git checkout #{silent} #{create_branch} #{branch}` if branch != what
end
end
def git_fetch
`git#{" svn" if git_svn?} fetch`
end
def assert_command_succeeded(*args)
raise(*args) unless $?.exitstatus == 0
end
def assert_rebase_succeeded(what = nil)
assert_command_succeeded(GitRebaseError, "conflict while rebasing branch #{what}")
end
def assert_branch_delete_succeeded(what = nil)
assert_command_succeeded(GitBranchDeleteError, "branch #{what} could not be deleted")
end
def git_rebase(what = nil)
if git_svn?
git_checkout(what) do
`git svn rebase --local`
assert_rebase_succeeded(what)
end
else
`git rebase origin/master #{what}`
assert_rebase_succeeded(what)
end
end
def git_push
git_svn? ? (`git svn dcommit`) : (`git push`)
end
def chroot
# store cwd
dir = Dir.pwd
# find .git
until File.directory?('.git') || File.expand_path('.') == '/'
Dir.chdir('..')
end
is_git = File.directory?('.git')
raise NoRepositoryError, "No repository found containing #{dir}" unless is_git
end
def git_svn?
chroot
(not File.readlines(".git/config").grep(/^\[svn-remote "svn"\]\s*$/).empty?)
end
end