git-process-lib 2.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.
Files changed (69) hide show
  1. data/CHANGELOG.md +123 -0
  2. data/Gemfile +21 -0
  3. data/Gemfile.lock +57 -0
  4. data/LICENSE +193 -0
  5. data/README.md +342 -0
  6. data/Rakefile +32 -0
  7. data/bin/git-new-fb +39 -0
  8. data/bin/git-pull-request +63 -0
  9. data/bin/git-sync +38 -0
  10. data/bin/git-to-master +44 -0
  11. data/docs/git-new-fb.1.adoc +83 -0
  12. data/docs/git-process.1.adoc +227 -0
  13. data/docs/git-pull-request.1.adoc +166 -0
  14. data/docs/git-sync.1.adoc +120 -0
  15. data/docs/git-to-master.1.adoc +172 -0
  16. data/git-new-fb.gemspec +20 -0
  17. data/git-process-lib.gemspec +25 -0
  18. data/git-process.gemspec +22 -0
  19. data/git-pull-request.gemspec +20 -0
  20. data/git-sync.gemspec +20 -0
  21. data/git-to-master.gemspec +20 -0
  22. data/lib/git-process/abstract_error_builder.rb +53 -0
  23. data/lib/git-process/changed_file_helper.rb +115 -0
  24. data/lib/git-process/git_abstract_merge_error_builder.rb +130 -0
  25. data/lib/git-process/git_branch.rb +105 -0
  26. data/lib/git-process/git_branches.rb +81 -0
  27. data/lib/git-process/git_config.rb +135 -0
  28. data/lib/git-process/git_lib.rb +646 -0
  29. data/lib/git-process/git_logger.rb +84 -0
  30. data/lib/git-process/git_merge_error.rb +28 -0
  31. data/lib/git-process/git_process.rb +159 -0
  32. data/lib/git-process/git_process_error.rb +18 -0
  33. data/lib/git-process/git_process_options.rb +101 -0
  34. data/lib/git-process/git_rebase_error.rb +30 -0
  35. data/lib/git-process/git_remote.rb +222 -0
  36. data/lib/git-process/git_status.rb +108 -0
  37. data/lib/git-process/github_configuration.rb +298 -0
  38. data/lib/git-process/github_pull_request.rb +165 -0
  39. data/lib/git-process/new_fb.rb +49 -0
  40. data/lib/git-process/parked_changes_error.rb +41 -0
  41. data/lib/git-process/pull_request.rb +136 -0
  42. data/lib/git-process/pull_request_error.rb +25 -0
  43. data/lib/git-process/rebase_to_master.rb +148 -0
  44. data/lib/git-process/sync_process.rb +55 -0
  45. data/lib/git-process/syncer.rb +157 -0
  46. data/lib/git-process/uncommitted_changes_error.rb +23 -0
  47. data/lib/git-process/version.rb +22 -0
  48. data/local-build.rb +24 -0
  49. data/spec/FileHelpers.rb +19 -0
  50. data/spec/GitRepoHelper.rb +123 -0
  51. data/spec/changed_file_helper_spec.rb +127 -0
  52. data/spec/git_abstract_merge_error_builder_spec.rb +64 -0
  53. data/spec/git_branch_spec.rb +123 -0
  54. data/spec/git_config_spec.rb +45 -0
  55. data/spec/git_lib_spec.rb +176 -0
  56. data/spec/git_logger_spec.rb +66 -0
  57. data/spec/git_process_spec.rb +208 -0
  58. data/spec/git_remote_spec.rb +227 -0
  59. data/spec/git_status_spec.rb +122 -0
  60. data/spec/github_configuration_spec.rb +152 -0
  61. data/spec/github_pull_request_spec.rb +117 -0
  62. data/spec/github_test_helper.rb +49 -0
  63. data/spec/new_fb_spec.rb +126 -0
  64. data/spec/pull_request_helper.rb +94 -0
  65. data/spec/pull_request_spec.rb +137 -0
  66. data/spec/rebase_to_master_spec.rb +362 -0
  67. data/spec/spec_helper.rb +21 -0
  68. data/spec/sync_spec.rb +1474 -0
  69. metadata +249 -0
