Skip to content

Commit d6d561d

Browse files
committed
Merge branch 'jt/rebase-allow-duplicate'
Allow "git rebase" to reapply all local commits, even if the may be already in the upstream, without checking first. * jt/rebase-allow-duplicate: rebase --merge: optionally skip upstreamed commits
2 parents c7d8f69 + 0fcb4f6 commit d6d561d

File tree

5 files changed

+110
-4
lines changed

5 files changed

+110
-4
lines changed

Documentation/git-rebase.txt

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ See also INCOMPATIBLE OPTIONS below.
279279
+
280280
Note that commits which start empty are kept (unless --no-keep-empty
281281
is specified), and commits which are clean cherry-picks (as determined
282-
by `git log --cherry-mark ...`) are always dropped.
282+
by `git log --cherry-mark ...`) are detected and dropped as a
283+
preliminary step (unless --reapply-cherry-picks is passed).
283284
+
284285
See also INCOMPATIBLE OPTIONS below.
285286

@@ -304,6 +305,24 @@ see the --empty flag.
304305
+
305306
See also INCOMPATIBLE OPTIONS below.
306307

308+
--reapply-cherry-picks::
309+
--no-reapply-cherry-picks::
310+
Reapply all clean cherry-picks of any upstream commit instead
311+
of preemptively dropping them. (If these commits then become
312+
empty after rebasing, because they contain a subset of already
313+
upstream changes, the behavior towards them is controlled by
314+
the `--empty` flag.)
315+
+
316+
By default (or if `--no-reapply-cherry-picks` is given), these commits
317+
will be automatically dropped. Because this necessitates reading all
318+
upstream commits, this can be expensive in repos with a large number
319+
of upstream commits that need to be read.
320+
+
321+
`--reapply-cherry-picks` allows rebase to forgo reading all upstream
322+
commits, potentially improving performance.
323+
+
324+
See also INCOMPATIBLE OPTIONS below.
325+
307326
--allow-empty-message::
308327
No-op. Rebasing commits with an empty message used to fail
309328
and this option would override that behavior, allowing commits
@@ -604,6 +623,7 @@ are incompatible with the following options:
604623
* --exec
605624
* --no-keep-empty
606625
* --empty=
626+
* --reapply-cherry-picks
607627
* --edit-todo
608628
* --root when used in combination with --onto
609629

@@ -1020,7 +1040,8 @@ Only works if the changes (patch IDs based on the diff contents) on
10201040
'subsystem' did.
10211041

10221042
In that case, the fix is easy because 'git rebase' knows to skip
1023-
changes that are already present in the new upstream. So if you say
1043+
changes that are already present in the new upstream (unless
1044+
`--reapply-cherry-picks` is given). So if you say
10241045
(assuming you're on 'topic')
10251046
------------
10261047
$ git rebase subsystem

builtin/rebase.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ struct rebase_options {
9696
struct strbuf git_format_patch_opt;
9797
int reschedule_failed_exec;
9898
int use_legacy_rebase;
99+
int reapply_cherry_picks;
99100
};
100101

101102
#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->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0;
390392

391393
switch (command) {
392394
case ACTION_NONE: {
@@ -1586,6 +1588,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
15861588
OPT_BOOL(0, "reschedule-failed-exec",
15871589
&reschedule_failed_exec,
15881590
N_("automatically re-schedule any `exec` that fails")),
1591+
OPT_BOOL(0, "reapply-cherry-picks", &options.reapply_cherry_picks,
1592+
N_("apply all changes, even those already present upstream")),
15891593
OPT_END(),
15901594
};
15911595
int i;
@@ -1829,6 +1833,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
18291833
if (options.empty != EMPTY_UNSPECIFIED)
18301834
imply_merge(&options, "--empty");
18311835

1836+
if (options.reapply_cherry_picks)
1837+
imply_merge(&options, "--reapply-cherry-picks");
1838+
18321839
if (gpg_sign)
18331840
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
18341841

sequencer.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4852,12 +4852,13 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
48524852
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
48534853
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
48544854
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
4855+
int reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;
48554856

48564857
repo_init_revisions(r, &revs, NULL);
48574858
revs.verbose_header = 1;
48584859
if (!rebase_merges)
48594860
revs.max_parents = 1;
4860-
revs.cherry_mark = 1;
4861+
revs.cherry_mark = !reapply_cherry_picks;
48614862
revs.limited = 1;
48624863
revs.reverse = 1;
48634864
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_REAPPLY_CHERRY_PICKS (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 '--reapply-cherry-picks' '
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 --reapply-cherry-picks, it works
195+
git -C repo rebase --merge --reapply-cherry-picks master
196+
'
197+
198+
test_expect_success '--reapply-cherry-picks 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 --reapply-cherry-picks 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)