Skip to content

Commit df072e1

Browse files
committed
Merge branch 'jt/rebase-allow-duplicate' into pu
* jt/rebase-allow-duplicate: rebase --merge: optionally skip upstreamed commits
2 parents cdd03bc + de8be84 commit df072e1

File tree

5 files changed

+106
-3
lines changed

5 files changed

+106
-3
lines changed

Documentation/git-rebase.txt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,20 @@ See also INCOMPATIBLE OPTIONS below.
347347
+
348348
See also INCOMPATIBLE OPTIONS below.
349349

350+
--skip-cherry-pick-detection::
351+
--no-skip-cherry-pick-detection::
352+
Whether rebase tries to determine if commits are already present
353+
upstream, i.e. if there are commits which are cherry-picks. If such
354+
detection is done, any commits being rebased which are cherry-picks
355+
will be dropped, since those commits are already found upstream. If
356+
such detection is not done, those commits will be re-applied, which
357+
most likely will result in no new changes (as the changes are already
358+
upstream) and result in the commit being dropped anyway. cherry-pick
359+
detection is the default, but can be expensive in repos with a large
360+
number of upstream commits that need to be read.
361+
+
362+
See also INCOMPATIBLE OPTIONS below.
363+
350364
--rerere-autoupdate::
351365
--no-rerere-autoupdate::
352366
Allow the rerere mechanism to update the index with the
@@ -601,6 +615,9 @@ In addition, the following pairs of options are incompatible:
601615
* --keep-base and --onto
602616
* --keep-base and --root
603617

618+
Also, the --skip-cherry-pick-detection option requires the use of the merge
619+
backend (e.g., through --merge).
620+
604621
BEHAVIORAL DIFFERENCES
605622
-----------------------
606623

@@ -1002,7 +1019,8 @@ Only works if the changes (patch IDs based on the diff contents) on
10021019
'subsystem' did.
10031020