@@ -0,0 +1,152 @@
1
+ require 'git-process/github_configuration'
2
+ require 'json'
3
+ require 'github_test_helper'
4
+
5
+
6
+ describe GitHubService::Configuration, :git_repo_helper do
7
+ include GitHubTestHelper
8
+
9
+
10
+ def test_token
11
+ 'hfgkdjfgksjhdfkls'
12
+ end
13
+
14
+
15
+ def ghc
16
+ @ghc ||= GitHubService::Configuration.new(gitlib.config, :user => 'tu', :password => 'dfsdf')
17
+ end
18
+
19
+
20
+ describe 'create_authorization' do
21
+
22
+ it "should return an auth_token for a good request" do
23
+ gitlib.remote.add('origin', 'git@github.com:jdigger/git-process.git')
24
+ stub_post('https://tu:dfsdf@api.github.com/authorizations', :send => auth_json,
25
+ :body => {:token => test_token})
26
+
27
+ ghc.create_authorization().should == test_token
28
+ end
29
+
30
+
31
+ it 'should 401 for bad password' do
32
+ gitlib.remote.add('origin', 'git@github.com:jdigger/git-process.git')
33
+ stub_post('https://tu:dfsdf@api.github.com/authorizations', :send => auth_json,
34
+ :status => 401)
35
+
36
+ expect { ghc.create_authorization() }.to raise_error Octokit::Unauthorized
37
+ end
38
+
39
+ end
40
+
41
+
42
+ describe "auth_token no username or password" do
43
+
44
+ it "should get the token from config if it exists" do
45
+ gitlib.config['github.user'] = 'test_user'
46
+ gitlib.config['gitProcess.github.authToken'] = test_token
47
+
48
+ ghc.auth_token.should == test_token
49
+ end
50
+
51
+ end
52
+
53
+
54
+ describe 'auth_token with password but no username' do
55
+
56
+ def ghc
57
+ @ghc ||= GitHubService::Configuration.new(gitlib.config, :user => nil, :password => 'dfsdf')
58
+ end
59
+
60
+
61
+ it 'should get the token from the server if it does not exist in config' do
62
+ gitlib.remote.add('origin', 'git@github.com:jdigger/git-process.git')
63
+ gitlib.config['github.user'] = 'test_user'
64
+ gitlib.config['gitProcess.github.authToken'] = ''
65
+
66
+ stub_post('https://test_user:dfsdf@api.github.com/authorizations', :send => auth_json,
67
+ :body => {:token => test_token})
68
+
69
+ ghc.auth_token.should == test_token
70
+ end
71
+
72
+ end
73
+
74
+
75
+ describe "user" do
76
+
77
+ def ghc
78
+ @ghc ||= GitHubService::Configuration.new(gitlib.config, :user => nil, :password => 'dfsdf')
79
+ end
80
+
81
+
82
+ it "should get the value from config" do
83
+ gitlib.config['github.user'] = 'test_user'
84
+
85
+ ghc.user.should == 'test_user'
86
+ end
87
+
88
+
89
+ it "should prompt the user and store it in the config" do
90
+ gitlib.config['github.user'] = ''
91
+
92
+ GitHubService::Configuration.stub(:ask_for_user).and_return('test_user')
93
+ ghc.user.should == 'test_user'
94
+ end
95
+
96
+ end
97
+
98
+
99
+ describe "using GHE instead of GitHub.com" do
100
+
101
+ it "should use the correct server and path for a non-GitHub.com site" do
102
+ gitlib.remote.add('origin', 'git@myco.com:jdigger/git-process.git')
103
+
104
+ stub_post('https://tu:dfsdf@myco.com/api/v3/authorizations',
105
+ :send => auth_json,
106
+ :body => {:token => test_token})
107
+
108
+ ghc.create_authorization().should == test_token
109
+ end
110
+
111
+
112
+ it "site should raise an error if remote.origin.url not set" do
113
+ gitlib.config['remote.origin.url'] = ''
114
+
115
+ expect { ghc.base_github_api_url_for_remote }.to raise_error GitHubService::NoRemoteRepository
116
+ end
117
+
118
+
119
+ it "site should not work for a garbage url address" do
120
+ gitlib.remote.add('origin', 'garbage')
121
+
122
+ expect { ghc.base_github_api_url_for_remote }.to raise_error URI::InvalidURIError
123
+ end
124
+
125
+
126
+ it "site should work for an ssh-configured url address" do
127
+ gitlib.remote.add('origin', 'git@github.myco.com:fooble')
128
+
129
+ ghc.base_github_api_url_for_remote.should == 'https://github.myco.com'
130
+ end
131
+
132
+ end
133
+
134
+
135
+ it "#url_to_base_github_api_url" do
136
+ c = GitHubService::Configuration
137
+
138
+ c.url_to_base_github_api_url('ssh://git@github.myco.com/fooble').should == 'https://github.myco.com'
139
+ c.url_to_base_github_api_url('git://myco.com/jdigger/git-process.git').should == 'https://myco.com'
140
+ c.url_to_base_github_api_url('http://github.myco.com/fooble').should == 'http://github.myco.com'
141
+ c.url_to_base_github_api_url('http://tu@github.myco.com/fooble').should == 'http://github.myco.com'
142
+ c.url_to_base_github_api_url('https://github.myco.com/fooble').should == 'https://github.myco.com'
143
+ c.url_to_base_github_api_url('https://github.com/fooble').should == 'https://api.github.com'
144
+ end
145
+
146
+
147
+ def auth_json
148
+ JSON({:note_url => 'http://jdigger.github.com/git-process',
149
+ :scopes => %w(repo user gist), :note => "Git-Process"})
150
+ end
151
+
152
+ end
@@ -0,0 +1,117 @@
1
+ require 'git-process/pull_request'
2
+ require 'GitRepoHelper'
3
+ require 'github_test_helper'
4
+ require 'json'
5
+ require 'octokit'
6
+ require 'tempfile'
7
+
8
+ describe GitHub::PullRequest, :git_repo_helper do
9
+ include GitHubTestHelper
10
+
11
+
12
+ def test_token
13
+ 'hfgkdjfgksjhdfkls'
14
+ end
15
+
16
+
17
+ def pull_request
18
+ @pr ||= GitHub::PullRequest.new(gitlib, 'test_remote', 'test_repo', :user => 'test_user')
19
+ end
20
+
21
+
22
+ before(:each) do
23
+ gitlib.config['gitProcess.github.authToken'] = test_token
24
+ gitlib.remote.add('test_remote', 'git@github.com:test_repo.git')
25
+ end
26
+
27
+
28
+ describe '#create' do
29
+
30
+ it 'should return a pull request for a good request' do
31
+ stub_post('https://api.github.com/repos/test_repo/pulls', :body => {:number => 1, :state => 'open'})
32
+
33
+ pull_request.create('test_base', 'test_head', 'test title', 'test body')[:state].should == 'open'
34
+ end
35
+
36
+
37
+ it 'should handle asking for a duplicate pull request' do
38
+ # trying to create the request should return "HTTP 422: Unprocessable Entity" because it already exists
39
+ stub_post('https://api.github.com/repos/test_repo/pulls', :status => 422)
40
+
41
+ # listing all existing pull requests should contain the current branch
42
+ stub_get('https://api.github.com/repos/test_repo/pulls?state=open', :status => 200,
43
+ :body => [{:html_url => 'test_url', :head => {:ref => 'test_head'}, :base => {:ref => 'test_base'}}])
44
+
45
+ pull_request.create('test_base', 'test_head', 'test title', 'test body')[:html_url].should == 'test_url'
46
+ end
47
+
48
+ end
49
+
50
+
51
+ describe 'get' do
52
+
53
+ it 'should return a pull request for a good request' do
54
+ stub_get('https://api.github.com/repos/test_repo/pulls/1', :body => {:number => 1, :state => 'open'})
55
+
56
+ pull_request.pull_request(1)[:state].should == 'open'
57
+ end
58
+
59
+ end
60
+
61
+
62
+ describe '#close' do
63
+
64
+ it 'should close a good current pull request' do
65
+ stub_get('https://api.github.com/repos/test_repo/pulls?state=open', :body => [
66
+ {:number => 1, :state => 'open', :html_url => 'test_url', :head => {:ref => 'test_head'},
67
+ :base => {:ref => 'test_base'}}])
68
+ stub_patch('https://api.github.com/repos/test_repo/pulls/1', :send => JSON({:state => 'closed'}),
69
+ :body => {:number => 1, :state => 'closed', :html_url => 'test_url', :head => {:ref => 'test_head'},
70
+ :base => {:ref => 'test_base'}})
71
+
72
+ pull_request.close('test_base', 'test_head')[:state].should == 'closed'
73
+ end
74
+
75
+
76
+ it 'should close a good current pull request using the pull request number' do
77
+ stub_patch('https://api.github.com/repos/test_repo/pulls/1', :send => JSON({:state => 'closed'}),
78
+ :body => {:number => 1, :state => 'closed', :html_url => 'test_url',
79
+ :head => {:ref => 'test_head'}, :base => {:ref => 'test_base'}})
80
+
81
+ pull_request.close(1)[:state].should == 'closed'
82
+ end
83
+
84
+
85
+ it 'should retry closing a good current pull request when getting a 422' do
86
+ stub = stub_request(:patch, 'https://api.github.com/repos/test_repo/pulls/1')
87
+
88
+ stub.with(:body => JSON({:state => 'closed'}))
89
+
90
+ stub.to_raise(Octokit::UnprocessableEntity.new).then.
91
+ to_raise(Octokit::UnprocessableEntity.new).then.
92
+ to_raise(Octokit::UnprocessableEntity.new).then.
93
+ to_raise(Octokit::UnprocessableEntity.new).then.
94
+ to_return(:status => 200, :body => {:number => 1, :state => 'closed', :html_url => 'test_url',
95
+ :head => {:ref => 'test_head'}, :base => {:ref => 'test_base'}})
96
+
97
+ pull_request.close(1)[:state].should == 'closed'
98
+ end
99
+
100
+
101
+ it 'should complain about a missing pull request' do
102
+ stub_get('https://api.github.com/repos/test_repo/pulls?state=open', :body => [
103
+ {:number => 1, :state => 'open', :html_url => 'test_url', :head => {:ref => 'test_head'},
104
+ :base => {:ref => 'test_base'}}])
105
+
106
+ expect { pull_request.close('test_base', 'missing_head') }.to raise_error GitHub::PullRequest::NotFoundError
107
+ end
108
+
109
+
110
+ it 'should complain about wrong number of arguments' do
111
+ expect { pull_request.close() }.to raise_error ::ArgumentError
112
+ expect { pull_request.close('1', '2', '3') }.to raise_error ::ArgumentError
113
+ end
114
+
115
+ end
116
+
117
+ end
@@ -0,0 +1,49 @@
1
+ require 'FileHelpers'
2
+ require 'git-process/git_process'
3
+ require 'webmock/rspec'
4
+
5
+ module GitHubTestHelper
6
+
7
+
8
+ def stub_get(url, opts = {})
9
+ stub = stub_request(:get, url)
10
+
11
+ if opts[:token]
12
+ stub.with(:Authorization => "token #{opts[:token]}")
13
+ end
14
+
15
+ stub.to_return(:status => opts[:status] ? opts[:status] : 200, :body => opts[:body] ? opts[:body] : '')
16
+ stub
17
+ end
18
+
19
+
20
+ def stub_post(url, opts = {})
21
+ stub = stub_request(:post, url)
22
+
23
+ if opts[:token]
24
+ stub.with(:Authorization => "token #{opts[:token]}")
25
+ end
26
+
27
+ stub.to_return(:status => opts[:status] ? opts[:status] : 200, :body => opts[:body] ? opts[:body] : '')
28
+
29
+ stub
30
+ end
31
+
32
+
33
+ def stub_patch(url, opts = {})
34
+ stub = stub_request(:patch, url)
35
+
36
+ if opts[:token]
37
+ stub.with(:Authorization => "token #{opts[:token]}")
38
+ end
39
+
40
+ if opts[:send]
41
+ stub.with(:body => opts[:send])
42
+ end
43
+
44
+ stub.to_return(:status => opts[:status] ? opts[:status] : 200, :body => opts[:body] ? opts[:body] : '')
45
+
46
+ stub
47
+ end
48
+
49
+ end
@@ -0,0 +1,126 @@
1
+ require 'git-process/new_fb'
2
+ require 'GitRepoHelper'
3
+ include GitProc
4
+
5
+
6
+ describe NewFeatureBranch do
7
+ include GitRepoHelper
8
+
9
+ before(:each) do
10
+ create_files(%w(.gitignore))
11
+ gitlib.commit('initial')
12
+ end
13
+
14
+
15
+ after(:each) do
16
+ rm_rf(gitlib.workdir)
17
+ end
18
+
19
+
20
+ def create_process(dir, opts = {})
21
+ opts[:branch_name] = 'test_branch'
22
+ NewFeatureBranch.new(dir, opts)
23
+ end
24
+
25
+
26
+ def log_level
27
+ Logger::ERROR
28
+ end
29
+
30
+
31
+ it 'should create the named branch against origin/master' do
32
+ clone_repo do |gl|
33
+ gp = create_process(gl)
34
+ gl.checkout('other_branch', :new_branch => 'master')
35
+ change_file_and_commit('a', '', gl)
36
+ change_file_and_commit('b', '', gl)
37
+ new_branch = gp.runner
38
+
39
+ new_branch.name.should == 'test_branch'
40
+ new_branch.sha.should == gl.branches['origin/master'].sha
41
+ end
42
+ end
43
+
44
+
45
+ it "should bring committed changes on _parking_ over to the new branch" do
46
+ clone_repo do |gl|
47
+ gl.checkout('_parking_', :new_branch => 'master')
48
+ change_file_and_commit('a', '', gl)
49
+ change_file_and_commit('b', '', gl)
50
+
51
+ gp = create_process(gl)
52
+ new_branch = gp.runner
53
+
54
+ new_branch.name.should == 'test_branch'
55
+ Dir.chdir(gl.workdir) do |_|
56
+ File.exists?('a').should be_true
57
+ File.exists?('b').should be_true
58
+ end
59
+
60
+ gl.config["branch.test_branch.remote"].should == 'origin'
61
+ gl.config["branch.test_branch.merge"].should == 'refs/heads/master'
62
+
63
+ gl.fetch
64
+ gl.branches.parking.should be_nil
65
+ new_branch.sha.should_not == gl.branches['origin/master'].sha
66
+ end
67
+
68
+ end
69
+
70
+
71
+ it "should move new branch over to the integration branch" do
72
+ clone_repo do |gl|
73
+ gl.checkout('_parking_', :new_branch => 'master')
74
+ change_file_and_commit('a', '', gitlib)
75
+ change_file_and_commit('b', '', gitlib)
76
+
77
+ gl.fetch
78
+ gp = create_process(gl)
79
+ new_branch = gp.runner
80
+
81
+ new_branch.name.should == 'test_branch'
82
+ Dir.chdir(gitlib.workdir) do |_|
83
+ File.exists?('a').should be_true
84
+ File.exists?('b').should be_true
85
+ end
86
+
87
+ gl.config["branch.test_branch.remote"].should == 'origin'
88
+ gl.config["branch.test_branch.merge"].should == 'refs/heads/master'
89
+
90
+ gl.fetch
91
+ gl.branches.parking.should be_nil
92
+ new_branch.sha.should == gl.branches['origin/master'].sha
93
+ end
94
+
95
+ end
96
+
97
+
98
+ it "should use 'integration_branch' instead of 'remote_master_branch'" do
99
+ change_file_and_commit('a', '')
100
+
101
+ new_branch = gitprocess.runner
102
+
103
+ new_branch.name.should == 'test_branch'
104
+ end
105
+
106
+
107
+ it "should bring new/uncommitted changes on _parking_ over to the new branch" do
108
+ gitlib.branch('origin/master', :base_branch => 'master')
109
+ gitlib.checkout('_parking_', :new_branch => 'master')
110
+ change_file_and_commit('a', '')
111
+ change_file_and_add('b', '')
112
+ change_file('c', '')
113
+
114
+ new_branch = gitprocess.runner
115
+
116
+ new_branch.name.should == 'test_branch'
117
+ Dir.chdir(gitlib.workdir) do |_|
118
+ File.exists?('a').should be_true
119
+ File.exists?('b').should be_true
120
+ File.exists?('c').should be_true
121
+ end
122
+
123
+ gitlib.branches.parking.should be_nil
124
+ end
125
+
126
+ end
@@ -0,0 +1,94 @@
1
+ module PullRequestHelper
2
+
3
+ def create_pull_request(opts = {})
4
+ v = {
5
+ :head_remote => 'testrepo',
6
+ :head_repo => 'test_repo',
7
+ :base_repo => 'test_repo',
8
+ :head_branch => 'test_branch',
9
+ :base_branch => 'source_branch',
10
+ :api_url => 'https://api.github.com',
11
+ :pr_number => '32',
12
+ :state => 'open',
13
+ }
14
+ v.merge! opts
15
+ v[:ssh_head_url] = "git@github.com:#{opts[:head_repo] || v[:head_repo]}.git" unless opts.has_key?(:ssh_head_url)
16
+ v[:ssh_base_url] = "git@github.com:#{opts[:base_repo] || v[:base_repo]}.git" unless opts.has_key?(:ssh_base_url)
17
+ PullRequestHelper::_basic_pull_request_data(v)
18
+ end
19
+
20
+
21
+ def self._basic_pull_request_data(opts = {})
22
+ {
23
+ :number => opts[:pr_number],
24
+ :state => opts[:state],
25
+ :head => {
26
+ :remote => opts[:head_repo], # pseudo-property for testing
27
+ :ref => opts[:head_branch],
28
+ :repo => {
29
+ :name => opts[:head_repo],
30
+ :ssh_url => opts[:ssh_head_url],
31
+ }
32
+ },
33
+ :base => {
34
+ :remote => opts[:base_repo], # pseudo-property for testing
35
+ :ref => opts[:base_branch],
36
+ :repo => {
37
+ :name => opts[:base_repo],
38
+ :ssh_url => opts[:ssh_base_url],
39
+ }
40
+ }
41
+ }
42
+ end
43
+
44
+
45
+ # @abstract the Hash/JSON of the pull request structure to use
46
+ # @return [Hash]
47
+ def pull_request
48
+ raise NotImplementedError
49
+ end
50
+
51
+
52
+ def api_url(remote_name, glib = gitlib)
53
+ GitHubService::Configuration.new(glib.config, :remote_name => remote_name).base_github_api_url_for_remote
54
+ end
55
+
56
+
57
+ def stub_get_pull_request(pr, glib = gitlib)
58
+ api_url = api_url(pr[:head][:remote], glib)
59
+ stub_get("#{api_url}/repos/#{pr[:head][:repo][:name]}/pulls/#{pr[:number]}", :body => pr)
60
+ end
61
+
62
+
63
+ def stub_fetch(which_remote, glib = gitlib)
64
+ rem = pull_request[which_remote][:remote]
65
+ glib.stub(:fetch).with(rem)
66
+ end
67
+
68
+
69
+ #
70
+ # Adds a remote to git's configuration based on {#pull_request}
71
+ #
72
+ # @param [:head, :base] which_remote
73
+ #
74
+ def add_remote(which_remote, glib = gitlib)
75
+ glib.remote.add(pull_request[which_remote][:remote], pull_request[which_remote][:repo][:ssh_url])
76
+ end
77
+
78
+
79
+ # Verifies the branch is checked out from the HEAD branch of the pull
80
+ # request and created by the same name
81
+ def expect_checkout_pr_head(glib = gitlib)
82
+ pr = pull_request
83
+ glib.should_receive(:checkout).with(pr[:head][:ref], :new_branch => "#{pr[:head][:remote]}/#{pr[:head][:ref]}")
84
+ end
85
+
86
+
87
+ # Verifies the tracking for the new branch is set to the BASE branch
88
+ # of the pull request
89
+ def expect_upstream_set(glib = gitlib)
90
+ pr = pull_request
91
+ glib.should_receive(:branch).with(pr[:head][:ref], :upstream => "#{pr[:base][:remote]}/#{pr[:base][:ref]}")
92
+ end
93
+
94
+ end