From: Warren He <pickydaemon@gmail.com>
To: johannes.schindelin@gmx.de
Cc: git@vger.kernel.org, pickydaemon@gmail.com, wh109@yahoo.com
Subject: [PATCH v2] rebase: introduce --update-branches option
Date: Sat, 7 Sep 2019 16:44:13 -0700 [thread overview]
Message-ID: <20190907234413.1591-2-wh109@yahoo.com> (raw)
In-Reply-To: <20190907234413.1591-1-wh109@yahoo.com>
Rebasing normally updates the current branch to the rewritten version.
If any other branches point to commits rewritten along the way, those
remain untouched. This commit adds an `--update-branches` option, which
instructs the command to update any such branches that it encounters to
point to the rewritten versions of those commits.
Signed-off-by: Warren He <wh109@yahoo.com>
---
Documentation/git-rebase.txt | 9 ++++
builtin/rebase.c | 11 ++++-
sequencer.c | 58 +++++++++++++++++++++++-
sequencer.h | 6 ++-
t/t3431-rebase-update-branches.sh | 94 +++++++++++++++++++++++++++++++++++++++
5 files changed, 173 insertions(+), 5 deletions(-)
create mode 100755 t/t3431-rebase-update-branches.sh
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 6156609..b650a8f 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -246,6 +246,13 @@ leave out at most one of A and B, in which case it defaults to HEAD.
+
See also INCOMPATIBLE OPTIONS below.
+--update-branches::
+ If there are branch refs that point to commits that will be
+ reapplied, add shell commands to the todo list to update those
+ refs to point to the commits in the final history.
++
+See also INCOMPATIBLE OPTIONS below.
+
--allow-empty-message::
By default, rebasing commits with an empty message will fail.
This option overrides that behavior, allowing commits with empty
@@ -535,6 +542,7 @@ are incompatible with the following options:
* --interactive
* --exec
* --keep-empty
+ * --update-branches
* --edit-todo
* --root when used in combination with --onto
@@ -543,6 +551,7 @@ In addition, the following pairs of options are incompatible:
* --preserve-merges and --interactive
* --preserve-merges and --signoff
* --preserve-merges and --rebase-merges
+ * --preserve-merges and --update-branches
* --rebase-merges and --strategy
* --rebase-merges and --strategy-option
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 670096c..ab2308c 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -78,6 +78,7 @@ struct rebase_options {
int signoff;
int allow_rerere_autoupdate;
int keep_empty;
+ int update_branches;
int autosquash;
char *gpg_sign_opt;
int autostash;
@@ -349,8 +350,8 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
split_exec_commands(opts->cmd, &commands);
ret = complete_action(the_repository, &replay, flags,
- shortrevisions, opts->onto_name, opts->onto, head_hash,
- &commands, opts->autosquash, &todo_list);
+ shortrevisions, opts->onto_name, opts->onto, opts->head_name,
+ head_hash, &commands, opts->autosquash, &todo_list);
}
string_list_clear(&commands, 0);
@@ -375,6 +376,7 @@ static int run_rebase_interactive(struct rebase_options *opts,
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
+ flags |= opts->update_branches ? TODO_LIST_UPDATE_BRANCHES : 0;
switch (command) {
case ACTION_NONE: {
@@ -1453,6 +1455,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
OPT_BOOL('k', "keep-empty", &options.keep_empty,
N_("preserve empty commits during rebase")),
+ OPT_BOOL(0, "update-branches", &options.update_branches,
+ N_("update branches that point to rebased commits")),
OPT_BOOL(0, "autosquash", &options.autosquash,
N_("move commits that begin with "
"squash!/fixup! under -i")),
@@ -1710,6 +1714,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.keep_empty)
imply_interactive(&options, "--keep-empty");
+ if (options.update_branches)
+ imply_interactive(&options, "--update-branches");
+
if (gpg_sign) {
free(options.gpg_sign_opt);
options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign);
diff --git a/sequencer.c b/sequencer.c
index 34ebf8e..0fe8452 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4901,6 +4901,59 @@ void todo_list_add_exec_commands(struct todo_list *todo_list,
todo_list->alloc = alloc;
}
+/*
+ * Add commands to update branch refs after the todo list would pick a commit
+ * that a branch ref points to.
+ */
+static void todo_list_add_branch_updates(struct repository *r,
+ struct todo_list *todo_list,
+ const char *head_name)
+{
+ struct strbuf *buf = &todo_list->buf;
+ /* watch out: items in here point into todo_list's buf, not its own */
+ struct todo_list new_list = TODO_LIST_INIT;
+ int i;
+
+ load_ref_decorations(NULL, 0);
+
+ for (i = 0; i < todo_list->nr; i++) {
+ const struct todo_item *item = todo_list->items + i;
+ enum todo_command command = item->command;
+ const struct name_decoration *decoration;
+
+ *append_new_todo(&new_list) = todo_list->items[i];
+
+ if (!(is_pick_or_similar(command) || command == TODO_MERGE))
+ continue;
+
+ decoration = get_name_decoration(&item->commit->object);
+ for (; decoration; decoration = decoration->next) {
+ size_t base_offset;
+
+ /*
+ * (i) skip other refs like tags and remote refs
+ * (ii) rebase itself will update the current branch
+ * for us
+ */
+ if (decoration->type != DECORATION_REF_LOCAL ||
+ !strcmp(decoration->name, head_name))
+ continue;
+
+ base_offset = buf->len;
+ strbuf_addf(buf, "exec git branch -f %s\n",
+ prettify_refname(decoration->name));
+ parse_insn_line(r, append_new_todo(&new_list), buf->buf,
+ buf->buf + base_offset,
+ buf->buf + buf->len - 1);
+ }
+ }
+
+ SWAP(new_list.items, todo_list->items);
+ SWAP(new_list.nr, todo_list->nr);
+ SWAP(new_list.alloc, todo_list->alloc);
+ todo_list_release(&new_list);
+}
+
static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,
struct strbuf *buf, int num, unsigned flags)
{
@@ -5051,7 +5104,7 @@ static int skip_unnecessary_picks(struct repository *r,
int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
const char *shortrevisions, const char *onto_name,
- struct commit *onto, const char *orig_head,
+ struct commit *onto, const char *head_name, const char *orig_head,
struct string_list *commands, unsigned autosquash,
struct todo_list *todo_list)
{
@@ -5070,6 +5123,9 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
item->arg_len = item->arg_offset = item->flags = item->offset_in_buf = 0;
}
+ if (flags & TODO_LIST_UPDATE_BRANCHES)
+ todo_list_add_branch_updates(r, todo_list, head_name);
+
if (autosquash && todo_list_rearrange_squash(todo_list))
return -1;
diff --git a/sequencer.h b/sequencer.h
index 6704acb..69c6f71 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -143,6 +143,7 @@ int sequencer_remove_state(struct replay_opts *opts);
*/
#define TODO_LIST_REBASE_COUSINS (1U << 4)
#define TODO_LIST_APPEND_TODO_HELP (1U << 5)
+#define TODO_LIST_UPDATE_BRANCHES (1U << 6)
int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
const char **argv, unsigned flags);
@@ -152,8 +153,9 @@ void todo_list_add_exec_commands(struct todo_list *todo_list,
int check_todo_list_from_file(struct repository *r);
int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
const char *shortrevisions, const char *onto_name,
- struct commit *onto, const char *orig_head, struct string_list *commands,
- unsigned autosquash, struct todo_list *todo_list);
+ struct commit *onto, const char *head_name, const char *orig_head,
+ struct string_list *commands, unsigned autosquash,
+ struct todo_list *todo_list);
int todo_list_rearrange_squash(struct todo_list *todo_list);
/*
diff --git a/t/t3431-rebase-update-branches.sh b/t/t3431-rebase-update-branches.sh
new file mode 100755
index 0000000..b57b64a
--- /dev/null
+++ b/t/t3431-rebase-update-branches.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+test_description='git rebase --update-branches
+
+This test runs git rebase, updating branch refs that point to commits
+that are rebased.
+
+Initial setup:
+
+ A - B (master)
+ |\
+ | C (linear-early)
+ | \
+ | D (linear-late)
+ |\
+ | E (feat-e)
+ |\ \
+ | F | (feat-f)
+ | \|
+ | G (interim)
+ | \
+ | H (my-dev, my-hotfixes)
+ \
+ I - J - fixup! I (fixup-early)
+ \
+ K - fixup! J (fixup-late)
+'
+. ./test-lib.sh
+
+test_expect_success 'set up common' '
+ test_commit A &&
+ test_commit B
+'
+
+test_expect_success 'set up linear' '
+ git checkout -b linear-early A &&
+ test_commit C &&
+ git checkout -b linear-late &&
+ test_commit D
+'
+
+test_expect_success 'smoketest linear' '
+ git rebase --update-branches master
+'
+
+test_expect_success 'check linear' '
+ test_cmp_rev linear-early HEAD^
+'
+
+test_expect_success 'set up merge' '
+ git checkout -b feat-e A &&
+ test_commit E &&
+ git checkout -b feat-f A &&
+ test_commit F &&
+ git checkout -b interim &&
+ test_merge G feat-e &&
+ git checkout -b my-dev &&
+ test_commit H &&
+ git branch my-hotfixes
+'
+
+test_expect_success 'smoketest merge' '
+ git rebase -r --update-branches master
+'
+
+test_expect_success 'check merge' '
+ test_cmp_rev feat-e HEAD^^2 &&
+ test_cmp_rev feat-f HEAD^^ &&
+ test_cmp_rev interim HEAD^ &&
+ test_cmp_rev my-hotfixes HEAD
+'
+
+test_expect_success 'set up fixup' '
+ git checkout -b fixup-early A &&
+ test_commit I &&
+ test_commit J &&
+ test_commit "fixup! I" I.t II fixup-I &&
+ git checkout -b fixup-late &&
+ test_commit K &&
+ test_commit "fixup! J" J.t JJ fixup-J
+'
+
+test_expect_success 'smoketest fixup' '
+ git rebase -i --autosquash --update-branches master
+'
+
+test_expect_success 'check fixup' '
+ test_cmp_rev fixup-early HEAD^ &&
+ test_cmp_rev fixup-early^:I.t fixup-I:I.t &&
+ test_cmp_rev fixup-early:J.t fixup-J:J.t &&
+ test_cmp_rev HEAD:K.t K:K.t
+'
+
+test_done
--
2.7.4
next prev parent reply other threads:[~2019-09-07 23:44 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-09-02 23:41 [PATCH] rebase: introduce --update-branches option Warren He
2019-09-02 23:41 ` Warren He
2019-09-03 13:26 ` Johannes Schindelin
2019-09-07 23:44 ` [PATCH v2] " Warren He
2019-09-07 23:44 ` Warren He [this message]
2019-09-09 10:44 ` Phillip Wood
2019-09-09 14:13 ` Johannes Schindelin
2019-09-09 18:11 ` Junio C Hamano
2019-09-23 9:40 ` Phillip Wood
2019-09-03 0:50 ` [PATCH] " brian m. carlson
2019-09-03 1:21 ` Junio C Hamano
2019-09-03 1:39 ` Taylor Blau
2019-09-07 23:41 ` Warren He
2019-09-08 18:04 ` brian m. carlson
2019-09-03 12:19 ` Phillip Wood
2019-09-07 23:43 ` Warren He
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190907234413.1591-2-wh109@yahoo.com \
--to=pickydaemon@gmail.com \
--cc=git@vger.kernel.org \
--cc=johannes.schindelin@gmx.de \
--cc=wh109@yahoo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).