diff mbox series

[v2,6/7] sequencer: implement 'update-refs' command

Message ID 68f8e51b19c024e1cb075dffd07f81cceaba5957.1654634569.git.gitgitgadget@gmail.com (mailing list archive)
State New, archived
Headers show
Series rebase: update branches in multi-part topic | expand

Commit Message

Derrick Stolee June 7, 2022, 8:42 p.m. UTC
From: Derrick Stolee <derrickstolee@github.com>

The previous change allowed 'git rebase --update-refs' to create 'label'
commands for each branch  among the commits being rewritten and add an
'update-refs' command at the end of the todo list. Now, teach Git to
update the refs during that final 'update-refs' command.

We need to create an array of new and old OIDs for each ref by iterating
over the refs/rewritten/for-update-refs/ namespace. We cannot update the
refs in-place since this will confuse the refs iterator.

Signed-off-by: Derrick Stolee <derrickstolee@github.com>
---
 sequencer.c                   | 62 ++++++++++++++++++++++++++++++++++-
 t/t3404-rebase-interactive.sh | 24 ++++++++++++++
 2 files changed, 85 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/sequencer.c b/sequencer.c
index 94f8d52e041..a8f62ce8e5f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -4061,11 +4061,71 @@  leave_merge:
 	return ret;
 }
 
-static int do_update_refs(struct repository *r)
+struct update_refs_context {
+	struct ref_store *refs;
+	char **ref_names;
+	struct object_id *old;
+	struct object_id *new;
+	size_t nr;
+	size_t alloc;
+};
+
+static int add_ref_to_context(const char *refname,
+			      const struct object_id *oid,
+			      int flags,
+			      void *data)
 {
+	int f = 0;
+	const char *name;
+	struct update_refs_context *ctx = data;
+
+	ALLOC_GROW(ctx->ref_names, ctx->nr + 1, ctx->alloc);
+	ALLOC_GROW(ctx->old, ctx->nr + 1, ctx->alloc);
+	ALLOC_GROW(ctx->new, ctx->nr + 1, ctx->alloc);
+
+	if (!skip_prefix(refname, "refs/rewritten/for-update-refs/", &name))
+		return 1;
+
+	ctx->ref_names[ctx->nr] = xstrdup(name);
+	oidcpy(&ctx->new[ctx->nr], oid);
+	if (!refs_resolve_ref_unsafe(ctx->refs, name, 0,
+				     &ctx->old[ctx->nr], &f))
+		return 1;
+
+	ctx->nr++;
 	return 0;
 }
 
+static int do_update_refs(struct repository *r)
+{
+	int i, res;
+	struct update_refs_context ctx = {
+		.refs = get_main_ref_store(r),
+		.alloc = 16,
+	};
+	ALLOC_ARRAY(ctx.ref_names, ctx.alloc);
+	ALLOC_ARRAY(ctx.old, ctx.alloc);
+	ALLOC_ARRAY(ctx.new, ctx.alloc);
+
+	res = refs_for_each_fullref_in(ctx.refs,
+				       "refs/rewritten/for-update-refs/",
+				       add_ref_to_context,
+				       &ctx);
+
+	for (i = 0; !res && i < ctx.nr; i++)
+		res = refs_update_ref(ctx.refs, "rewritten during rebase",
+				ctx.ref_names[i],
+				&ctx.new[i], &ctx.old[i],
+				0, UPDATE_REFS_MSG_ON_ERR);
+
+	for (i = 0; i < ctx.nr; i++)
+		free(ctx.ref_names[i]);
+	free(ctx.ref_names);
+	free(ctx.old);
+	free(ctx.new);
+	return res;
+}
+
 static int is_final_fixup(struct todo_list *todo_list)
 {
 	int i = todo_list->current;
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 5e99ad7f3b6..72711efec28 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1815,6 +1815,30 @@  test_expect_success '--update-refs adds commands with --rebase-merges' '
 	)
 '
 
+compare_two_refs () {
+	git rev-parse $1 >expect &&
+	git rev-parse $2 >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success '--update-refs updates refs correctly' '
+	git checkout -B update-refs no-conflict-branch &&
+	git branch -f base HEAD~4 &&
+	git branch -f first HEAD~3 &&
+	git branch -f second HEAD~3 &&
+	git branch -f third HEAD~1 &&
+	test_commit extra2 fileX &&
+	git commit --amend --fixup=L &&
+	(
+		git rebase -i --autosquash --update-refs primary &&
+
+		compare_two_refs HEAD~3 refs/heads/first &&
+		compare_two_refs HEAD~3 refs/heads/second &&
+		compare_two_refs HEAD~1 refs/heads/third &&
+		compare_two_refs HEAD refs/heads/no-conflict-branch
+	)
+'
+
 # This must be the last test in this file
 test_expect_success '$EDITOR and friends are unchanged' '
 	test_editor_unchanged