riquedafreak-github 0.1.3 → 0.1.4

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.
data/README CHANGED
@@ -17,7 +17,7 @@ Run it:
17
17
 
18
18
 
19
19
  =============
20
- Pulling Changes
20
+ Pulling Upstream Changes
21
21
  =============
22
22
 
23
23
  Let's say you just forked `github-gem` on GitHub from defunkt.
@@ -44,6 +44,40 @@ master branch, use the `merge` flag:
44
44
 
45
45
  $ github pull --merge defunkt
46
46
 
47
+
48
+ ==========
49
+ Fetching and Evaluation Downstream Changes
50
+ ==========
51
+
52
+ If you are the maintainer of a project, you will often need to fetch commits
53
+ from other developers, evaluate and/or test them, then merge them into the
54
+ project.
55
+
56
+ Let's say you are 'defunkt' and 'mojombo' has forked your 'github-gem' repo,
57
+ made some changes and issues you a pull request for his 'master' branch.
58
+
59
+ From the root of the project, you can do:
60
+
61
+ $ github fetch mojombo master
62
+
63
+ This will leave you in the 'mojombo/master' branch after fetching his commits.
64
+ Your local 'mojombo/master' branch is now at the exact same place as mojombo's
65
+ 'master' branch. You can now run tests or evaluate the code for awesomeness.
66
+
67
+ If mojombo's changes are good, you'll want to merge your 'master' (or another
68
+ branch) into those changes so you can retest post-integration:
69
+
70
+ $ git merge master
71
+
72
+ Test/analyze again and if everything is ok:
73
+
74
+ $ git checkout master
75
+ $ git merge mojombo/master
76
+
77
+ The latter command will be a fast-forward merge since you already did the
78
+ real merge previously.
79
+
80
+
47
81
  ==========
48
82
  Contributors
49
83
  ==========
@@ -52,3 +86,4 @@ Contributors
52
86
  - maddox
53
87
  - halorgium
54
88
  - kballard
89
+ - mojombo
data/commands/commands.rb CHANGED
@@ -61,6 +61,22 @@ command :track do |remote, user|
61
61
  end
62
62
  end
63
63
 
64
+ desc "Fetch from a remote to a local branch."
65
+ command :fetch do |user, branch|
66
+ die "Specify a user to pull from" if user.nil?
67
+ user, branch = user.split("/", 2) if branch.nil?
68
+ branch ||= 'master'
69
+ GitHub.invoke(:track, user) unless helper.tracking?(user)
70
+
71
+ die "Unknown branch (#{branch}) specified" unless helper.remote_branch?(user, branch)
72
+ die "Unable to switch branches, your current branch has uncommitted changes" if helper.branch_dirty?
73
+
74
+ puts "Fetching #{user}/#{branch}"
75
+ git "fetch #{user} #{branch}:refs/remotes/#{user}/#{branch}"
76
+ git "update-ref refs/heads/#{user}/#{branch} refs/remotes/#{user}/#{branch}"
77
+ git_exec "checkout #{user}/#{branch}"
78
+ end
79
+
64
80
  desc "Pull from a remote."
65
81
  flags :merge => "Automatically merge remote's changes into your master."
66
82
  command :pull do |user, branch|
@@ -68,28 +84,15 @@ command :pull do |user, branch|
68
84
  user, branch = user.split("/", 2) if branch.nil?
69
85
  branch ||= 'master'
70
86
  GitHub.invoke(:track, user) unless helper.tracking?(user)
71
-
72
- # check to see if the branch even exists on the remote user
73
- die "Unknown branch (#{branch}) specified" if !helper.heads?(user, branch)
74
-
75
- # if the merge option was specified then don't switch to a new branch
76
- if !options[:merge]
77
- puts "Switching to #{user}/#{branch}"
78
87
 
