Skip to content

Commit 5fb2459

Browse files
author
Evgenii Pecherkin
committed
Support merge-base
1 parent c8d1012 commit 5fb2459

File tree

4 files changed

+197
-0
lines changed

4 files changed

+197
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ And here are the operations that will need to write to your git repository.
215215
g.merge(g.branch('master'))
216216
g.merge([branch1, branch2])
217217

218+
g.merge_base('branch1', 'branch2')
219+
218220
r = g.add_remote(name, uri) # Git::Remote
219221
r = g.add_remote(name, Git::Base) # Git::Remote
220222

lib/git/base/factory.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ def tag(tag_name)
6868
Git::Object.new(self, tag_name, 'tag', true)
6969
end
7070

71+
# Find as good common ancestors as possible for a merge
72+
# example: g.merge_base('master', 'some_branch', 'some_sha', octopus: true)
73+
# returns Array<Git::Object::Commit>
74+
def merge_base(*args)
75+
shas = self.lib.merge_base(*args)
76+
shas.map { |sha| gcommit(sha) }
77+
end
78+
7179
end
7280

7381
end

lib/git/lib.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,21 @@ def merge(branch, message = nil)
660660
command('merge', arr_opts)
661661
end
662662

663+
def merge_base(*args)
664+
opts = args.last.is_a?(Hash) ? args.pop : {}
665+
666+
arg_opts = []
667+
668+
arg_opts << '--octopus' if opts[:octopus]
669+
arg_opts << '--independent' if opts[:independent]
670+
arg_opts << '--fork-point' if opts[:fork_point]
671+
arg_opts << '--all' if opts[:all]
672+
673+
arg_opts += args
674+
675+
command('merge-base', arg_opts).lines.map(&:strip)
676+
end
677+
663678
def unmerged
664679
unmerged = []
665680
command_lines('diff', ["--cached"]).each do |line|

