Skip to content

Commit b1c76b0

Browse files
Handle one more corner case in push (#994)
Change: push-corner-case
1 parent e1d10b6 commit b1c76b0

File tree

2 files changed

+128
-10
lines changed

2 files changed

+128
-10
lines changed

src/history.rs

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -465,12 +465,12 @@ pub fn unapply_filter(
465465
}
466466

467467
// This will typically be parent_count == 2 and mean we are dealing with a merge
468-
// where the parents have differences outside of the filter. This is only possible
469-
// if one of the parents is a descendant of the target branch and the other is not.
470-
// In that case pick the tree of the one that is a descendant.
468+
// where the parents have differences outside of the filter.
471469
parent_count => {
472470
let mut tid = git2::Oid::zero();
473471
for i in 0..parent_count {
472+
// If one of the parents is a descendant of the target branch and the other is
473+
// not, pick the tree of the one that is a descendant.
474474
if (original_parents_refs[i].id() == original_target)
475475
|| transaction
476476
.repo()
@@ -481,19 +481,36 @@ pub fn unapply_filter(
481481
}
482482
}
483483

484-
if tid != git2::Oid::zero() {
485-
transaction.repo().find_tree(tid)?
486-
} else {
487-
// This used to be our only fallback for the parent_count > 1 case.
488-
// It should never happen anymore.
484+
if tid == git2::Oid::zero() && parent_count == 2 {
485+
// If we could not select one of the parents, try to merge them.
486+
487+
if let Ok(mut merged_index) = transaction.repo().merge_commits(
488+
original_parents_refs[0],
489+
original_parents_refs[1],
490+
None,
491+
) {
492+
// If we can auto merge without conflicts, take the result.
493+
if !merged_index.has_conflicts() {
494+
tid = merged_index.write_tree_to(transaction.repo())?;
495+
}
496+
}
497+
}
498+
499+
if tid == git2::Oid::zero() {
500+
// We give up. If we see this message again we need to investigate once
501+
// more and maybe consider allowing a manual override as last resort.
489502
tracing::warn!("rejecting merge");
490503
let msg = format!(
491-
"rejecting merge with {} parents:\n{:?}",
504+
"rejecting merge with {} parents:\n{:?}\n1) {:?}\n2) {:?}",
492505
parent_count,
493-
module_commit.summary().unwrap_or_default()
506+
module_commit.summary().unwrap_or_default(),
507+
original_parents_refs[0].summary().unwrap_or_default(),
508+
original_parents_refs[1].summary().unwrap_or_default(),
494509
);
495510
return Err(josh_error(&msg));
496511
}
512+
513+
transaction.repo().find_tree(tid)?
497514
}
498515
};
499516

tests/filter/subtree_prefix.t

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,104 @@ And then re-extract, which should re-construct the same subtree.
106106
[5] :/subtree
107107
[5] :rev(c036f944faafb865e0585e4fa5e005afa0aeea3f:prefix=subtree)
108108
$ test $(git rev-parse subtree) = $(git rev-parse subtree2)
109+
110+
Simulate a feature branch on the main repo that crosses subtree changes
111+
$ git checkout master 2>/dev/null
112+
$ git checkout -b feature1 2>/dev/null
113+
$ git reset --hard $SUBTREE_TIP >/dev/null
114+
$ echo work > feature1
115+
$ git add feature1 >/dev/null
116+
$ git commit -m feature1 >/dev/null
117+
$ git checkout master 2>/dev/null
118+
$ git merge feature1 --no-ff >/dev/null
119+
120+
On the subtree, simulate some independent work, and then a sync, then some more work.
121+
$ git checkout subtree 2>/dev/null
122+
$ echo work > subfeature1
123+
$ git add subfeature1 >/dev/null
124+
$ git commit -m subfeature1 >/dev/null
125+
$ josh-filter -s ":rev($SUBTREE_TIP:prefix=subtree):/subtree" refs/heads/master --update refs/heads/subtree-sync >/dev/null
126+
$ git merge subtree-sync --no-ff >/dev/null
127+
$ echo work > subfeature2
128+
$ git add subfeature2 >/dev/null
129+
$ git commit -m subfeature2 >/dev/null
130+
131+
And another main tree feature off of SUBTREE_TIP
132+
$ git checkout -b feature2 2>/dev/null
133+
$ git reset --hard $SUBTREE_TIP >/dev/null
134+
$ echo work > feature2
135+
$ git add feature2 >/dev/null
136+
$ git commit -m feature2 >/dev/null
137+
$ git checkout master 2>/dev/null
138+
$ git merge feature2 --no-ff >/dev/null
139+
140+
And finally, sync first from main to sub and then back.
141+
$ git checkout subtree 2>/dev/null
142+
$ josh-filter -s ":rev($SUBTREE_TIP:prefix=subtree):/subtree" refs/heads/master --update refs/heads/subtree-sync >/dev/null
143+
$ git merge subtree-sync --no-ff >/dev/null
144+
145+
$ git log --graph --pretty=%s refs/heads/master
146+
* Merge branch 'feature2'
147+
|\
148+
| * feature2
149+
* | Merge branch 'feature1'
150+
|\ \
151+
| * | feature1
152+
| |/
153+
* | add even more content
154+
* | subtree edit from main repo
155+
* | subtree merge
156+
|\|
157+
| * add file2 (in subtree)
158+
* add file1
159+
$ git log --graph --pretty=%s refs/heads/subtree
160+
* Merge branch 'subtree-sync' into subtree
161+
|\
162+
| * Merge branch 'feature2'
163+
| |\
164+
| | * feature2
165+
* | | subfeature2
166+
* | | Merge branch 'subtree-sync' into subtree
167+
|\| |
168+
| * | Merge branch 'feature1'
169+
| |\ \
170+
| | * | feature1
171+
| | |/
172+
* | / subfeature1
173+
|/ /
174+
* | add even more content
175+
* | subtree edit from main repo
176+
|/
177+
* add file2 (in subtree)
178+
$ josh-filter -s ":rev($SUBTREE_TIP:prefix=subtree):/subtree" refs/heads/master --update refs/heads/subtree --reverse
179+
[1] :prefix=subtree
180+
[9] :/subtree
181+
[9] :rev(c036f944faafb865e0585e4fa5e005afa0aeea3f:prefix=subtree)
182+
183+
$ git log --graph --pretty=%H:%s refs/heads/master
184+
* 6ac0ba56575859cfaacd5818084333e532ffc442:Merge branch 'subtree-sync' into subtree
185+
|\
186+
| * 38a6d753c8b17b4c6721050befbccff012dfde85:Merge branch 'feature2'
187+
| |\
188+
| | * 221f5ceab31209c3d3b16d5b2485ea54c465eca6:feature2
189+
* | | 75e90f7f1b54cc343f2f75dcdee33650654a52a6:subfeature2
190+
* | | 3fa497039e5b384cb44b704e6e96f52e0ae599c9:Merge branch 'subtree-sync' into subtree
191+
|\| |
192+
| * | 2739fb8f0b3f6d5a264fb89ea20674fe34790321:Merge branch 'feature1'
193+
| |\ \
194+
| | * | dbfaf5dd32fc39ce3c0ebe61864406bb7e2ad113:feature1
195+
| | |/
196+
* | / 59b5c1623da3f89229c6dd36f8baf2e5868d0288:subfeature1
197+
|/ /
198+
* | 103bfec17c47adbe70a95fca90caefb989b6cda6:add even more content
199+
* | 41130c5d66736545562212f820cdbfbb3d3779c4:subtree edit from main repo
200+
* | 0642c36d6b53f7e829531aed848e3ceff0762c64:subtree merge
201+
|\|
202+
| * c036f944faafb865e0585e4fa5e005afa0aeea3f:add file2 (in subtree)
203+
* 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:add file1
204+
205+
$ git ls-tree --name-only -r 3fa497039e5b384cb44b704e6e96f52e0ae599c9
206+
feature1
207+
file1
208+
subtree/file2
209+
subtree/subfeature1

0 commit comments

Comments
 (0)