git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: <rsbecker@nexbridge.com>
To: "'Edmundo Carmona Antoranz'" <eantoranz@gmail.com>,
	<git@vger.kernel.org>
Subject: RE: [RFC] introducing git replay
Date: Wed, 13 Apr 2022 13:26:51 -0400	[thread overview]
Message-ID: <033701d84f5b$ad167ce0$074376a0$@nexbridge.com> (raw)
In-Reply-To: <20220413164336.101390-1-eantoranz@gmail.com>

On April 13, 2022 12:44 PM, Edmundo Carmona Antoranz wrote:
>Let me explain with an easy-to-follow example:
>
>$ git checkout v2.35.0
>.
>.
>.
>HEAD is now at 89bece5c8c Git 2.35
>$ git commit --amend --no-edit
>[detached HEAD c58a5e5621] Git 2.35
> Author: Junio C Hamano <someone@somewhere>
> Date: Mon Jan 24 09:25:25 2022 -0800
> 2 files changed, 11 insertions(+), 1 deletion(-) $ git rebase
--rebase-merges --onto
>HEAD v2.35.0 v2.36.0-rc1 Auto-merging GIT-VERSION-GEN CONFLICT (content):
>Merge conflict in GIT-VERSION-GEN CONFLICT (content): Merge conflict in
>RelNotes
>error: could not apply 4c53a8c20f... Git 2.35.1
>hint: Resolve all conflicts manually, mark them as resolved with
>hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
>hint: You can instead skip this commit: run "git rebase --skip".
>hint: To abort and get back to the state before "git rebase", run "git
rebase --
>abort".
>Could not apply 4c53a8c20f... Git 2.35.1
>
>If HEAD and v2.35.0 share the same tree, it _should_ be possible to
recreate the
>commits that make up the range v2.35.0..v2.36.0-rc1 on top of HEAD without
>requiring any real "rebasing". Just creating new revisions with the same
>information except for different parents (and possibly a committer?).
>
>This is what git replay does. To achieve the same in this example:
>
>$ git checkout v2.35.0
>.
>.
>.
>HEAD is now at 89bece5c8c Git 2.35
>$ git commit --amend --no-edit
>[detached HEAD c682d8a22e] Git 2.35
> Author: Junio C Hamano <someone@somewhere>
> Date: Mon Jan 24 09:25:25 2022 -0800
> 2 files changed, 11 insertions(+), 1 deletion(-) $ git replay HEAD v2.35.0
v2.36.0-rc1
>8312ecf6404ab1bacd5521a2d8681a2410d13ede
>
>The ID that is printed out is the equivalent of V2.36.0-rc1 after
replaying.
>
>This is a RFC because:
>- Perhaps it is already possible to do it with git rebase
>  to achieve the same? But I haven't seen a recipe that
>  gets it done in stackoverflow, at least.
>- If it is not possible, I think getting this logic in rebase
>  (with a flag, for example --replay) makes sense.
>
>Let me know what you think.
>Interesting? Not?
>Keep it as a builtin (polishing it, it's just a rough cut at the time) or
get it into
>rebase?
>---
> Makefile         |   1 +
> builtin.h        |   1 +
> builtin/replay.c | 148
>+++++++++++++++++++++++++++++++++++++++++++++++
> git.c            |   1 +
> 4 files changed, 151 insertions(+)
> create mode 100644 builtin/replay.c
>
>diff --git a/Makefile b/Makefile
>index f8bccfab5e..71924d0c43 100644
>--- a/Makefile
>+++ b/Makefile
>@@ -1194,6 +1194,7 @@ BUILTIN_OBJS += builtin/remote-fd.o  BUILTIN_OBJS +=
>builtin/remote.o  BUILTIN_OBJS += builtin/repack.o  BUILTIN_OBJS +=
>builtin/replace.o
>+BUILTIN_OBJS += builtin/replay.o
> BUILTIN_OBJS += builtin/rerere.o
> BUILTIN_OBJS += builtin/reset.o
> BUILTIN_OBJS += builtin/rev-list.o
>diff --git a/builtin.h b/builtin.h
>index 40e9ecc848..0c1915c4c9 100644
>--- a/builtin.h
>+++ b/builtin.h
>@@ -207,6 +207,7 @@ int cmd_remote(int argc, const char **argv, const char
>*prefix);  int cmd_remote_ext(int argc, const char **argv, const char
*prefix);  int
>cmd_remote_fd(int argc, const char **argv, const char *prefix);  int
>cmd_repack(int argc, const char **argv, const char *prefix);
>+int cmd_replay(int argc, const char **argv, const char *prefix);
> int cmd_rerere(int argc, const char **argv, const char *prefix);  int
cmd_reset(int
>argc, const char **argv, const char *prefix);  int cmd_restore(int argc,
const char
>**argv, const char *prefix); diff --git a/builtin/replay.c
b/builtin/replay.c new file
>mode 100644 index 0000000000..ed970fa057
>--- /dev/null
>+++ b/builtin/replay.c
>@@ -0,0 +1,148 @@
>+/*
>+ * "git replay" builtin command
>+ *
>+ * Copyright (c) 2022 Edmundo Carmona Antoranz
>+ * Released under the terms of GPLv2
>+ */
>+
>+#include "builtin.h"
>+#include "revision.h"
>+#include "commit.h"
>+#include "cache.h"
>+
>+static struct commit **new_commits;
>+static unsigned long mappings_size = 0; static struct commit_list
>+*old_commits = NULL;
>+
>+static unsigned int replay_indexof(struct commit *commit,
>+				   struct commit_list *list)
>+{
>+	int res;
>+	if (list == NULL)
>+		return -1;
>+	if (!oidcmp(&list->item->object.oid,
>+		    &commit->object.oid))
>+		return 0;
>+	res = replay_indexof(commit, list->next);
>+	return res < 0 ? res : res + 1;
>+}
>+
>+static struct commit *replay_find_commit(const char *name) {
>+	struct commit *commit = lookup_commit_reference_by_name(name);
>+	if (!commit)
>+		die(_("no such branch/commit '%s'"), name);
>+	return commit;
>+}
>+
>+static struct commit* replay_commit(struct commit * orig_commit) {
>+	struct pretty_print_context ctx = {0};
>+	struct strbuf body = STRBUF_INIT;
>+	struct strbuf author = STRBUF_INIT;
>+	struct strbuf committer = STRBUF_INIT;
>+	struct object_id new_commit_oid;
>+	struct commit *new_commit;
>+
>+	struct commit_list *new_parents_head = NULL;
>+	struct commit_list **new_parents = &new_parents_head;
>+	struct commit_list *parents = orig_commit->parents;
>+	while (parents) {
>+		struct commit *parent = parents->item;
>+		int commit_index;
>+		struct commit *new_parent;
>+
>+		commit_index = replay_indexof(parent, old_commits);
>+
>+		if (commit_index < 0)
>+			 // won't be replayed, use the original parent
>+			new_parent = parent;
>+		else {
>+			// it might have been translated already
>+			if (!new_commits[commit_index])
>+				new_commits[commit_index] =
>replay_commit(parent);
>+			new_parent = new_commits[commit_index];
>+		}
>+		new_parents = commit_list_append(new_parent, new_parents);
>+		parents = parents->next;
>+	}
>+
>+	format_commit_message(orig_commit, "%B", &body, &ctx);
>+	// TODO timezones
>+	format_commit_message(orig_commit, "%an <%ae> %at +0000",
>&author, &ctx);
>+	// TODO consider committer (control with an option)
>+	format_commit_message(orig_commit, "%cn <%ce> %ct +0000",
>&committer,
>+&ctx);
>+
>+	commit_tree_extended(body.buf,
>+			     body.len,
>+			     get_commit_tree_oid(orig_commit),
>+			     new_parents_head,
>+			     &new_commit_oid,
>+			     author.buf,
>+			     committer.buf,
>+			     NULL, NULL);
>+
>+	new_commit = lookup_commit_or_die(&new_commit_oid,
>+					  "new commit");
>+
>+	strbuf_release(&author);
>+	strbuf_release(&body);
>+	strbuf_release(&committer);
>+
>+	return new_commit;
>+}
>+
>+static struct commit* replay(struct commit *new_base, struct commit
*old_base,
>+		      struct commit *tip)
>+{
>+	struct rev_info revs;
>+	struct commit *commit;
>+
>+	init_revisions(&revs, NULL);
>+
>+	old_base->object.flags |= UNINTERESTING;
>+	add_pending_object(&revs, &old_base->object, "old-base");
>+	add_pending_object(&revs, &tip->object, "tip");
>+
>+	if (prepare_revision_walk(&revs))
>+		die("Could not get revisions to replay");
>+
>+	while ((commit = get_revision(&revs)) != NULL)
>+		commit_list_insert(commit, &old_commits);
>+
>+	// save the mapping between the new and the old base
>+	commit_list_insert(old_base, &old_commits);
>+	mappings_size = commit_list_count(old_commits);
>+	new_commits = calloc(mappings_size, sizeof(struct commit));
>+	new_commits[replay_indexof(old_base, old_commits)] = new_base;
>+
>+	return replay_commit(tip);
>+}
>+
>+
>+int cmd_replay(int argc, const char **argv, const char *prefix) {
>+	struct commit *new_base;
>+	struct commit *old_base;
>+	struct commit *tip;
>+	struct commit *new_tip;
>+
>+	if (argc < 4) {
>+		die("Not enough parameters");
>+	}
>+
>+	new_base = replay_find_commit(argv[1]);
>+	old_base = replay_find_commit(argv[2]);
>+	tip = replay_find_commit(argv[3]);
>+
>+	if (oidcmp(get_commit_tree_oid(new_base),
>+		   get_commit_tree_oid(old_base)))
>+		die("The old and the new base do not have the same tree");
>+
>+	// get the list of revisions between old_base and tip
>+	new_tip = replay(new_base, old_base, tip);
>+
>+	printf("%s\n", oid_to_hex(&new_tip->object.oid));
>+
>+	return 0;
>+}
>diff --git a/git.c b/git.c
>index 3d8e48cf55..14de8d666f 100644
>--- a/git.c
>+++ b/git.c
>@@ -590,6 +590,7 @@ static struct cmd_struct commands[] = {
> 	{ "remote-fd", cmd_remote_fd, NO_PARSEOPT },
> 	{ "repack", cmd_repack, RUN_SETUP },
> 	{ "replace", cmd_replace, RUN_SETUP },
>+	{ "replay", cmd_replay, RUN_SETUP },
> 	{ "rerere", cmd_rerere, RUN_SETUP },
> 	{ "reset", cmd_reset, RUN_SETUP },
> 	{ "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE },
>--
>2.35.1

I'm sorry if I'm missing something here but how is this different from
cherry-pick A..B?
--Randall


  parent reply	other threads:[~2022-04-13 17:27 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-13 16:43 [RFC] introducing git replay Edmundo Carmona Antoranz
2022-04-13 17:05 ` Junio C Hamano
2022-04-15 18:46   ` Edmundo Carmona Antoranz
2022-04-15 20:33     ` Junio C Hamano
2022-04-16  5:35       ` Edmundo Carmona Antoranz
2022-04-16  6:39         ` Junio C Hamano
2022-04-16  7:02           ` Edmundo Carmona Antoranz
2022-04-17  5:05         ` Elijah Newren
2022-04-17  5:37           ` Edmundo Carmona Antoranz
2022-04-17 17:22             ` Martin von Zweigbergk
2022-04-18  7:04               ` Edmundo Carmona Antoranz
2022-04-18  7:29           ` Sergey Organov
2022-04-18 16:27             ` Elijah Newren
2022-04-18 17:33               ` Sergey Organov
2022-04-20 11:27               ` Tao Klerks
2022-04-21  2:33                 ` Elijah Newren
2022-04-13 17:26 ` rsbecker [this message]
2022-04-13 17:30   ` Edmundo Carmona Antoranz
2022-04-13 17:44     ` Edmundo Carmona Antoranz
2022-04-13 17:44 ` Phillip Susi
2022-04-13 17:49   ` Edmundo Carmona Antoranz
2022-04-13 19:07     ` Ævar Arnfjörð Bjarmason
2022-04-13 17:48 ` Junio C Hamano
2022-04-13 17:56   ` Edmundo Carmona Antoranz
2022-04-13 20:06   ` Eric Sunshine

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='033701d84f5b$ad167ce0$074376a0$@nexbridge.com' \
    --to=rsbecker@nexbridge.com \
    --cc=eantoranz@gmail.com \
    --cc=git@vger.kernel.org \
    /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).