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
This commit is contained in:
commit
d6d561db1c
@ -279,7 +279,8 @@ See also INCOMPATIBLE OPTIONS below.
|
|||||||
+
|
+
|
||||||
Note that commits which start empty are kept (unless --no-keep-empty
|
Note that commits which start empty are kept (unless --no-keep-empty
|
||||||
is specified), and commits which are clean cherry-picks (as determined
|
is specified), and commits which are clean cherry-picks (as determined
|
||||||
by `git log --cherry-mark ...`) are always dropped.
|
by `git log --cherry-mark ...`) are detected and dropped as a
|
||||||
|
preliminary step (unless --reapply-cherry-picks is passed).
|
||||||
+
|
+
|
||||||
See also INCOMPATIBLE OPTIONS below.
|
See also INCOMPATIBLE OPTIONS below.
|
||||||
|
|
||||||
@ -304,6 +305,24 @@ see the --empty flag.
|
|||||||
+
|
+
|
||||||
See also INCOMPATIBLE OPTIONS below.
|
See also INCOMPATIBLE OPTIONS below.
|
||||||
|
|
||||||
|
--reapply-cherry-picks::
|
||||||
|
--no-reapply-cherry-picks::
|
||||||
|
Reapply all clean cherry-picks of any upstream commit instead
|
||||||
|
of preemptively dropping them. (If these commits then become
|
||||||
|
empty after rebasing, because they contain a subset of already
|
||||||
|
upstream changes, the behavior towards them is controlled by
|
||||||
|
the `--empty` flag.)
|
||||||
|
+
|
||||||
|
By default (or if `--no-reapply-cherry-picks` is given), these commits
|
||||||
|
will be automatically dropped. Because this necessitates reading all
|
||||||
|
upstream commits, this can be expensive in repos with a large number
|
||||||
|
of upstream commits that need to be read.
|
||||||
|
+
|
||||||
|
`--reapply-cherry-picks` allows rebase to forgo reading all upstream
|
||||||
|
commits, potentially improving performance.
|
||||||
|
+
|
||||||
|
See also INCOMPATIBLE OPTIONS below.
|
||||||
|
|
||||||
--allow-empty-message::
|
--allow-empty-message::
|
||||||
No-op. Rebasing commits with an empty message used to fail
|
No-op. Rebasing commits with an empty message used to fail
|
||||||
and this option would override that behavior, allowing commits
|
and this option would override that behavior, allowing commits
|
||||||
@ -604,6 +623,7 @@ are incompatible with the following options:
|
|||||||
* --exec
|
* --exec
|
||||||
* --no-keep-empty
|
* --no-keep-empty
|
||||||
* --empty=
|
* --empty=
|
||||||
|
* --reapply-cherry-picks
|
||||||
* --edit-todo
|
* --edit-todo
|
||||||
* --root when used in combination with --onto
|
* --root when used in combination with --onto
|
||||||
|
|
||||||
@ -1020,7 +1040,8 @@ Only works if the changes (patch IDs based on the diff contents) on
|
|||||||
'subsystem' did.
|
'subsystem' did.
|
||||||
|
|
||||||
In that case, the fix is easy because 'git rebase' knows to skip
|
In that case, the fix is easy because 'git rebase' knows to skip
|
||||||
changes that are already present in the new upstream. So if you say
|
changes that are already present in the new upstream (unless
|
||||||
|
`--reapply-cherry-picks` is given). So if you say
|
||||||
(assuming you're on 'topic')
|
(assuming you're on 'topic')
|
||||||
------------
|
------------
|
||||||
$ git rebase subsystem
|
$ git rebase subsystem
|
||||||
|
@ -96,6 +96,7 @@ struct rebase_options {
|
|||||||
struct strbuf git_format_patch_opt;
|
struct strbuf git_format_patch_opt;
|
||||||
int reschedule_failed_exec;
|
int reschedule_failed_exec;
|
||||||
int use_legacy_rebase;
|
int use_legacy_rebase;
|
||||||
|
int reapply_cherry_picks;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define REBASE_OPTIONS_INIT { \
|
#define REBASE_OPTIONS_INIT { \
|
||||||
@ -387,6 +388,7 @@ static int run_sequencer_rebase(struct rebase_options *opts,
|
|||||||
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
|
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
|
||||||
flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
|
flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
|
||||||
flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
|
flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
|
||||||
|
flags |= opts->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0;
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case ACTION_NONE: {
|
case ACTION_NONE: {
|
||||||
@ -1586,6 +1588,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
OPT_BOOL(0, "reschedule-failed-exec",
|
OPT_BOOL(0, "reschedule-failed-exec",
|
||||||
&reschedule_failed_exec,
|
&reschedule_failed_exec,
|
||||||
N_("automatically re-schedule any `exec` that fails")),
|
N_("automatically re-schedule any `exec` that fails")),
|
||||||
|
OPT_BOOL(0, "reapply-cherry-picks", &options.reapply_cherry_picks,
|
||||||
|
N_("apply all changes, even those already present upstream")),
|
||||||
OPT_END(),
|
OPT_END(),
|
||||||
};
|
};
|
||||||
int i;
|
int i;
|
||||||
@ -1829,6 +1833,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
if (options.empty != EMPTY_UNSPECIFIED)
|
if (options.empty != EMPTY_UNSPECIFIED)
|
||||||
imply_merge(&options, "--empty");
|
imply_merge(&options, "--empty");
|
||||||
|
|
||||||
|
if (options.reapply_cherry_picks)
|
||||||
|
imply_merge(&options, "--reapply-cherry-picks");
|
||||||
|
|
||||||
if (gpg_sign)
|
if (gpg_sign)
|
||||||
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
|
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
|
||||||
|
|
||||||
|
@ -4852,12 +4852,13 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
|
|||||||
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
|
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
|
||||||
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
|
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
|
||||||
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
|
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
|
||||||
|
int reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;
|
||||||
|
|
||||||
repo_init_revisions(r, &revs, NULL);
|
repo_init_revisions(r, &revs, NULL);
|
||||||
revs.verbose_header = 1;
|
revs.verbose_header = 1;
|
||||||
if (!rebase_merges)
|
if (!rebase_merges)
|
||||||
revs.max_parents = 1;
|
revs.max_parents = 1;
|
||||||
revs.cherry_mark = 1;
|
revs.cherry_mark = !reapply_cherry_picks;
|
||||||
revs.limited = 1;
|
revs.limited = 1;
|
||||||
revs.reverse = 1;
|
revs.reverse = 1;
|
||||||
revs.right_only = 1;
|
revs.right_only = 1;
|
||||||
|
@ -150,7 +150,7 @@ int sequencer_remove_state(struct replay_opts *opts);
|
|||||||
* `--onto`, we do not want to re-generate the root commits.
|
* `--onto`, we do not want to re-generate the root commits.
|
||||||
*/
|
*/
|
||||||
#define TODO_LIST_ROOT_WITH_ONTO (1U << 6)
|
#define TODO_LIST_ROOT_WITH_ONTO (1U << 6)
|
||||||
|
#define TODO_LIST_REAPPLY_CHERRY_PICKS (1U << 7)
|
||||||
|
|
||||||
int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
|
int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
|
||||||
const char **argv, unsigned flags);
|
const char **argv, unsigned flags);
|
||||||
|
@ -162,4 +162,81 @@ test_expect_success 'rebase --skip works with two conflicts in a row' '
|
|||||||
git rebase --skip
|
git rebase --skip
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success '--reapply-cherry-picks' '
|
||||||
|
git init repo &&
|
||||||
|
|
||||||
|
# O(1-10) -- O(1-11) -- O(0-10) master
|
||||||
|
# \
|
||||||
|
# -- O(1-11) -- O(1-12) otherbranch
|
||||||
|
|
||||||
|
printf "Line %d\n" $(test_seq 1 10) >repo/file.txt &&
|
||||||
|
git -C repo add file.txt &&
|
||||||
|
git -C repo commit -m "base commit" &&
|
||||||
|
|
||||||
|
printf "Line %d\n" $(test_seq 1 11) >repo/file.txt &&
|
||||||
|
git -C repo commit -a -m "add 11" &&
|
||||||
|
|
||||||
|
printf "Line %d\n" $(test_seq 0 10) >repo/file.txt &&
|
||||||
|
git -C repo commit -a -m "add 0 delete 11" &&
|
||||||
|
|
||||||
|
git -C repo checkout -b otherbranch HEAD^^ &&
|
||||||
|
printf "Line %d\n" $(test_seq 1 11) >repo/file.txt &&
|
||||||
|
git -C repo commit -a -m "add 11 in another branch" &&
|
||||||
|
|
||||||
|
printf "Line %d\n" $(test_seq 1 12) >repo/file.txt &&
|
||||||
|
git -C repo commit -a -m "add 12 in another branch" &&
|
||||||
|
|
||||||
|
# Regular rebase fails, because the 1-11 commit is deduplicated
|
||||||
|
test_must_fail git -C repo rebase --merge master 2> err &&
|
||||||
|
test_i18ngrep "error: could not apply.*add 12 in another branch" err &&
|
||||||
|
git -C repo rebase --abort &&
|
||||||
|
|
||||||
|
# With --reapply-cherry-picks, it works
|
||||||
|
git -C repo rebase --merge --reapply-cherry-picks master
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success '--reapply-cherry-picks refrains from reading unneeded blobs' '
|
||||||
|
git init server &&
|
||||||
|
|
||||||
|
# O(1-10) -- O(1-11) -- O(1-12) master
|
||||||
|
# \
|
||||||
|
# -- O(0-10) otherbranch
|
||||||
|
|
||||||
|
printf "Line %d\n" $(test_seq 1 10) >server/file.txt &&
|
||||||
|
git -C server add file.txt &&
|
||||||
|
git -C server commit -m "merge base" &&
|
||||||
|
|
||||||
|
printf "Line %d\n" $(test_seq 1 11) >server/file.txt &&
|
||||||
|
git -C server commit -a -m "add 11" &&
|
||||||
|
|
||||||
|
printf "Line %d\n" $(test_seq 1 12) >server/file.txt &&
|
||||||
|
git -C server commit -a -m "add 12" &&
|
||||||
|
|
||||||
|
git -C server checkout -b otherbranch HEAD^^ &&
|
||||||
|
printf "Line %d\n" $(test_seq 0 10) >server/file.txt &&
|
||||||
|
git -C server commit -a -m "add 0" &&
|
||||||
|
|
||||||
|
test_config -C server uploadpack.allowfilter 1 &&
|
||||||
|
test_config -C server uploadpack.allowanysha1inwant 1 &&
|
||||||
|
|
||||||
|
git clone --filter=blob:none "file://$(pwd)/server" client &&
|
||||||
|
git -C client checkout origin/master &&
|
||||||
|
git -C client checkout origin/otherbranch &&
|
||||||
|
|
||||||
|
# Sanity check to ensure that the blobs from the merge base and "add
|
||||||
|
# 11" are missing
|
||||||
|
git -C client rev-list --objects --all --missing=print >missing_list &&
|
||||||
|
MERGE_BASE_BLOB=$(git -C server rev-parse master^^:file.txt) &&
|
||||||
|
ADD_11_BLOB=$(git -C server rev-parse master^:file.txt) &&
|
||||||
|
grep "[?]$MERGE_BASE_BLOB" missing_list &&
|
||||||
|
grep "[?]$ADD_11_BLOB" missing_list &&
|
||||||
|
|
||||||
|
git -C client rebase --merge --reapply-cherry-picks origin/master &&
|
||||||
|
|
||||||
|
# The blob from the merge base had to be fetched, but not "add 11"
|
||||||
|
git -C client rev-list --objects --all --missing=print >missing_list &&
|
||||||
|
! grep "[?]$MERGE_BASE_BLOB" missing_list &&
|
||||||
|
grep "[?]$ADD_11_BLOB" missing_list
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user