Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.kernel.org/pub/scm/git/git.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2020-04-22 23:43:00 +0300
committerJunio C Hamano <gitster@pobox.com>2020-04-22 23:43:00 +0300
commitd6d561db1c0a14e8b89149694c0c662096c5fc9d (patch)
treed0180fa78b9b0c5d4c03e843686cdffcac8647e2
parentc7d8f69da5443788950b823bd6b9b663e1e686a3 (diff)
parent0fcb4f6b62c87a73971890070dea847b56176338 (diff)
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
-rw-r--r--Documentation/git-rebase.txt25
-rw-r--r--builtin/rebase.c7
-rw-r--r--sequencer.c3
-rw-r--r--sequencer.h2
-rwxr-xr-xt/t3402-rebase-merge.sh77
5 files changed, 110 insertions, 4 deletions
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 206e1db918..bed500f151 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -279,7 +279,8 @@ See also INCOMPATIBLE OPTIONS below.
+
Note that commits which start empty are kept (unless --no-keep-empty
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.
@@ -304,6 +305,24 @@ see the --empty flag.
+
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::
No-op. Rebasing commits with an empty message used to fail
and this option would override that behavior, allowing commits
@@ -604,6 +623,7 @@ are incompatible with the following options:
* --exec
* --no-keep-empty
* --empty=
+ * --reapply-cherry-picks
* --edit-todo
* --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.
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')
------------
$ git rebase subsystem
diff --git a/builtin/rebase.c b/builtin/rebase.c
index b62b450f8c..c466923869 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -96,6 +96,7 @@ struct rebase_options {
struct strbuf git_format_patch_opt;
int reschedule_failed_exec;
int use_legacy_rebase;
+ int reapply_cherry_picks;
};
#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->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
+ flags |= opts->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0;
switch (command) {
case ACTION_NONE: {
@@ -1586,6 +1588,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_BOOL(0, "reschedule-failed-exec",
&reschedule_failed_exec,
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(),
};
int i;
@@ -1829,6 +1833,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.empty != EMPTY_UNSPECIFIED)
imply_merge(&options, "--empty");
+ if (options.reapply_cherry_picks)
+ imply_merge(&options, "--reapply-cherry-picks");
+
if (gpg_sign)
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
diff --git a/sequencer.c b/sequencer.c
index ce28cad60a..f30bb73c70 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4852,12 +4852,13 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
+ int reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;
repo_init_revisions(r, &revs, NULL);
revs.verbose_header = 1;
if (!rebase_merges)
revs.max_parents = 1;
- revs.cherry_mark = 1;
+ revs.cherry_mark = !reapply_cherry_picks;
revs.limited = 1;
revs.reverse = 1;
revs.right_only = 1;
diff --git a/sequencer.h b/sequencer.h
index 04c5c31904..9611605711 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -150,7 +150,7 @@ int sequencer_remove_state(struct replay_opts *opts);
* `--onto`, we do not want to re-generate the root commits.
*/
#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,
const char **argv, unsigned flags);
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index a1ec501a87..6e032716a6 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -162,4 +162,81 @@ test_expect_success 'rebase --skip works with two conflicts in a row' '
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