10041021
In that case, the fix is easy because 'git rebase' knows to skip
1005-
changes that are already present in the new upstream. So if you say
1022+
changes that are already present in the new upstream (unless
1023+
`--skip-cherry-pick-detection` is given). So if you say
10061024
(assuming you're on 'topic')
10071025
------------
10081026
$ git rebase subsystem

builtin/rebase.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ struct rebase_options {
9898
struct strbuf git_format_patch_opt;
9999
int reschedule_failed_exec;
100100
int use_legacy_rebase;
101+
int skip_cherry_pick_detection;
101102
};
102103

103104
#define REBASE_OPTIONS_INIT { \
@@ -387,6 +388,7 @@ static int run_sequencer_rebase(struct rebase_options *opts,
387388
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
388389
flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
389390
flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
391+
flags |= opts->skip_cherry_pick_detection ? TODO_LIST_SKIP_CHERRY_PICK_DETECTION : 0;
390392

391393
switch (command) {
392394
case ACTION_NONE: {
@@ -1394,6 +1396,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
13941396
OPT_BOOL(0, "reschedule-failed-exec",
13951397
&reschedule_failed_exec,
13961398
N_("automatically re-schedule any `exec` that fails")),
1399+
OPT_BOOL(0, "skip-cherry-pick-detection", &options.skip_cherry_pick_detection,
1400+
N_("skip changes that are already present in the new upstream")),
13971401
OPT_END(),
13981402
};
13991403
int i;
@@ -1751,6 +1755,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
17511755
if (reschedule_failed_exec >= 0)
17521756
options.reschedule_failed_exec = reschedule_failed_exec;
17531757

1758+
if (options.skip_cherry_pick_detection && !is_merge(&options))
1759+
die(_("--skip-cherry-pick-detection does not work with the 'apply' backend"));
1760+
17541761
if (options.signoff) {
17551762
if (options.type == REBASE_PRESERVE_MERGES)
17561763
die("cannot combine '--signoff' with "

sequencer.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4881,12 +4881,13 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
48814881
struct commit *commit;
48824882
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
48834883
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
4884+
int skip_cherry_pick_detection = flags & TODO_LIST_SKIP_CHERRY_PICK_DETECTION;
48844885

48854886
repo_init_revisions(r, &revs, NULL);
48864887
revs.verbose_header = 1;
48874888
if (!rebase_merges)
48884889
revs.max_parents = 1;
4889-
revs.cherry_mark = 1;
4890+
revs.cherry_mark = !skip_cherry_pick_detection;
48904891
revs.limited = 1;
48914892
revs.reverse = 1;
48924893
revs.right_only = 1;

sequencer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ int sequencer_remove_state(struct replay_opts *opts);
150150
* `--onto`, we do not want to re-generate the root commits.
151151
*/
152152
#define TODO_LIST_ROOT_WITH_ONTO (1U << 6)
153-
153+
#define TODO_LIST_SKIP_CHERRY_PICK_DETECTION (1U << 7)
154154

155155
int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
156156
const char **argv, unsigned flags);

t/t3402-rebase-merge.sh

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,81 @@ test_expect_success 'rebase --skip works with two conflicts in a row' '
162162
git rebase --skip
163163
'
164164

165+
test_expect_success '--skip-cherry-pick-detection' '
166+
git init repo &&
167+
168+
# O(1-10) -- O(1-11) -- O(0-10) master
169+
# \
170+
# -- O(1-11) -- O(1-12) otherbranch
171+
172+
printf "Line %d\n" $(test_seq 1 10) >repo/file.txt &&
173+
git -C repo add file.txt &&
174+
git -C repo commit -m "base commit" &&
175+
176+
printf "Line %d\n" $(test_seq 1 11) >repo/file.txt &&
177+
git -C repo commit -a -m "add 11" &&
178+
179+
printf "Line %d\n" $(test_seq 0 10) >repo/file.txt &&
180+
git -C repo commit -a -m "add 0 delete 11" &&
181+
182+
git -C repo checkout -b otherbranch HEAD^^ &&
183+
printf "Line %d\n" $(test_seq 1 11) >repo/file.txt &&
184+
git -C repo commit -a -m "add 11 in another branch" &&
185+
186+
printf "Line %d\n" $(test_seq 1 12) >repo/file.txt &&
187+
git -C repo commit -a -m "add 12 in another branch" &&
188+
189+
# Regular rebase fails, because the 1-11 commit is deduplicated
190+
test_must_fail git -C repo rebase --merge master 2> err &&
191+
test_i18ngrep "error: could not apply.*add 12 in another branch" err &&
192+
git -C repo rebase --abort &&
193+
194+
# With --skip-cherry-pick-detection, it works
195+
git -C repo rebase --merge --skip-cherry-pick-detection master
196+
'
197+
198+
test_expect_success '--skip-cherry-pick-detection refrains from reading unneeded blobs' '
199+
git init server &&
200+
201+
# O(1-10) -- O(1-11) -- O(1-12) master
202+
# \
203+
# -- O(0-10) otherbranch
204+
205+
printf "Line %d\n" $(test_seq 1 10) >server/file.txt &&
206+
git -C server add file.txt &&
207+
git -C server commit -m "merge base" &&
208+
209+
printf "Line %d\n" $(test_seq 1 11) >server/file.txt &&
210+
git -C server commit -a -m "add 11" &&
211+
212+
printf "Line %d\n" $(test_seq 1 12) >server/file.txt &&
213+
git -C server commit -a -m "add 12" &&
214+
215+
git -C server checkout -b otherbranch HEAD^^ &&
216+
printf "Line %d\n" $(test_seq 0 10) >server/file.txt &&
217+
git -C server commit -a -m "add 0" &&
218+
219+
test_config -C server uploadpack.allowfilter 1 &&
220+
test_config -C server uploadpack.allowanysha1inwant 1 &&
221+
222+
git clone --filter=blob:none "file://$(pwd)/server" client &&
223+
git -C client checkout origin/master &&
224+
git -C client checkout origin/otherbranch &&
225+
226+
# Sanity check to ensure that the blobs from the merge base and "add
227+
# 11" are missing
228+
git -C client rev-list --objects --all --missing=print >missing_list &&
229+
MERGE_BASE_BLOB=$(git -C server rev-parse master^^:file.txt) &&
230+
ADD_11_BLOB=$(git -C server rev-parse master^:file.txt) &&
231+
grep "\\?$MERGE_BASE_BLOB" missing_list &&
232+
grep "\\?$ADD_11_BLOB" missing_list &&
233+
234+
git -C client rebase --merge --skip-cherry-pick-detection origin/master &&
235+
236+
# The blob from the merge base had to be fetched, but not "add 11"
237+
git -C client rev-list --objects --all --missing=print >missing_list &&
238+
! grep "\\?$MERGE_BASE_BLOB" missing_list &&
239+
grep "\\?$ADD_11_BLOB" missing_list
240+
'
241+
165242
test_done

0 commit comments

Comments
 (0)