79
- if helper.heads?(".", "#{user}/#{branch}")
80
- # if the remote branch is already locally tracked then switch to it
81
- die "Unable to checkout branch (#{branch})" if git("checkout #{user}/#{branch}").error?
82
- else
83
- # if the remote branch is not already locally tracked, then fetch it
84
- git_exec "fetch #{user} +#{branch}:#{user}/#{branch}"
85
- die "Unable to checkout branch (#{branch})" if git("checkout #{user}/#{branch}").error?
86
- return
87
- end
88
- else
89
- puts "Merging from contents of #{user}/#{branch}"
88
+ die "Unknown branch (#{branch}) specified" unless helper.remote_branch?(user, branch)
89
+ die "Unable to switch branches, your current branch has uncommitted changes" if helper.branch_dirty?
90
+
91
+ unless options[:merge]
92
+ puts "Switching to #{user}/#{branch}"
93
+ git "update-ref refs/heads/#{user}/#{branch} HEAD"
94
+ git "checkout #{user}/#{branch}"
90
95
  end
91
-
92
- # get the contents of the branch, merging it with whatever is in the local branch
93
96
  git_exec "pull #{user} #{branch}"
94
97
  end
95
98
 
@@ -120,4 +123,4 @@ command :'pull-request' do |user, branch|
120
123
 
121
124
  git_exec "request-pull #{user}/#{branch} origin"
122
125
  end
123
- end
126
+ end
data/commands/helpers.rb CHANGED
@@ -36,7 +36,7 @@ end
36
36
 
37
37
  helper :remotes do
38
38
  regexp = '^remote\.(.+)\.url$'
39
- `git config --get-regexp '#{regexp}'`.split(/\n/).inject({}) do |memo, line|
39
+ `git config --get-regexp '#{regexp}'`.split("\n").inject({}) do |memo, line|
40
40
  name_string, url = line.split(/ /, 2)