tests/units/test_merge_base.rb

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#!/usr/bin/env ruby
2+
3+
require File.dirname(__FILE__) + '/../test_helper'
4+
5+
class TestMergeBase < Test::Unit::TestCase
6+
def setup
7+
set_file_paths
8+
end
9+
10+
def test_branch_and_master_merge_base
11+
in_temp_dir do |path|
12+
g = Git.clone(@wbare, 'branch_merge_test')
13+
Dir.chdir('branch_merge_test') do
14+
15+
true_ancestor_sha = g.gcommit('master').sha
16+
17+
g.branch('new_branch').in_branch('test commit 1') do
18+
new_file('new_file_1', 'hello')
19+
g.add
20+
true
21+
end
22+
23+
g.branch('master').in_branch('test commit 2') do
24+
new_file('new_file_2', 'hello')
25+
g.add
26+
true
27+
end
28+
29+
ancestors = g.merge_base('master', 'new_branch')
30+
assert_equal(ancestors.size, 1) # there is only one true ancestor
31+
assert_equal(ancestors.first.sha, true_ancestor_sha) # proper common ancestor
32+
end
33+
end
34+
end
35+
36+
def test_branch_and_master_independent_merge_base
37+
in_temp_dir do |path|
38+
g = Git.clone(@wbare, 'branch_merge_test')
39+
Dir.chdir('branch_merge_test') do
40+
41+
true_ancestor_sha = g.gcommit('master').sha
42+
43+
g.branch('new_branch').in_branch('test commit 1') do
44+
new_file('new_file_1', 'hello')
45+
g.add
46+
true
47+
end
48+
49+
g.branch('master').in_branch('test commit 2') do
50+
new_file('new_file_2', 'hello')
51+
g.add
52+
true
53+
end
54+
55+
independent_commits = g.merge_base(true_ancestor_sha, 'master', 'new_branch', independent: true)
56+
assert_equal(independent_commits.size, 2) # both new master and a branch are unreachable from each other
57+
true_independent_commits_shas = [g.gcommit('master').sha, g.gcommit('new_branch').sha]
58+
assert_equal(independent_commits.map(&:sha).sort, true_independent_commits_shas.sort)
59+
end
60+
end
61+
end
62+
63+
def test_branch_and_master_fork_point_merge_base
64+
in_temp_dir do |path|
65+
g = Git.clone(@wbare, 'branch_merge_test')
66+
Dir.chdir('branch_merge_test') do
67+
68+
g.branch('master').in_branch('test commit 1') do
69+
new_file('new_file_1', 'hello')
70+
g.add
71+
true
72+
end
73+
74+
true_ancestor_sha = g.gcommit('master').sha
75+
76+
g.branch('new_branch').in_branch('test commit 2') do
77+
new_file('new_file_2', 'hello')
78+
g.add
79+
true
80+
end
81+
82+
g.branch('master').in_branch('test commit 3') do
83+
g.reset_hard(g.gcommit('HEAD^'))
84+
85+
new_file('new_file_3', 'hello')
86+
g.add
87+
true
88+
end
89+
90+
ancestors = g.merge_base('master', 'new_branch', fork_point: true)
91+
assert_equal(ancestors.size, 1) # there is only one true ancestor
92+
assert_equal(ancestors.first.sha, true_ancestor_sha) # proper common ancestor
93+
end
94+
end
95+
end
96+
97+
def test_branches_and_master_merge_base
98+
in_temp_dir do |path|
99+
g = Git.clone(@wbare, 'branch_merge_test')
100+
Dir.chdir('branch_merge_test') do
101+
102+
g.branch('new_branch_1').in_branch('test commit 1') do
103+
new_file('new_file_1', 'hello')
104+
g.add
105+
true
106+
end
107+
108+
g.branch('master').in_branch('test commit 2') do
109+
new_file('new_file_2', 'hello')
110+
g.add
111+
true
112+
end
113+
114+
non_octopus_ancestor_sha = g.gcommit('master').sha
115+
116+
g.branch('new_branch_2').in_branch('test commit 3') do
117+
new_file('new_file_3', 'hello')
118+
g.add
119+
true
120+
end
121+
122+
g.branch('master').in_branch('test commit 4') do
123+
new_file('new_file_4', 'hello')
124+
g.add
125+
true
126+
end
127+
128+
ancestors = g.merge_base('master', 'new_branch_1', 'new_branch_2')
129+
assert_equal(ancestors.size, 1) # there is only one true ancestor
130+
assert_equal(ancestors.first.sha, non_octopus_ancestor_sha) # proper common ancestor
131+
end
132+
end
133+
end
134+
135+
def test_branches_and_master_octopus_merge_base
136+
in_temp_dir do |path|
137+
g = Git.clone(@wbare, 'branch_merge_test')
138+
Dir.chdir('branch_merge_test') do
139+
140+
true_ancestor_sha = g.gcommit('master').sha
141+
142+
g.branch('new_branch_1').in_branch('test commit 1') do
143+
new_file('new_file_1', 'hello')
144+
g.add
145+
true
146+
end
147+
148+
g.branch('master').in_branch('test commit 2') do
149+
new_file('new_file_2', 'hello')
150+
g.add
151+
true
152+
end
153+
154+
g.branch('new_branch_2').in_branch('test commit 3') do
155+
new_file('new_file_3', 'hello')
156+
g.add
157+
true
158+
end
159+
160+
g.branch('master').in_branch('test commit 4') do
161+
new_file('new_file_4', 'hello')
162+
g.add
163+
true
164+
end
165+
166+
ancestors = g.merge_base('master', 'new_branch_1', 'new_branch_2', octopus: true)
167+
assert_equal(ancestors.size, 1) # there is only one true ancestor
168+
assert_equal(ancestors.first.sha, true_ancestor_sha) # proper common ancestor
169+
end
170+
end
171+
end
172+
end

0 commit comments

Comments
 (0)