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,157 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'git-process/git_lib'
14
+ require 'git-process/git_process'
15
+ require 'git-process/parked_changes_error'
16
+ require 'git-process/uncommitted_changes_error'
17
+ require 'git-process/changed_file_helper'
18
+
19
+
20
+ module GitProc
21
+
22
+ class Syncer
23
+
24
+ class << self
25
+
26
+ def do_sync(gitlib, opts)
27
+ if !opts[:merge].nil? and opts[:merge] == opts[:rebase]
28
+ raise ArgumentError.new(":merge = #{opts[:merge]} and :rebase = #{opts[:rebase]}")
29
+ end
30
+
31
+ local = opts[:local]
32
+
33
+ branch_name = opts[:branch_name]
34
+ checkout_branch(gitlib, branch_name) unless branch_name.nil?
35
+
36
+ if do_rebase?(gitlib, opts)
37
+ rebase_sync(gitlib, local)
38
+ else
39
+ merge_sync(gitlib, opts[:force], local)
40
+ end
41
+ end
42
+
43
+
44
+ def merge_sync(gitlib, force, local)
45
+ gitlib.logger.info 'Doing merge-based sync'
46
+ gitlib.proc_merge(gitlib.config.integration_branch)
47
+
48
+ current_branch = gitlib.branches.current
49
+ remote_branch = "#{gitlib.remote.name}/#{current_branch}"
50
+
51
+ runner = lambda { merge_sync(gitlib, force, local) }
52
+ push_to_server(gitlib, current_branch, remote_branch, force, local, runner)
53
+ end
54
+
55
+
56
+ def rebase_sync(gitlib, stay_local)
57
+ gitlib.logger.info 'Doing rebase-based sync'
58
+
59
+ current_branch = gitlib.branches.current
60
+ remote_branch = "#{gitlib.remote.name}/#{current_branch}"
61
+
62
+ runner = lambda { rebase_sync(gitlib, stay_local) }
63
+
64
+ # if the remote branch has changed, bring in those changes in
65
+ remote_sha = gitlib.previous_remote_sha(current_branch, remote_branch)
66
+ if remote_sha.nil?
67
+ gitlib.logger.debug 'There were no changes on the remote branch.'
68
+ else
69
+ handle_remote_rebase_changes(current_branch, gitlib, remote_branch, remote_sha, runner, stay_local)
70
+ end
71
+
72
+ gitlib.proc_rebase(gitlib.config.integration_branch)
73
+
74
+ push_to_server(gitlib, current_branch, remote_branch, true, stay_local, runner)
75
+ end
76
+
77
+
78
+ private
79
+
80
+
81
+ def checkout_branch(gitlib, branch_name)
82
+ unless gitlib.remote.exists?
83
+ raise GitProc::GitProcessError.new("Specifying '#{branch_name}' does not make sense without a remote")
84
+ end
85
+
86
+ gitlib.fetch_remote_changes
87
+
88
+ remote_branch = "#{gitlib.remote.name}/#{branch_name}"
89
+
90
+ unless gitlib.branches.include?(remote_branch)
91
+ raise GitProc::GitProcessError.new("There is not a remote branch for '#{branch_name}'")
92
+ end
93
+
94
+ if gitlib.branches.include?(branch_name)
95
+ handle_existing_branch(gitlib, branch_name, remote_branch)
96
+ else
97
+ gitlib.logger.debug { "There is not already a local branch named #{branch_name}" }
98
+ gitlib.checkout(branch_name, :new_branch => remote_branch, :no_track => true)
99
+ end
100
+
101
+ gitlib.branches[branch_name].upstream(gitlib.config.integration_branch)
102
+ end
103
+
104
+
105
+ def handle_existing_branch(gitlib, branch_name, remote_branch)
106
+ if gitlib.branches[remote_branch].contains_all_of(branch_name)
107
+ gitlib.logger.info "There is already a local branch named #{branch_name} and it is fully subsumed by #{remote_branch}"
108
+ gitlib.checkout(branch_name)
109
+ else
110
+ raise GitProc::GitProcessError.new("There is already a local branch named #{branch_name} that is not fully subsumed by #{remote_branch}")
111
+ end
112
+ end
113
+
114
+
115
+ def handle_remote_rebase_changes(current_branch, gitlib, remote_branch, remote_sha, runner, stay_local)
116
+ gitlib.logger.info 'There have been changes on the remote branch, so will bring them in.'
117
+ gitlib.proc_rebase(remote_branch, :oldbase => remote_sha)
118
+
119
+ push_to_server(gitlib, current_branch, remote_branch, true, stay_local, runner)
120
+ end
121
+
122
+
123
+ def do_rebase?(gitlib, opts)
124
+ if opts[:rebase].nil?
125
+ gitlib.config.default_rebase_sync?
126
+ else
127
+ opts[:rebase]
128
+ end
129
+ end
130
+
131
+
132
+ def push_to_server(gitlib, current_branch, remote_branch, force, local, runner)
133
+ gitlib.push_to_server(current_branch, current_branch,
134
+ :local => local,
135
+ :force => force,
136
+ :prepush => lambda { handle_remote_changed(gitlib, current_branch, remote_branch, runner) },
137
+ :postpush => lambda { gitlib.write_sync_control_file(current_branch) }
138
+ )
139
+ end
140
+
141
+
142
+ def handle_remote_changed(gitlib, current_branch, remote_branch, runner)
143
+ old_sha = gitlib.remote_branch_sha(remote_branch)
144
+ gitlib.fetch_remote_changes
145
+ new_sha = gitlib.remote_branch_sha(remote_branch)
146
+
147
+ if old_sha != new_sha
148
+ gitlib.logger.warn("'#{current_branch}' changed on '#{gitlib.remote.name}'; trying sync again.")
149
+ runner.call # try again
150
+ end
151
+ end
152
+
153
+ end
154
+
155
+ end
156
+
157
+ end
@@ -0,0 +1,23 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'git-process/git_process_error'
14
+
15
+ module GitProc
16
+
17
+ class UncommittedChangesError < GitProcessError
18
+ def initialize
19
+ super("There are uncommitted changes.\nPlease either commit your changes, or use 'git stash' to set them aside.")
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,22 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ module GitProc
14
+ module Version
15
+ MAJOR = 2
16
+ MINOR = 0
17
+ PATCH = 0
18
+ BUILD = nil
19
+
20
+ STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
21
+ end
22
+ end
data/local-build.rb ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+ require File.expand_path('../lib/git-process/version', __FILE__)
3
+
4
+ gems = %w(git-process-lib git-sync git-to-master git-new-fb git-pull-request git-process)
5
+
6
+ %x[rake manpage 2>&1]
7
+
8
+ for gem in gems.reverse
9
+ %x[gem uninstall #{gem} -x -v #{GitProc::Version::STRING} 2>&1]
10
+ end
11
+
12
+ for gem in gems
13
+ puts %x(a2x -f manpage -D man docs/#{gem}.1.adoc)
14
+ SystemExit.new($?) if $?.exitstatus
15
+
16
+ %x[gem build #{gem}.gemspec]
17
+ SystemExit.new($?) if $?.exitstatus
18
+ end
19
+
20
+ puts %x(gem install ./git-process-#{GitProc::Version::STRING}.gem -l -u)
21
+ SystemExit.new($?) if $?.exitstatus
22
+
23
+ puts %x(gem push ./git-process-#{GitProc::Version::STRING}.gem -l -u)
24
+ SystemExit.new($?) if $?.exitstatus
@@ -0,0 +1,19 @@
1
+ require 'tmpdir'
2
+ include FileUtils
3
+
4
+ module FileHelpers
5
+ TEST_DIR = File.dirname(__FILE__)
6
+
7
+
8
+ def dir_files(dir)
9
+ Dir.entries(dir).grep(/^[^.]/)
10
+ end
11
+
12
+
13
+ def compare_files(file1name, file2name)
14
+ str1 = IO.read(file1name)
15
+ str2 = IO.read(file2name)
16
+ str1.should == str2
17
+ end
18
+
19
+ end
@@ -0,0 +1,123 @@
1
+ require 'tmpdir'
2
+ require 'FileHelpers'
3
+ require 'git-process/git_process'
4
+ include GitProc
5
+
6
+ module GitRepoHelper
7
+
8
+ def gitprocess
9
+ if @gitprocess.nil? and respond_to?(:create_process)
10
+ @gitprocess = create_process(gitlib, :log_level => log_level)
11
+ end
12
+ @gitprocess
13
+ end
14
+
15
+
16
+ def gitlib
17
+ if @gitlib.nil?
18
+ if @gitprocess.nil?
19
+ @gitlib = create_gitlib(Dir.mktmpdir, :log_level => log_level)
20
+ else
21
+ @gitlib = gitprocess.gitlib
22
+ end
23
+ end
24
+ @gitlib
25
+ end
26
+
27
+
28
+ def config
29
+ gitlib.config
30
+ end
31
+
32
+
33
+ def remote
34
+ gitlib.remote
35
+ end
36
+
37
+
38
+ def commit_count
39
+ gitlib.log_count
40
+ end
41
+
42
+
43
+ def log_level
44
+ Logger::ERROR
45
+ end
46
+
47
+
48
+ def logger
49
+ gitlib.logger
50
+ end
51
+
52
+
53
+ def create_files(file_names)
54
+ GitRepoHelper.create_files gitlib, file_names
55
+ end
56
+
57
+
58
+ def self.create_files(gitlib, file_names)
59
+ Dir.chdir(gitlib.workdir) do |dir|
60
+ file_names.each do |fn|
61
+ gitlib.logger.debug { "Creating #{dir}/#{fn}" }
62
+ FileUtils.touch fn
63
+ end
64
+ end
65
+ gitlib.add(file_names)
66
+ end
67
+
68
+
69
+ def change_file(filename, contents, lib = gitlib)
70
+ Dir.chdir(lib.workdir) do
71
+ File.open(filename, 'w') { |f| f.puts contents }
72
+ end
73
+ end
74
+
75
+
76
+ def change_file_and_add(filename, contents, lib = gitlib)
77
+ change_file(filename, contents, lib)
78
+ lib.add(filename)
79
+ end
80
+
81
+
82
+ def change_file_and_commit(filename, contents, lib = gitlib)
83
+ change_file_and_add(filename, contents, lib)
84
+ lib.commit("#{filename} - #{contents}")
85
+ end
86
+
87
+
88
+ def create_gitlib(dir, opts)
89
+ git_lib = GitLib.new(dir, opts)
90
+ git_lib.config['user.email'] = 'test.user@test.com'
91
+ git_lib.config['user.name'] = 'test user'
92
+ git_lib
93
+ end
94
+
95
+
96
+ def clone_repo(branch='master', remote_name = 'origin', &block)
97
+ td = Dir.mktmpdir
98
+
99
+ logger.debug { "Cloning '#{gitlib.workdir}' to '#{td}'" }
100
+
101
+ gl = create_gitlib(td, :log_level => logger.level)
102
+ gl.remote.add(remote_name, "file://#{gitlib.workdir}")
103
+ gl.fetch(remote_name)
104
+
105
+ if branch == 'master'
106
+ gl.reset("#{remote_name}/#{branch}", :hard => true)
107
+ else
108
+ gl.checkout(branch, :new_branch => "#{remote_name}/#{branch}")
109
+ end
110
+
111
+ if block_given?
112
+ begin
113
+ block.arity < 1 ? gl.instance_eval(&block) : block.call(gl)
114
+ ensure
115
+ rm_rf(gl.workdir)
116
+ end
117
+ nil
118
+ else
119
+ gl
120
+ end
121
+ end
122
+
123
+ end
@@ -0,0 +1,127 @@
1
+ require 'git-process/sync_process'
2
+ include GitProc
3
+
4
+ describe ChangeFileHelper, :git_repo_helper do
5
+
6
+ def log_level
7
+ Logger::ERROR
8
+ end
9
+
10
+
11
+ #noinspection RubyUnusedLocalVariable
12
+ def create_process(dir, opts)
13
+ nil
14
+ end
15
+
16
+
17
+ describe 'uncommitted changes' do
18
+
19
+ it 'should fail when there are unmerged files' do
20
+ change_file_and_commit('modified file.txt', 'start')
21
+
22
+ clone_repo do |gl|
23
+ change_file_and_commit('modified file.txt', 'changed', gl)
24
+ change_file_and_commit('modified file.txt', 'conflict', gitlib)
25
+ gl.fetch
26
+
27
+ gl.merge('origin/master') rescue ''
28
+
29
+ change_file_helper = ChangeFileHelper.new(gl)
30
+ expect { change_file_helper.offer_to_help_uncommitted_changes }.to raise_error GitProc::UncommittedChangesError
31
+ end
32
+ end
33
+
34
+
35
+ def change_file_helper
36
+ @change_file_helper ||= ChangeFileHelper.new(gitlib)
37
+ end
38
+
39
+
40
+ describe "using 'unknown' file" do
41
+
42
+ before(:each) do
43
+ change_file('unknown file.txt', '')
44
+ change_file_helper.stub(:say)
45
+ end
46
+
47
+
48
+ it 'should then add it' do
49
+ ChangeFileHelper.stub(:ask_how_to_handle_unknown_files).and_return(:add)
50
+ change_file_helper.gitlib.should_receive(:add).with(['unknown file.txt'])
51
+
52
+ change_file_helper.offer_to_help_uncommitted_changes
53
+ end
54
+
55
+
56
+ it 'should ignore the file' do
57
+ ChangeFileHelper.stub(:ask_how_to_handle_unknown_files).and_return(:ignore)
58
+ change_file_helper.should_not_receive(:add)
59
+
60
+ change_file_helper.offer_to_help_uncommitted_changes
61
+ end
62
+
63
+ end
64
+
65
+
66
+ describe "using changed files" do
67
+
68
+ before(:each) do
69
+ change_file_and_commit('modified file.txt', 'start')
70
+ change_file_and_commit('modified file2.txt', 'start')
71
+ change_file_and_commit('modified file3.txt', 'start')
72
+ change_file_and_commit('modified file4.txt', 'start')
73
+ change_file_and_commit('removed file.txt', 'content')
74
+ change_file_and_add('added file.txt', '')
75
+ change_file('modified file.txt', 'modified')
76
+ change_file_and_add('modified file2.txt', 'modified')
77
+ change_file_and_add('modified file3.txt', 'modified')
78
+ change_file('modified file2.txt', 'modified again')
79
+ change_file_and_add('removed file2.txt', 'content')
80
+ change_file_and_add('modified file4.txt', 'content')
81
+ File.delete(File.join(gitlib.workdir, 'removed file.txt'))
82
+ File.delete(File.join(gitlib.workdir, 'removed file2.txt'))
83
+ File.delete(File.join(gitlib.workdir, 'modified file3.txt'))
84
+
85
+ # End state of the above is:
86
+ # A "added file.txt"
87
+ # M "modified file.txt"
88
+ # MM "modified file2.txt"
89
+ # MD "modified file3.txt"
90
+ # M "modified file4.txt"
91
+ # D "removed file.txt"
92
+ # AD "removed file2.txt"
93
+
94
+ change_file_helper.stub(:say)
95
+ end
96
+
97
+
98
+ it 'should ask about modified files, then commit them' do
99
+ ChangeFileHelper.stub(:ask_how_to_handle_changed_files).and_return(:commit)
100
+ gitlib.should_receive(:add).with(["added file.txt", "modified file.txt", "modified file2.txt", "modified file4.txt"])
101
+ gitlib.should_receive(:remove).with(["modified file3.txt", "removed file.txt", "removed file2.txt"])
102
+ gitlib.should_receive(:commit).with(nil)
103
+
104
+ change_file_helper.offer_to_help_uncommitted_changes
105
+ end
106
+
107
+
108
+ it 'should ask about modified files, then stash them' do
109
+ ChangeFileHelper.stub(:ask_how_to_handle_changed_files).and_return(:stash)
110
+
111
+ change_file_helper.offer_to_help_uncommitted_changes
112
+
113
+ gitlib.status.clean?.should be_true
114
+
115
+ gitlib.stash_pop
116
+
117
+ stat = gitlib.status
118
+ stat.added.should == ["added file.txt", "removed file2.txt"]
119
+ stat.modified.should == ["modified file.txt", "modified file2.txt", "modified file4.txt"]
120
+ stat.deleted.should == ["modified file3.txt", "removed file.txt"]
121
+ end
122
+
123
+ end
124
+
125
+ end
126
+
127
+ end