41
41
  m, name = *name_string.match(/#{regexp}/)
42
42
  memo[name.to_sym] = url
@@ -44,20 +44,25 @@ helper :remotes do
44
44
  end
45
45
  end
46
46
 
47
- helper :heads do |user|
48
- results = `git ls-remote -h #{user} 2> /dev/null`
49
- results = `git rev-parse --symbolic-full-name --branches 2> /dev/null | grep refs/heads/#{user}` if $?.exitstatus != 0
50
-
51
- results.split(/\n/).inject({}) do |memo, line|
47
+ helper :remote_branches_for do |user|
48
+ `git ls-remote -h #{user} 2> /dev/null`.split(/\n/).inject({}) do |memo, line|
52
49
  hash, head = line.split(/\t/, 2)
53
- head = line.scan(/refs\/heads\/(.+)$/)[0][0]
54
- memo[head] = hash
50
+ head = head[%r{refs/heads/(.+)$},1] unless head.nil?
51
+ memo[head] = hash unless head.nil?
55
52
  memo
56
- end
53
+ end if !(user.nil? || user.strip.empty?)
54
+ end
55
+
56
+ helper :remote_branch? do |user, branch|
57
+ remote_branches_for(user).key?(branch)
57
58
  end
58
59
 
59
- helper :heads? do |user, branch|
60
- heads(user).key?(branch)
60
+ helper :branch_dirty? do
61
+ # see if there are any cached or tracked files that have been modified
62
+ # originally, we were going to use git-ls-files but that could only
63
+ # report modified track files...not files that have been staged
64
+ # for committal
65
+ !(system("git diff --quiet 2>/dev/null") or !system("git diff --cached --quiet 2>/dev/null"))
61
66
  end
62
67
 
63
68
  helper :tracking do
@@ -40,11 +40,11 @@ module GitHub
40
40
  def git_exec(*command)
41
41
  cmdstr = ['git', command].flatten.join(' ')
42
42
  GitHub.debug "exec: #{cmdstr}"
43
- `#{cmdstr}`
43
+ exec cmdstr
44
44
  end
45
45
 
46
46
  def sh(*command)
47
- return Shell.new(*command).run
47
+ Shell.new(*command).run
48
48
  end
49
49
 
50
50
  def die(message)
@@ -53,17 +53,24 @@ module GitHub
53
53
  end
54
54
 
55
55
  class Shell < String
56
+ attr_reader :error
57
+ attr_reader :out
58
+
56
59
  def initialize(*command)
57
60
  @command = command
58
61
  end
59
62
 
60
63
  def run
61
64
  GitHub.debug "sh: #{command}"
62
-
63
- out = `#{command} 2>&1`
64
- replace @error = $?.exitstatus.to_s if $?.exitstatus != 0
65
+ _, out, err = Open3.popen3(*@command)
66
+
67
+ out = out.read.strip
68
+ err = err.read.strip
69
+
70
+ replace @error = err if err.any?
65
71
  replace @out = out if out.any?
66
- return self
72
+
73
+ self
67
74
  end
68
75
 
69
76
  def command
@@ -73,7 +80,7 @@ module GitHub
73
80
  def error?
74
81
  !!@error
75
82
  end
76
-
83
+
77
84
  def out?
78
85
  !!@out
79
86
  end
data/spec/command_spec.rb CHANGED
@@ -24,14 +24,25 @@ describe GitHub::Command do
24
24
  unguard(Kernel, :exec)
25
25
  hi = @command.sh("echo hi")
26
26
  hi.should == "hi"
27
+ hi.out.should == "hi"
27
28
  hi.out?.should be(true)
29
+ hi.error.should be_nil
28
30
  hi.error?.should be(false)
29
31
  hi.command.should == "echo hi"
30
32
  bye = @command.sh("echo bye >&2")
31
33
  bye.should == "bye"
34
+ bye.out.should be_nil
32
35
  bye.out?.should be(false)
36
+ bye.error.should == "bye"
33
37
  bye.error?.should be(true)
34
38
  bye.command.should == "echo bye >&2"
39
+ hi_and_bye = @command.sh("echo hi; echo bye >&2")
40
+ hi_and_bye.should == "hi"
41
+ hi_and_bye.out.should == "hi"
42
+ hi_and_bye.out?.should be(true)
43
+ hi_and_bye.error.should == "bye"
44
+ hi_and_bye.error?.should be(true)
45
+ hi_and_bye.command.should == "echo hi; echo bye >&2"
35
46
  end
36
47
 
37
48
  it "should return the results of a git operation" do
data/spec/helper_spec.rb CHANGED
@@ -149,6 +149,67 @@ remote.nex3.url git://github.com/nex3/github-gem.git
149
149
  end
150
150
  end
151
151
 
152
+ helper :remote_branches_for do
153
+ it "should return an empty list because no user was provided" do
154
+ @helper.remote_branches_for(nil).should == nil
155
+ end
156
+
157
+ it "should return a list of remote branches for defunkt" do
158
+ @helper.should_receive(:`).with('git ls-remote -h defunkt 2> /dev/null').and_return <<-EOF
159
+ fe1f852f3cf719c7cd86147031732f570ad89619 refs/heads/kballard/master
160
+ f8a6bb42b0ed43ac7336bfcda246e59a9da949d6 refs/heads/master
161
+ 624d9c2f742ff24a79353a7e02bf289235c72ff1 refs/heads/restart
162
+ EOF
163
+ @helper.remote_branches_for("defunkt").should == {
164
+ "master" => "f8a6bb42b0ed43ac7336bfcda246e59a9da949d6",
165
+ "kballard/master" => "fe1f852f3cf719c7cd86147031732f570ad89619",
166
+ "restart" => "624d9c2f742ff24a79353a7e02bf289235c72ff1"
167
+ }
168
+ end
169
+
170
+ it "should return an empty list of remote branches for nex3 and nex4" do
171
+ # the following use-case should never happen as the -h parameter should only return heads on remote branches
172
+ # however, we are testing this particular case to verify how remote_branches_for would respond if random
173
+ # git results
174
+ @helper.should_receive(:`).with('git ls-remote -h nex3 2> /dev/null').and_return <<-EOF
175
+ fe1f852f3cf719c7cd86147031732f570ad89619 HEAD
176
+ a1a392369e5b7842d01cce965272d4b96c2fd343 refs/tags/v0.1.3
177
+ 624d9c2f742ff24a79353a7e02bf289235c72ff1 refs/remotes/origin/master
178
+ random
179
+ random_again
180
+ EOF
181
+ @helper.remote_branches_for("nex3").should be_empty
182
+
183
+ @helper.should_receive(:`).with('git ls-remote -h nex4 2> /dev/null').and_return ""
184
+ @helper.remote_branches_for("nex4").should be_empty
185
+ end
186
+ end
187
+
188
+ helper :remote_branch? do
189
+ it "should return whether the branch exists at the remote user" do
190
+ @helper.should_receive(:remote_branches_for).with("defunkt").any_number_of_times.and_return({
191
+ "master" => "f8a6bb42b0ed43ac7336bfcda246e59a9da949d6",
192
+ "kballard/master" => "fe1f852f3cf719c7cd86147031732f570ad89619",
193
+ "restart" => "624d9c2f742ff24a79353a7e02bf289235c72ff1"
194
+ })
195
+ @helper.remote_branch?("defunkt", "master").should == true
196
+ @helper.remote_branch?("defunkt", "not_master").should == false
197
+ end
198
+ end
199
+
200
+ helper :branch_dirty? do
201
+ it "should return false" do
202
+ @helper.should_receive(:system).with(/^git diff/).and_return(0, 0)
203
+ @helper.branch_dirty?.should == 0
204
+ end
205
+
206
+ it "should return true" do
207
+ @helper.should_receive(:system).with(/^git diff/).and_return(1, 1, 0, 1)
208
+ @helper.branch_dirty?.should == 1
209
+ @helper.branch_dirty?.should == 1
210
+ end
211
+ end
212
+
152
213
  helper :tracking do
153
214
  it "should return a list of remote/user_or_url pairs" do
154
215
  @helper.should_receive(:remotes).and_return({
data/spec/ui_spec.rb CHANGED
@@ -157,16 +157,16 @@ EOF
157
157
  end
158
158
  end
159
159
 
160
- # -- pull --
161
- specify "pull should die with no args" do
162
- running :pull do
163
- @command.should_receive(:die).with("Specify a user to pull from").and_return { raise "Died" }
160
+ # -- fetch --
161
+ specify "fetch should die with no args" do
162
+ running :fetch do
163
+ @command.should_receive(:die).with("Specify a user to pull from").and_return { raise "Died "}
164
164
  self.should raise_error("Died")
165
165
  end
166
166
  end
167
167
 
168
- specify "pull defunkt should start tracking defunkt if they're not already tracked" do
169
- running :pull, "defunkt" do
168
+ specify "fetch defunkt should start tracking defunkt if they're not already tracked" do
169
+ running :fetch, "defunkt" do
170
170
  setup_remote(:origin, :user => "user", :ssh => true)
171
171
  setup_remote(:external, :url => "home:/path/to/project.git")
172
172
  GitHub.should_receive(:invoke).with(:track, "defunkt").and_return { raise "Tracked" }
@@ -174,56 +174,98 @@ EOF
174
174
  end
175
175
  end
176
176
 
177
- specify "pull defunkt should create defunkt/master and pull from the defunkt remote" do
178
- running :pull, "defunkt" do
177
+ specify "fetch defunkt should create defunkt/master and fetch from the defunkt remote" do
178
+ running :fetch, "defunkt" do
179
179
  setup_remote(:defunkt)
180
- @command.should_receive(:git).with("checkout -b defunkt/master").ordered.and_return do
181
- mock("checkout -b defunkt/master").tap { |m| m.stub!(:error?) }
182
- end
183
- @command.should_receive(:git_exec).with("pull defunkt master").ordered
180
+ @helper.should_receive(:branch_dirty?).and_return false
181
+ @command.should_receive(:git).with("update-ref refs/heads/defunkt/master HEAD").ordered
182
+ @command.should_receive(:git).with("checkout defunkt/master").ordered
183
+ @command.should_receive(:git_exec).with("fetch defunkt master").ordered
184
184
  stdout.should == "Switching to defunkt/master\n"
185
185
  end
186
186
  end
187
187
 
188
- specify "pull defunkt should switch to pre-existing defunkt/master and pull from the defunkt remote" do
189
- running :pull, "defunkt" do
188
+ specify "fetch defunkt should die if there is a dirty branch" do
189
+ running :fetch, "defunkt" do
190
190
  setup_remote(:defunkt)
191
- @command.should_receive(:git).with("checkout -b defunkt/master").ordered.and_return do
192
- mock("checkout -b defunkt/master").tap { |m| m.should_receive(:error?) { true } }
193
- end
191
+ @helper.should_receive(:branch_dirty?).and_return true
192
+ @command.should_receive(:die).with("Unable to switch branches, your current branch has uncommitted changes").and_return { raise "Died" }
193
+ self.should raise_error("Died")
194
+ end
195
+ end
196
+
197
+ specify "fetch defunkt/wip should create defunkt/wip and fetch from wip branch on defunkt remote" do
198
+ running :fetch, "defunkt/wip" do
199
+ setup_remote(:defunkt, :remote_branches => ["master", "wip"])
200
+ @helper.should_receive(:branch_dirty?).and_return false
201
+ @command.should_receive(:git).with("update-ref refs/heads/defunkt/wip HEAD").ordered
202
+ @command.should_receive(:git).with("checkout defunkt/wip").ordered
203
+ @command.should_receive(:git_exec).with("fetch defunkt wip").ordered
204
+ stdout.should == "Switching to defunkt/wip\n"
205
+ end
206
+ end
207
+
208
+ specify "fetch --merge defunkt should fetch from defunkt remote into current branch" do
209
+ running :fetch, "--merge", "defunkt" do
210
+ setup_remote(:defunkt)
211
+ @helper.should_receive(:branch_dirty?).and_return false
212
+ @command.should_receive(:git_exec).with("fetch defunkt master")
213
+ end
214
+ end
215
+
216
+ # -- fetch --
217
+ specify "fetch should die with no args" do
218
+ running :fetch do
219
+ @command.should_receive(:die).with("Specify a user to fetch from").and_return { raise "Died" }
220
+ self.should raise_error("Died")
221
+ end
222
+ end
223
+
224
+ specify "fetch defunkt should start tracking defunkt if they're not already tracked" do
225
+ running :fetch, "defunkt" do
226
+ setup_remote(:origin, :user => "user", :ssh => true)
227
+ setup_remote(:external, :url => "home:/path/to/project.git")
228
+ GitHub.should_receive(:invoke).with(:track, "defunkt").and_return { raise "Tracked" }
229
+ self.should raise_error("Tracked")
230
+ end
231
+ end
232
+
233
+ specify "fetch defunkt should create defunkt/master and fetch from the defunkt remote" do
234
+ running :fetch, "defunkt" do
235
+ setup_remote(:defunkt)
236
+ @helper.should_receive(:branch_dirty?).and_return false
237
+ @command.should_receive(:git).with("update-ref refs/heads/defunkt/master HEAD").ordered
194
238
  @command.should_receive(:git).with("checkout defunkt/master").ordered
195
- @command.should_receive(:git_exec).with("pull defunkt master").ordered
239
+ @command.should_receive(:git_exec).with("fetch defunkt master").ordered
196
240
  stdout.should == "Switching to defunkt/master\n"
197
241
  end
198
242
  end
199
243
 
200
- specify "pull defunkt wip should create defunkt/wip and pull from wip branch on defunkt remote" do
201
- running :pull, "defunkt", "wip" do
244
+ specify "fetch defunkt should die if there is a dirty branch" do
245
+ running :fetch, "defunkt" do
202
246
  setup_remote(:defunkt)
203
- @command.should_receive(:git).with("checkout -b defunkt/wip").ordered.and_return do
204
- mock("checkout -b defunkt/wip").tap { |m| m.stub!(:error?) }
205
- end
206
- @command.should_receive(:git_exec).with("pull defunkt wip").ordered
207
- stdout.should == "Switching to defunkt/wip\n"
247
+ @helper.should_receive(:branch_dirty?).and_return true
248
+ @command.should_receive(:die).with("Unable to switch branches, your current branch has uncommitted changes").and_return { raise "Died" }
249
+ self.should raise_error("Died")
208
250
  end
209
251
  end
210
252
 
211
- specify "pull defunkt/wip should switch to pre-existing defunkt/wip and pull from wip branch on defunkt remote" do
212
- running :pull, "defunkt/wip" do
213
- setup_remote(:defunkt)
214
- @command.should_receive(:git).with("checkout -b defunkt/wip").ordered.and_return do
215
- mock("checkout -b defunkt/wip").tap { |m| m.should_receive(:error?) { true } }
216
- end
253
+ specify "fetch defunkt/wip should create defunkt/wip and fetch from wip branch on defunkt remote" do
254
+ running :fetch, "defunkt/wip" do
255
+ setup_remote(:defunkt, :remote_branches => ["master", "wip"])
256
+ @helper.should_receive(:branch_dirty?).and_return false
257
+ @command.should_receive(:git).with("update-ref refs/heads/defunkt/wip HEAD").ordered
217
258
  @command.should_receive(:git).with("checkout defunkt/wip").ordered
218
- @command.should_receive(:git_exec).with("pull defunkt wip").ordered
259
+ @command.should_receive(:git_exec).with("fetch defunkt wip").ordered
219
260
  stdout.should == "Switching to defunkt/wip\n"
220
261
  end
221
262
  end
222
263
 
223
- specify "pull --merge defunkt should pull from defunkt remote into current branch" do
224
- running :pull, "--merge", "defunkt" do
264
+ specify "fetch --merge defunkt should fetch from defunkt remote into current branch" do
265
+ running :fetch, "--merge", "defunkt" do
225
266
  setup_remote(:defunkt)
226
- @command.should_receive(:git_exec).with("pull defunkt master")
267
+ @helper.should_receive(:branch_dirty?).and_return false
268
+ @command.should_receive(:git_exec).with("fetch defunkt master")
227
269
  end
228
270
  end
229
271
 
@@ -390,12 +432,14 @@ EOF
390
432
  @stderr_mock.invoke unless @stderr_mock.nil?
391
433
  end
392
434
 
393
- def setup_remote(remote, options = {:user => nil, :project => "project"})
435
+ def setup_remote(remote, options = {:user => nil, :project => "project", :remote_branches => nil})
394
436
  @remotes ||= {}
437
+ @remote_branches ||= {}
395
438
  user = options[:user] || remote
396
439
  project = options[:project]
397
440
  ssh = options[:ssh]
398
441
  url = options[:url]
442
+ remote_branches = options[:remote_branches] || ["master"]
399
443
  if url
400
444
  @remotes[remote.to_sym] = url
401
445
  elsif ssh
@@ -403,6 +447,11 @@ EOF
403
447
  else
404
448
  @remotes[remote.to_sym] = "git://github.com/#{user}/#{project}.git"
405
449
  end
450
+
451
+ @remote_branches[remote.to_sym] = (@remote_branches[remote.to_sym] || Array.new) | remote_branches
452
+ @helper.should_receive(:remote_branch?).any_number_of_times.and_return do |remote, branch|
453
+ @remote_branches.fetch(remote.to_sym,[]).include?(branch)
454
+ end
406
455
  end
407
456
 
408
457
  def mock_remotes()
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riquedafreak-github
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Wanstrath, Kevin Ballard