Skip to content

Commit 8e0fc61

Browse files
committed
[CodeExtractor] Support merge commits
When using cherry-pick, it wouldn't handle merges properly, since it requires a `-m 1` argument for it to work properly. By switching to using `git rebase --root --onto`, we can take the branch that now is a "orphan" branch, and apply the commits onto the target remote's target branch. Left extra code in there to show other possibilities (since this isn't getting merged in away)
1 parent 52d2a40 commit 8e0fc61

File tree

2 files changed

+104
-3
lines changed

2 files changed

+104
-3
lines changed

lib/code_extractor.rb

+37-3
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ def extract_commits extractions, upstream_name
9494
end
9595
end
9696

97+
# Three step process to filter out the commits we want in three passes:
98+
#
99+
# - Move code we want to keep into a separate tmp dir (using @prune_script)
100+
# - Prune anything that isn't in that subdirectory
101+
# - Move the tmp directory back into the root of the directory
102+
#
103+
#
104+
# Note: We don't use `--subdirectory-filter` here as it will remove merge
105+
# commits, which we don't want.
106+
#
97107
def prune_commits extractions
98108
puts "Pruning commits…"
99109

@@ -102,7 +112,11 @@ def prune_commits extractions
102112
Dir.chdir git_dir do
103113
`git checkout -b #{prune_branch} #{@source_branch}`
104114
`git filter-branch -f --prune-empty --tree-filter #{@prune_script} HEAD`
105-
`git filter-branch -f --prune-empty --subdirectory-filter #{@keep_directory}`
115+
`git filter-branch -f --prune-empty --index-filter '
116+
git read-tree --empty
117+
git reset $GIT_COMMIT -- #{@keep_directory}
118+
'HEAD`
119+
`git filter-branch -f --prune-empty --tree-filter 'mv #{@keep_directory}/* .' HEAD`
106120
end
107121
end
108122

@@ -188,8 +202,28 @@ def inject_commits target_base_branch, upstream_name
188202
echo "(transferred from #{upstream_name}@$GIT_COMMIT)"
189203
' -- #{prune_branch}`
190204

191-
`git checkout --no-track -b #{inject_branch} #{@reference_target_branch}`
192-
`git cherry-pick ..#{prune_branch}`
205+
# Old (bad: doesn't handle merges)
206+
#
207+
# `git checkout --no-track -b #{inject_branch} #{@reference_target_branch}`
208+
# `git cherry-pick ..#{prune_branch}`
209+
#
210+
#
211+
# Attempted #1 (not working, but uses older `git` methods)
212+
#
213+
# `git checkout --orphan #{inject_branch}`
214+
# `git commit -m "Dummy Init commit"`
215+
# orphan_commit = `git log --pretty="%H" -n1`.chomp
216+
# prune_first = `git log --pretty="%H" --reverse -n1 #{prune_branch}`.chomp
217+
# `git rebase --onto #{orphan_commit} #{prune_first} #{inject_branch}`
218+
# `git replace #{orphan_commit} #{@reference_target_branch}`
219+
#
220+
#
221+
# Better
222+
#
223+
# Ref: git rebase --onto code_extractor_inject_the_extracted --root
224+
#
225+
`git checkout --no-track -b #{inject_branch} #{prune_branch}`
226+
`git rebase --preserve-merges --root --onto #{@reference_target_branch} #{inject_branch}`
193227
end
194228
end
195229

test/code_extractor_reinsert_test.rb

+67
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ def test_unextract_an_extraction
3434
]
3535

3636
refute Dir.exist? "foo"
37+
refute File.exist? "README.md"
38+
3739
assert File.exist? "qux"
3840
assert File.exist? "lib/foo/bar"
3941
assert File.exist? "lib/foo/baz"
@@ -129,6 +131,68 @@ def test_reinsert_when_code_extractor_was_not_used
129131
]
130132

131133
refute Dir.exist? "foo"
134+
refute File.exist? "README.md"
135+
136+
assert File.exist? "qux"
137+
assert File.exist? "lib/foo/bar"
138+
assert File.exist? "lib/foo/baz"
139+
end
140+
end
141+
142+
# Basically, a test to ensure we are using a rebase and not a cherry-pick
143+
# method, since `git cherry-pick` doesn't handle merge commits well.
144+
def test_reinsert_when_there_is_a_merge_commit
145+
# original extraction to work off of, in which we "un-extract" this later
146+
create_base_repo
147+
set_extractions ["foo"]
148+
run_extraction
149+
150+
# Perform updates to extracted repo to simulate changes since extraction
151+
perform_merges_of_extracted_code
152+
apply_new_commits_on_extracted_repo do
153+
checkout_b 'master', 'origin/master'
154+
155+
update_file "foo/bar", "Updated Bar Content"
156+
commit "update bar content"
157+
158+
checkout_b 'add_baz', 'master'
159+
160+
update_file "foo/baz", "Baz Content"
161+
commit "add new baz"
162+
163+
checkout 'master'
164+
merge 'add_baz'
165+
166+
add_file "README.md", "READ ME!"
167+
commit "add README"
168+
end
169+
update_extraction_hash
170+
171+
# Run new extraction, with some extra commits added to the new repo that
172+
# has been extracted previously
173+
#
174+
# This next line will run the actual extraction we are testing
175+
#
176+
# aka: updated commits and puts 'lib/foo' back into the original repo
177+
run_extraction
178+
179+
in_git_dir do
180+
assert_commits [
181+
"Move foo/ into lib/",
182+
"Merged branch 'add_baz' into master",
183+
"add new baz",
184+
"update bar content",
185+
"Re-insert extractions from MyOrg/extracted_repo",
186+
"Merged branch 'extract_my_extractions' into master",
187+
"Extract my_extractions",
188+
"Commit #3",
189+
"add Bar content",
190+
"Initial Commit"
191+
]
192+
193+
refute Dir.exist? "foo"
194+
refute File.exist? "README.md"
195+
132196
assert File.exist? "qux"
133197
assert File.exist? "lib/foo/bar"
134198
assert File.exist? "lib/foo/baz"
@@ -170,6 +234,9 @@ def apply_new_commits_on_extracted_repo &block
170234

171235
update_file "foo/baz", "Baz Content"
172236
commit "add new baz"
237+
238+
add_file "README.md", "READ ME!"
239+
commit "add README"
173240
end
174241
end
175242

0 commit comments

Comments
 (0)