{"thread":{"id":"57725","subject":"[RFC] introducing git replay","startedAt":"2022-04-13T16:44:38Z","lastAt":"2022-04-21T02:33:59Z","messageCount":25,"participants":["Edmundo Carmona Antoranz","Junio C Hamano","rsbecker@nexbridge.com","Phillip Susi","Ævar Arnfjörð Bjarmason","Eric Sunshine","Elijah Newren","Martin von Zweigbergk","Sergey Organov","Tao Klerks"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"453502","messageId":"20220413164336.101390-1-eantoranz@gmail.com","threadId":"57725","inReplyTo":null,"subject":"[RFC] introducing git replay","fromName":"Edmundo Carmona Antoranz","fromEmail":"eantoranz@gmail.com","sentAt":"2022-04-13T16:43:35Z","receivedAt":"2022-04-13T16:44:38Z","isPatch":false,"sender":{"key":"eantoranz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1491018?v=4"},"body":"Let me explain with an easy-to-follow example:\n\n$ git checkout v2.35.0\n.\n.\n.\nHEAD is now at 89bece5c8c Git 2.35\n$ git commit --amend --no-edit\n[detached HEAD c58a5e5621] Git 2.35\n Author: Junio C Hamano <someone@somewhere>\n Date: Mon Jan 24 09:25:25 2022 -0800\n 2 files changed, 11 insertions(+), 1 deletion(-)\n$ git rebase --rebase-merges --onto HEAD v2.35.0 v2.36.0-rc1\nAuto-merging GIT-VERSION-GEN\nCONFLICT (content): Merge conflict in GIT-VERSION-GEN\nCONFLICT (content): Merge conflict in RelNotes\nerror: could not apply 4c53a8c20f... Git 2.35.1\nhint: Resolve all conflicts manually, mark them as resolved with\nhint: \"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\nhint: You can instead skip this commit: run \"git rebase --skip\".\nhint: To abort and get back to the state before \"git rebase\", run \"git rebase --abort\".\nCould not apply 4c53a8c20f... Git 2.35.1\n\nIf HEAD and v2.35.0 share the same tree, it _should_ be possible\nto recreate the commits that make up the range v2.35.0..v2.36.0-rc1\non top of HEAD without requiring any real \"rebasing\". Just creating\nnew revisions with the same information except for different parents\n(and possibly a committer?).\n\nThis is what git replay does. To achieve the same in this example:\n\n$ git checkout v2.35.0\n.\n.\n.\nHEAD is now at 89bece5c8c Git 2.35\n$ git commit --amend --no-edit\n[detached HEAD c682d8a22e] Git 2.35\n Author: Junio C Hamano <someone@somewhere>\n Date: Mon Jan 24 09:25:25 2022 -0800\n 2 files changed, 11 insertions(+), 1 deletion(-)\n$ git replay HEAD v2.35.0 v2.36.0-rc1\n8312ecf6404ab1bacd5521a2d8681a2410d13ede\n\nThe ID that is printed out is the equivalent\nof V2.36.0-rc1 after replaying.\n\nThis is a RFC because:\n- Perhaps it is already possible to do it with git rebase\n  to achieve the same? But I haven't seen a recipe that\n  gets it done in stackoverflow, at least.\n- If it is not possible, I think getting this logic in rebase\n  (with a flag, for example --replay) makes sense.\n\nLet me know what you think.\nInteresting? Not?\nKeep it as a builtin (polishing it, it's just a rough cut\nat the time) or get it into rebase?\n---\n Makefile         |   1 +\n builtin.h        |   1 +\n builtin/replay.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++\n git.c            |   1 +\n 4 files changed, 151 insertions(+)\n create mode 100644 builtin/replay.c\n\ndiff --git a/Makefile b/Makefile\nindex f8bccfab5e..71924d0c43 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1194,6 +1194,7 @@ BUILTIN_OBJS += builtin/remote-fd.o\n BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n+BUILTIN_OBJS += builtin/replay.o\n BUILTIN_OBJS += builtin/rerere.o\n BUILTIN_OBJS += builtin/reset.o\n BUILTIN_OBJS += builtin/rev-list.o\ndiff --git a/builtin.h b/builtin.h\nindex 40e9ecc848..0c1915c4c9 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -207,6 +207,7 @@ int cmd_remote(int argc, const char **argv, const char *prefix);\n int cmd_remote_ext(int argc, const char **argv, const char *prefix);\n int cmd_remote_fd(int argc, const char **argv, const char *prefix);\n int cmd_repack(int argc, const char **argv, const char *prefix);\n+int cmd_replay(int argc, const char **argv, const char *prefix);\n int cmd_rerere(int argc, const char **argv, const char *prefix);\n int cmd_reset(int argc, const char **argv, const char *prefix);\n int cmd_restore(int argc, const char **argv, const char *prefix);\ndiff --git a/builtin/replay.c b/builtin/replay.c\nnew file mode 100644\nindex 0000000000..ed970fa057\n--- /dev/null\n+++ b/builtin/replay.c\n@@ -0,0 +1,148 @@\n+/*\n+ * \"git replay\" builtin command\n+ *\n+ * Copyright (c) 2022 Edmundo Carmona Antoranz\n+ * Released under the terms of GPLv2\n+ */\n+\n+#include \"builtin.h\"\n+#include \"revision.h\"\n+#include \"commit.h\"\n+#include \"cache.h\"\n+\n+static struct commit **new_commits;\n+static unsigned long mappings_size = 0;\n+static struct commit_list *old_commits = NULL;\n+\n+static unsigned int replay_indexof(struct commit *commit,\n+\t\t\t\t   struct commit_list *list)\n+{\n+\tint res;\n+\tif (list == NULL)\n+\t\treturn -1;\n+\tif (!oidcmp(&list->item->object.oid,\n+\t\t    &commit->object.oid))\n+\t\treturn 0;\n+\tres = replay_indexof(commit, list->next);\n+\treturn res < 0 ? res : res + 1;\n+}\n+\n+static struct commit *replay_find_commit(const char *name)\n+{\n+\tstruct commit *commit = lookup_commit_reference_by_name(name);\n+\tif (!commit)\n+\t\tdie(_(\"no such branch/commit '%s'\"), name);\n+\treturn commit;\n+}\n+\n+static struct commit* replay_commit(struct commit * orig_commit)\n+{\n+\tstruct pretty_print_context ctx = {0};\n+\tstruct strbuf body = STRBUF_INIT;\n+\tstruct strbuf author = STRBUF_INIT;\n+\tstruct strbuf committer = STRBUF_INIT;\n+\tstruct object_id new_commit_oid;\n+\tstruct commit *new_commit;\n+\n+\tstruct commit_list *new_parents_head = NULL;\n+\tstruct commit_list **new_parents = &new_parents_head;\n+\tstruct commit_list *parents = orig_commit->parents;\n+\twhile (parents) {\n+\t\tstruct commit *parent = parents->item;\n+\t\tint commit_index;\n+\t\tstruct commit *new_parent;\n+\n+\t\tcommit_index = replay_indexof(parent, old_commits);\n+\n+\t\tif (commit_index < 0)\n+\t\t\t // won't be replayed, use the original parent\n+\t\t\tnew_parent = parent;\n+\t\telse {\n+\t\t\t// it might have been translated already\n+\t\t\tif (!new_commits[commit_index])\n+\t\t\t\tnew_commits[commit_index] = replay_commit(parent);\n+\t\t\tnew_parent = new_commits[commit_index];\n+\t\t}\n+\t\tnew_parents = commit_list_append(new_parent, new_parents);\n+\t\tparents = parents->next;\n+\t}\n+\n+\tformat_commit_message(orig_commit, \"%B\", &body, &ctx);\n+\t// TODO timezones\n+\tformat_commit_message(orig_commit, \"%an <%ae> %at +0000\", &author, &ctx);\n+\t// TODO consider committer (control with an option)\n+\tformat_commit_message(orig_commit, \"%cn <%ce> %ct +0000\", &committer, &ctx);\n+\n+\tcommit_tree_extended(body.buf,\n+\t\t\t     body.len,\n+\t\t\t     get_commit_tree_oid(orig_commit),\n+\t\t\t     new_parents_head,\n+\t\t\t     &new_commit_oid,\n+\t\t\t     author.buf,\n+\t\t\t     committer.buf,\n+\t\t\t     NULL, NULL);\n+\n+\tnew_commit = lookup_commit_or_die(&new_commit_oid,\n+\t\t\t\t\t  \"new commit\");\n+\n+\tstrbuf_release(&author);\n+\tstrbuf_release(&body);\n+\tstrbuf_release(&committer);\n+\n+\treturn new_commit;\n+}\n+\n+static struct commit* replay(struct commit *new_base, struct commit *old_base,\n+\t\t      struct commit *tip)\n+{\n+\tstruct rev_info revs;\n+\tstruct commit *commit;\n+\n+\tinit_revisions(&revs, NULL);\n+\n+\told_base->object.flags |= UNINTERESTING;\n+\tadd_pending_object(&revs, &old_base->object, \"old-base\");\n+\tadd_pending_object(&revs, &tip->object, \"tip\");\n+\n+\tif (prepare_revision_walk(&revs))\n+\t\tdie(\"Could not get revisions to replay\");\n+\n+\twhile ((commit = get_revision(&revs)) != NULL)\n+\t\tcommit_list_insert(commit, &old_commits);\n+\n+\t// save the mapping between the new and the old base\n+\tcommit_list_insert(old_base, &old_commits);\n+\tmappings_size = commit_list_count(old_commits);\n+\tnew_commits = calloc(mappings_size, sizeof(struct commit));\n+\tnew_commits[replay_indexof(old_base, old_commits)] = new_base;\n+\n+\treturn replay_commit(tip);\n+}\n+\n+\n+int cmd_replay(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct commit *new_base;\n+\tstruct commit *old_base;\n+\tstruct commit *tip;\n+\tstruct commit *new_tip;\n+\n+\tif (argc < 4) {\n+\t\tdie(\"Not enough parameters\");\n+\t}\n+\n+\tnew_base = replay_find_commit(argv[1]);\n+\told_base = replay_find_commit(argv[2]);\n+\ttip = replay_find_commit(argv[3]);\n+\n+\tif (oidcmp(get_commit_tree_oid(new_base),\n+\t\t   get_commit_tree_oid(old_base)))\n+\t\tdie(\"The old and the new base do not have the same tree\");\n+\n+\t// get the list of revisions between old_base and tip\n+\tnew_tip = replay(new_base, old_base, tip);\n+\n+\tprintf(\"%s\\n\", oid_to_hex(&new_tip->object.oid));\n+\n+\treturn 0;\n+}\ndiff --git a/git.c b/git.c\nindex 3d8e48cf55..14de8d666f 100644\n--- a/git.c\n+++ b/git.c\n@@ -590,6 +590,7 @@ static struct cmd_struct commands[] = {\n \t{ \"remote-fd\", cmd_remote_fd, NO_PARSEOPT },\n \t{ \"repack\", cmd_repack, RUN_SETUP },\n \t{ \"replace\", cmd_replace, RUN_SETUP },\n+\t{ \"replay\", cmd_replay, RUN_SETUP },\n \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t{ \"reset\", cmd_reset, RUN_SETUP },\n \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\n-- \n2.35.1\n\n"},{"id":"453505","messageId":"xmqq4k2wap8g.fsf@gitster.g","threadId":"57725","inReplyTo":"20220413164336.101390-1-eantoranz@gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-13T17:05:03Z","receivedAt":"2022-04-13T17:05:13Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Edmundo Carmona Antoranz <eantoranz@gmail.com> writes:\n\n> This is a RFC because:\n> - Perhaps it is already possible to do it with git rebase\n>   to achieve the same? But I haven't seen a recipe that\n>   gets it done in stackoverflow, at least.\n\nWithout thinking about it too much, out of gut reaction, it looks\nlike a better target for fast-export piped to fast-import than\nrebase or amend, if all it can do is to replay on _identical_ state\nand nothing else.\n\n> Let me know what you think.\n> Interesting? Not?\n\nIf this _were_ to allow some slight deviations of the base and carry\nthe differences forward, then it definitely belongs to rebase, and\nperhaps \"rebase --replay-merges\" should be taught to behave better\nwithout introducing a new option.  But otherwise, I do not think it\nis all that useful.  \n\nAlso, if this _were_ to allow recreating the shape of the history,\nusing updated tips of branches that were merged in the original\nhistory, perhaps taking hints from \"Merge branch X into Y\" in the\noriginal merge commit's log messages, that would be quite useful\naddition to the rebase mechanism, but this is not that.\n\nSo, not really, to me at least.\n"},{"id":"453506","messageId":"033701d84f5b$ad167ce0$074376a0$@nexbridge.com","threadId":"57725","inReplyTo":"20220413164336.101390-1-eantoranz@gmail.com","subject":"RE: [RFC] introducing git replay","fromName":"","fromEmail":"rsbecker@nexbridge.com","sentAt":"2022-04-13T17:26:51Z","receivedAt":"2022-04-13T17:27:04Z","isPatch":false,"sender":{"key":"randall.becker@nexbridge.ca","avatar":"https://avatars.githubusercontent.com/u/28956764?v=4"},"body":"On April 13, 2022 12:44 PM, Edmundo Carmona Antoranz wrote:\n>Let me explain with an easy-to-follow example:\n>\n>$ git checkout v2.35.0\n>.\n>.\n>.\n>HEAD is now at 89bece5c8c Git 2.35\n>$ git commit --amend --no-edit\n>[detached HEAD c58a5e5621] Git 2.35\n> Author: Junio C Hamano <someone@somewhere>\n> Date: Mon Jan 24 09:25:25 2022 -0800\n> 2 files changed, 11 insertions(+), 1 deletion(-) $ git rebase\n--rebase-merges --onto\n>HEAD v2.35.0 v2.36.0-rc1 Auto-merging GIT-VERSION-GEN CONFLICT (content):\n>Merge conflict in GIT-VERSION-GEN CONFLICT (content): Merge conflict in\n>RelNotes\n>error: could not apply 4c53a8c20f... Git 2.35.1\n>hint: Resolve all conflicts manually, mark them as resolved with\n>hint: \"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n>hint: You can instead skip this commit: run \"git rebase --skip\".\n>hint: To abort and get back to the state before \"git rebase\", run \"git\nrebase --\n>abort\".\n>Could not apply 4c53a8c20f... Git 2.35.1\n>\n>If HEAD and v2.35.0 share the same tree, it _should_ be possible to\nrecreate the\n>commits that make up the range v2.35.0..v2.36.0-rc1 on top of HEAD without\n>requiring any real \"rebasing\". Just creating new revisions with the same\n>information except for different parents (and possibly a committer?).\n>\n>This is what git replay does. To achieve the same in this example:\n>\n>$ git checkout v2.35.0\n>.\n>.\n>.\n>HEAD is now at 89bece5c8c Git 2.35\n>$ git commit --amend --no-edit\n>[detached HEAD c682d8a22e] Git 2.35\n> Author: Junio C Hamano <someone@somewhere>\n> Date: Mon Jan 24 09:25:25 2022 -0800\n> 2 files changed, 11 insertions(+), 1 deletion(-) $ git replay HEAD v2.35.0\nv2.36.0-rc1\n>8312ecf6404ab1bacd5521a2d8681a2410d13ede\n>\n>The ID that is printed out is the equivalent of V2.36.0-rc1 after\nreplaying.\n>\n>This is a RFC because:\n>- Perhaps it is already possible to do it with git rebase\n>  to achieve the same? But I haven't seen a recipe that\n>  gets it done in stackoverflow, at least.\n>- If it is not possible, I think getting this logic in rebase\n>  (with a flag, for example --replay) makes sense.\n>\n>Let me know what you think.\n>Interesting? Not?\n>Keep it as a builtin (polishing it, it's just a rough cut at the time) or\nget it into\n>rebase?\n>---\n> Makefile         |   1 +\n> builtin.h        |   1 +\n> builtin/replay.c | 148\n>+++++++++++++++++++++++++++++++++++++++++++++++\n> git.c            |   1 +\n> 4 files changed, 151 insertions(+)\n> create mode 100644 builtin/replay.c\n>\n>diff --git a/Makefile b/Makefile\n>index f8bccfab5e..71924d0c43 100644\n>--- a/Makefile\n>+++ b/Makefile\n>@@ -1194,6 +1194,7 @@ BUILTIN_OBJS += builtin/remote-fd.o  BUILTIN_OBJS +=\n>builtin/remote.o  BUILTIN_OBJS += builtin/repack.o  BUILTIN_OBJS +=\n>builtin/replace.o\n>+BUILTIN_OBJS += builtin/replay.o\n> BUILTIN_OBJS += builtin/rerere.o\n> BUILTIN_OBJS += builtin/reset.o\n> BUILTIN_OBJS += builtin/rev-list.o\n>diff --git a/builtin.h b/builtin.h\n>index 40e9ecc848..0c1915c4c9 100644\n>--- a/builtin.h\n>+++ b/builtin.h\n>@@ -207,6 +207,7 @@ int cmd_remote(int argc, const char **argv, const char\n>*prefix);  int cmd_remote_ext(int argc, const char **argv, const char\n*prefix);  int\n>cmd_remote_fd(int argc, const char **argv, const char *prefix);  int\n>cmd_repack(int argc, const char **argv, const char *prefix);\n>+int cmd_replay(int argc, const char **argv, const char *prefix);\n> int cmd_rerere(int argc, const char **argv, const char *prefix);  int\ncmd_reset(int\n>argc, const char **argv, const char *prefix);  int cmd_restore(int argc,\nconst char\n>**argv, const char *prefix); diff --git a/builtin/replay.c\nb/builtin/replay.c new file\n>mode 100644 index 0000000000..ed970fa057\n>--- /dev/null\n>+++ b/builtin/replay.c\n>@@ -0,0 +1,148 @@\n>+/*\n>+ * \"git replay\" builtin command\n>+ *\n>+ * Copyright (c) 2022 Edmundo Carmona Antoranz\n>+ * Released under the terms of GPLv2\n>+ */\n>+\n>+#include \"builtin.h\"\n>+#include \"revision.h\"\n>+#include \"commit.h\"\n>+#include \"cache.h\"\n>+\n>+static struct commit **new_commits;\n>+static unsigned long mappings_size = 0; static struct commit_list\n>+*old_commits = NULL;\n>+\n>+static unsigned int replay_indexof(struct commit *commit,\n>+\t\t\t\t   struct commit_list *list)\n>+{\n>+\tint res;\n>+\tif (list == NULL)\n>+\t\treturn -1;\n>+\tif (!oidcmp(&list->item->object.oid,\n>+\t\t    &commit->object.oid))\n>+\t\treturn 0;\n>+\tres = replay_indexof(commit, list->next);\n>+\treturn res < 0 ? res : res + 1;\n>+}\n>+\n>+static struct commit *replay_find_commit(const char *name) {\n>+\tstruct commit *commit = lookup_commit_reference_by_name(name);\n>+\tif (!commit)\n>+\t\tdie(_(\"no such branch/commit '%s'\"), name);\n>+\treturn commit;\n>+}\n>+\n>+static struct commit* replay_commit(struct commit * orig_commit) {\n>+\tstruct pretty_print_context ctx = {0};\n>+\tstruct strbuf body = STRBUF_INIT;\n>+\tstruct strbuf author = STRBUF_INIT;\n>+\tstruct strbuf committer = STRBUF_INIT;\n>+\tstruct object_id new_commit_oid;\n>+\tstruct commit *new_commit;\n>+\n>+\tstruct commit_list *new_parents_head = NULL;\n>+\tstruct commit_list **new_parents = &new_parents_head;\n>+\tstruct commit_list *parents = orig_commit->parents;\n>+\twhile (parents) {\n>+\t\tstruct commit *parent = parents->item;\n>+\t\tint commit_index;\n>+\t\tstruct commit *new_parent;\n>+\n>+\t\tcommit_index = replay_indexof(parent, old_commits);\n>+\n>+\t\tif (commit_index < 0)\n>+\t\t\t // won't be replayed, use the original parent\n>+\t\t\tnew_parent = parent;\n>+\t\telse {\n>+\t\t\t// it might have been translated already\n>+\t\t\tif (!new_commits[commit_index])\n>+\t\t\t\tnew_commits[commit_index] =\n>replay_commit(parent);\n>+\t\t\tnew_parent = new_commits[commit_index];\n>+\t\t}\n>+\t\tnew_parents = commit_list_append(new_parent, new_parents);\n>+\t\tparents = parents->next;\n>+\t}\n>+\n>+\tformat_commit_message(orig_commit, \"%B\", &body, &ctx);\n>+\t// TODO timezones\n>+\tformat_commit_message(orig_commit, \"%an <%ae> %at +0000\",\n>&author, &ctx);\n>+\t// TODO consider committer (control with an option)\n>+\tformat_commit_message(orig_commit, \"%cn <%ce> %ct +0000\",\n>&committer,\n>+&ctx);\n>+\n>+\tcommit_tree_extended(body.buf,\n>+\t\t\t     body.len,\n>+\t\t\t     get_commit_tree_oid(orig_commit),\n>+\t\t\t     new_parents_head,\n>+\t\t\t     &new_commit_oid,\n>+\t\t\t     author.buf,\n>+\t\t\t     committer.buf,\n>+\t\t\t     NULL, NULL);\n>+\n>+\tnew_commit = lookup_commit_or_die(&new_commit_oid,\n>+\t\t\t\t\t  \"new commit\");\n>+\n>+\tstrbuf_release(&author);\n>+\tstrbuf_release(&body);\n>+\tstrbuf_release(&committer);\n>+\n>+\treturn new_commit;\n>+}\n>+\n>+static struct commit* replay(struct commit *new_base, struct commit\n*old_base,\n>+\t\t      struct commit *tip)\n>+{\n>+\tstruct rev_info revs;\n>+\tstruct commit *commit;\n>+\n>+\tinit_revisions(&revs, NULL);\n>+\n>+\told_base->object.flags |= UNINTERESTING;\n>+\tadd_pending_object(&revs, &old_base->object, \"old-base\");\n>+\tadd_pending_object(&revs, &tip->object, \"tip\");\n>+\n>+\tif (prepare_revision_walk(&revs))\n>+\t\tdie(\"Could not get revisions to replay\");\n>+\n>+\twhile ((commit = get_revision(&revs)) != NULL)\n>+\t\tcommit_list_insert(commit, &old_commits);\n>+\n>+\t// save the mapping between the new and the old base\n>+\tcommit_list_insert(old_base, &old_commits);\n>+\tmappings_size = commit_list_count(old_commits);\n>+\tnew_commits = calloc(mappings_size, sizeof(struct commit));\n>+\tnew_commits[replay_indexof(old_base, old_commits)] = new_base;\n>+\n>+\treturn replay_commit(tip);\n>+}\n>+\n>+\n>+int cmd_replay(int argc, const char **argv, const char *prefix) {\n>+\tstruct commit *new_base;\n>+\tstruct commit *old_base;\n>+\tstruct commit *tip;\n>+\tstruct commit *new_tip;\n>+\n>+\tif (argc < 4) {\n>+\t\tdie(\"Not enough parameters\");\n>+\t}\n>+\n>+\tnew_base = replay_find_commit(argv[1]);\n>+\told_base = replay_find_commit(argv[2]);\n>+\ttip = replay_find_commit(argv[3]);\n>+\n>+\tif (oidcmp(get_commit_tree_oid(new_base),\n>+\t\t   get_commit_tree_oid(old_base)))\n>+\t\tdie(\"The old and the new base do not have the same tree\");\n>+\n>+\t// get the list of revisions between old_base and tip\n>+\tnew_tip = replay(new_base, old_base, tip);\n>+\n>+\tprintf(\"%s\\n\", oid_to_hex(&new_tip->object.oid));\n>+\n>+\treturn 0;\n>+}\n>diff --git a/git.c b/git.c\n>index 3d8e48cf55..14de8d666f 100644\n>--- a/git.c\n>+++ b/git.c\n>@@ -590,6 +590,7 @@ static struct cmd_struct commands[] = {\n> \t{ \"remote-fd\", cmd_remote_fd, NO_PARSEOPT },\n> \t{ \"repack\", cmd_repack, RUN_SETUP },\n> \t{ \"replace\", cmd_replace, RUN_SETUP },\n>+\t{ \"replay\", cmd_replay, RUN_SETUP },\n> \t{ \"rerere\", cmd_rerere, RUN_SETUP },\n> \t{ \"reset\", cmd_reset, RUN_SETUP },\n> \t{ \"restore\", cmd_restore, RUN_SETUP | NEED_WORK_TREE },\n>--\n>2.35.1\n\nI'm sorry if I'm missing something here but how is this different from\ncherry-pick A..B?\n--Randall\n\n"},{"id":"453507","messageId":"CAOc6etY0fyBnT9kMSm+s1LqfCsSV4RRppokuE5igme-M-cvW6g@mail.gmail.com","threadId":"57725","inReplyTo":"033701d84f5b$ad167ce0$074376a0$@nexbridge.com","subject":"Re: [RFC] introducing git replay","fromName":"Edmundo Carmona Antoranz","fromEmail":"eantoranz@gmail.com","sentAt":"2022-04-13T17:30:42Z","receivedAt":"2022-04-13T17:30:59Z","isPatch":false,"sender":{"key":"eantoranz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1491018?v=4"},"body":"On Wed, Apr 13, 2022 at 7:26 PM <rsbecker@nexbridge.com> wrote:\n>\n> >2.35.1\n>\n> I'm sorry if I'm missing something here but how is this different from\n> cherry-pick A..B?\n> --Randall\n>\n\nGood question, but cherry-pick has troubles with merges:\n\n(same example, after amending):\n$ git cherry-pick v2.35.0..v2.36.0-rc1\nerror: commit bb4921cf45e11d063e7bbe55f594adf8f0077d5d is a merge but\nno -m option was given.\nfatal: cherry-pick failed\n"},{"id":"453509","messageId":"CAOc6eta7-ZieTnTjmCicXoqf-j=d1SmfRT=PS+cAZUxKw+ti=Q@mail.gmail.com","threadId":"57725","inReplyTo":"CAOc6etY0fyBnT9kMSm+s1LqfCsSV4RRppokuE5igme-M-cvW6g@mail.gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Edmundo Carmona Antoranz","fromEmail":"eantoranz@gmail.com","sentAt":"2022-04-13T17:44:42Z","receivedAt":"2022-04-13T17:44:57Z","isPatch":false,"sender":{"key":"eantoranz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1491018?v=4"},"body":"On Wed, Apr 13, 2022 at 7:30 PM Edmundo Carmona Antoranz\n<eantoranz@gmail.com> wrote:\n>\n> On Wed, Apr 13, 2022 at 7:26 PM <rsbecker@nexbridge.com> wrote:\n> >\n> > >2.35.1\n> >\n> > I'm sorry if I'm missing something here but how is this different from\n> > cherry-pick A..B?\n> > --Randall\n> >\n>\n> Good question, but cherry-pick has troubles with merges:\n>\n> (same example, after amending):\n> $ git cherry-pick v2.35.0..v2.36.0-rc1\n> error: commit bb4921cf45e11d063e7bbe55f594adf8f0077d5d is a merge but\n> no -m option was given.\n> fatal: cherry-pick failed\n\nBy the way, Randall, it's not just _merges_. Correct me if I'm wrong\nbut cherry-pick (or rebase) will run merges (in all their glory) to get code\ncherry-picked. What I want to do is skip merges/conflicts altogether\nby using the information of existing revisions (the ones we want to\ncherry-pick) just adjusting their parents to get the new revisions.\n\nLet me know!\n"},{"id":"453510","messageId":"8735iglvxq.fsf@vps.thesusis.net","threadId":"57725","inReplyTo":"20220413164336.101390-1-eantoranz@gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Phillip Susi","fromEmail":"phill@thesusis.net","sentAt":"2022-04-13T17:44:01Z","receivedAt":"2022-04-13T17:45:23Z","isPatch":false,"sender":{"key":"phill@thesusis.net","avatar":null},"body":"\nEdmundo Carmona Antoranz <eantoranz@gmail.com> writes:\n\n> If HEAD and v2.35.0 share the same tree, it _should_ be possible\n> to recreate the commits that make up the range v2.35.0..v2.36.0-rc1\n> on top of HEAD without requiring any real \"rebasing\". Just creating\n\nIsn't that literally the definition of rebase?\n\n"},{"id":"453511","messageId":"xmqqlew898n3.fsf@gitster.g","threadId":"57725","inReplyTo":"20220413164336.101390-1-eantoranz@gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-13T17:48:48Z","receivedAt":"2022-04-13T17:48:56Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Edmundo Carmona Antoranz <eantoranz@gmail.com> writes:\n\nI've already gave my take on \"is this interesting?\" question with a\n\"not really\", but let's look at the code, as the expertise will\ntranslate to your future contributions easily, even if this\nparticular code may turn out not to be used in the project.\n\n> +static unsigned int replay_indexof(struct commit *commit,\n> +\t\t\t\t   struct commit_list *list)\n> +{\n> +\tint res;\n> +\tif (list == NULL)\n> +\t\treturn -1;\n\nWe encourage to have a blank line between the end of the decls and\nthe beginning of the statements.  Add one after the line that\ndeclares the variable \"res\".  \n\nIn an existing code that consistently uses the abbreviation, it is a\ndifferent story, but \"result\" is not too long to spell out, and\nbecause you do not use the variable that often anyway, you would\navoid unnecessary friction on the readers not to abbreviate it to\n\"res\" here.\n\n> +\tif (!oidcmp(&list->item->object.oid,\n> +\t\t    &commit->object.oid))\n\n\tif (!oidcmp(&list->item->object.oid, &commit->object.oid))\n\nis 66 columns wide and this line is wrapped too short, without an\napparent upside to make the result any easier to read.\n\n> +\t\treturn 0;\n> +\tres = replay_indexof(commit, list->next);\n\nDo we need to go recursive here?  It feels wasteful, compared to\niteratively doing this.  FWIW, is the singly chained commit_list the\nbest data structure if you have \"a collection of commits, among\nwhich you'd need to find an existing one, if any\"?  You may want to\nconsider using a hashset, perhaps, as the only thing you seem to be\ngetting out of the data structure is \"is this among the old_commits\nset?\"  Another possibility, if you do not call APIs that use the\nobject flags, may be to allocate a flag bit and mark these commits\nin the original history you discover via get_revision(), instead of\nplacing them in a singly chained commit_list structure.  Then the\nloop in replay_commit() can just see if parent->object.flags has\nthat \"in the original history?\" bit set to decide.\n\n> +\treturn res < 0 ? res : res + 1;\n> +}\n> +\n> +static struct commit *replay_find_commit(const char *name)\n> +{\n> +\tstruct commit *commit = lookup_commit_reference_by_name(name);\n> +\tif (!commit)\n> +\t\tdie(_(\"no such branch/commit '%s'\"), name);\n> +\treturn commit;\n> +}\n> +\n> +static struct commit* replay_commit(struct commit * orig_commit)\n\nIn our codebase, an asterisk sticks to the identifier, not the type.\n\n> +{\n> +\tstruct pretty_print_context ctx = {0};\n> +\tstruct strbuf body = STRBUF_INIT;\n> +\tstruct strbuf author = STRBUF_INIT;\n> +\tstruct strbuf committer = STRBUF_INIT;\n> +\tstruct object_id new_commit_oid;\n> +\tstruct commit *new_commit;\n> +\n> +\tstruct commit_list *new_parents_head = NULL;\n> +\tstruct commit_list **new_parents = &new_parents_head;\n> +\tstruct commit_list *parents = orig_commit->parents;\n> +\twhile (parents) {\n\nIt is not _wrong_ to have a blank line inside the decl block if\nthere is a logical separation between the groups.  I am not sure if\nthe one in the above after \"struct commit *new_commit\" qualifies as\none.\n\nRegardless, after such a large decl block, we want a blank line\nbefore the first statement.\n\n> +\t\tstruct commit *parent = parents->item;\n> +\t\tint commit_index;\n> +\t\tstruct commit *new_parent;\n> +\n> +\t\tcommit_index = replay_indexof(parent, old_commits);\n> +\n> +\t\tif (commit_index < 0)\n> +\t\t\t // won't be replayed, use the original parent\n\nWe still frown upon // comments in this codebase.\n\n> +\t\t\tnew_parent = parent;\n> +\t\telse {\n> +\t\t\t// it might have been translated already\n> +\t\t\tif (!new_commits[commit_index])\n> +\t\t\t\tnew_commits[commit_index] = replay_commit(parent);\n> +\t\t\tnew_parent = new_commits[commit_index];\n> +\t\t}\n> +\t\tnew_parents = commit_list_append(new_parent, new_parents);\n> +\t\tparents = parents->next;\n> +\t}\n> +\n> +\tformat_commit_message(orig_commit, \"%B\", &body, &ctx);\n> +\t// TODO timezones\n> +\tformat_commit_message(orig_commit, \"%an <%ae> %at +0000\", &author, &ctx);\n> +\t// TODO consider committer (control with an option)\n> +\tformat_commit_message(orig_commit, \"%cn <%ce> %ct +0000\", &committer, &ctx);\n\nYuck.  Shouldn't this code, whose purpose is to replay the messages\nand metadata of the commit as faithfully as possible while\nreparenting them, be just reusing the original commit object?\n\nPerhaps turning save_commit_buffer on, reading the original commit\nobject in the raw format, rewriting the parent pointers (and nothing\nelse) and finally calling write_object_file() to create the new\ncommit object is what this code should be doing instead.\n\nAnd that would be another reason why this is probably better done as\nfast-export piped to fast-import, but this message is not about the\ndesign of the \"feature\" itself, so let me stop talking about that.\n"},{"id":"453512","messageId":"CAOc6etbheKZ9CYJ+6Chz9gDj1WGK_5hQeHYTmhOKiUtDd0RKtQ@mail.gmail.com","threadId":"57725","inReplyTo":"8735iglvxq.fsf@vps.thesusis.net","subject":"Re: [RFC] introducing git replay","fromName":"Edmundo Carmona Antoranz","fromEmail":"eantoranz@gmail.com","sentAt":"2022-04-13T17:49:05Z","receivedAt":"2022-04-13T17:49:21Z","isPatch":false,"sender":{"key":"eantoranz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1491018?v=4"},"body":"On Wed, Apr 13, 2022 at 7:45 PM Phillip Susi <phill@thesusis.net> wrote:\n>\n>\n> Edmundo Carmona Antoranz <eantoranz@gmail.com> writes:\n>\n> > If HEAD and v2.35.0 share the same tree, it _should_ be possible\n> > to recreate the commits that make up the range v2.35.0..v2.36.0-rc1\n> > on top of HEAD without requiring any real \"rebasing\". Just creating\n>\n> Isn't that literally the definition of rebase?\n>\n\nWell, yeah. :-) What I mean is to skip the rebase _engine_. No\nmerging/cherry-picking/conflicts along the way of recreating the\nnew revisions. Say, clone the exact same revisions that we want to\n_rebase_ and adjust their parents, nothing else (or little else, like adjusting\nthe committer).\n"},{"id":"453513","messageId":"CAOc6etbJXDvXTKKpwi-dKcTnqadjEQH0wQ2pb_CHnJSHQiGZJg@mail.gmail.com","threadId":"57725","inReplyTo":"xmqqlew898n3.fsf@gitster.g","subject":"Re: [RFC] introducing git replay","fromName":"Edmundo Carmona Antoranz","fromEmail":"eantoranz@gmail.com","sentAt":"2022-04-13T17:56:50Z","receivedAt":"2022-04-13T17:57:07Z","isPatch":false,"sender":{"key":"eantoranz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1491018?v=4"},"body":"On Wed, Apr 13, 2022 at 7:48 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Edmundo Carmona Antoranz <eantoranz@gmail.com> writes:\n>\n> I've already gave my take on \"is this interesting?\" question with a\n> \"not really\", but let's look at the code, as the expertise will\n> translate to your future contributions easily, even if this\n> particular code may turn out not to be used in the project.\n>\n\nActually, thanks for taking the time. I knew there would be better\nways to do things so thanks for the tips.\n"},{"id":"453519","messageId":"220413.86o814er20.gmgdl@evledraar.gmail.com","threadId":"57725","inReplyTo":"CAOc6etbheKZ9CYJ+6Chz9gDj1WGK_5hQeHYTmhOKiUtDd0RKtQ@mail.gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T19:07:25Z","receivedAt":"2022-04-13T19:14:28Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Apr 13 2022, Edmundo Carmona Antoranz wrote:\n\n> On Wed, Apr 13, 2022 at 7:45 PM Phillip Susi <phill@thesusis.net> wrote:\n>>\n>>\n>> Edmundo Carmona Antoranz <eantoranz@gmail.com> writes:\n>>\n>> > If HEAD and v2.35.0 share the same tree, it _should_ be possible\n>> > to recreate the commits that make up the range v2.35.0..v2.36.0-rc1\n>> > on top of HEAD without requiring any real \"rebasing\". Just creating\n>>\n>> Isn't that literally the definition of rebase?\n>>\n>\n> Well, yeah. :-) What I mean is to skip the rebase _engine_. No\n> merging/cherry-picking/conflicts along the way of recreating the\n> new revisions. Say, clone the exact same revisions that we want to\n> _rebase_ and adjust their parents, nothing else (or little else, like adjusting\n> the committer).\n\nYeah I think this is fundimentally a good idea to pursue, and it's been\ndiscussed at various times in the past, and indeed, it seems best to\npursue it as a rebase optimization.\n\nI.e. given a history that has say files A.txt and B.txt, and a fork from\nA adding X.txt and Y.txt (and nothing else) we should be able to do a\n\"light rebase\" in moving that X & Y forward to it has B as the parent.\n\nRight now we do a rebase in all its glory to do that, with index\nupdating along the way (I forget how much that's been optimized, if at\nall) etc.\n\nBut if we can detect that we say only have additions of new files we\ncould just munge the headers as we go along, and the rest should all be\nhappening essentially as fast as we can SHA-1 the commit objects, which\nis basically what this built-in does, right?\n\n"},{"id":"453590","messageId":"CAPig+cRztjLPNOr0Fqm2NnRAGQ8kJDK5-WNWAMYhzG23uHp99Q@mail.gmail.com","threadId":"57725","inReplyTo":"xmqqlew898n3.fsf@gitster.g","subject":"Re: [RFC] introducing git replay","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-04-13T20:06:35Z","receivedAt":"2022-04-13T20:06:51Z","isPatch":false,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Apr 13, 2022 at 3:27 PM Junio C Hamano <gitster@pobox.com> wrote:\n> Edmundo Carmona Antoranz <eantoranz@gmail.com> writes:\n> > +static unsigned int replay_indexof(struct commit *commit,\n> > +                                struct commit_list *list)\n> > +{\n> > +     int res;\n> > +     if (list == NULL)\n> > +             return -1;\n>\n> We encourage to have a blank line between the end of the decls and\n> the beginning of the statements.  Add one after the line that\n> declares the variable \"res\".\n>\n> In an existing code that consistently uses the abbreviation, it is a\n> different story, but \"result\" is not too long to spell out, and\n> because you do not use the variable that often anyway, you would\n> avoid unnecessary friction on the readers not to abbreviate it to\n> \"res\" here.\n\nOne other minor point... in Git source code, comparison against NULL is spelled:\n\n    if (!list)\n        ...\n\n(And the inverse case of `if (list != NULL)` would just be `if (list)`.)\n"},{"id":"453718","messageId":"CAOc6etYvOhqQn3icWj3Ny1m+J_60h7aiqW-gvm=dQyDLgG=6NA@mail.gmail.com","threadId":"57725","inReplyTo":"xmqq4k2wap8g.fsf@gitster.g","subject":"Re: [RFC] introducing git replay","fromName":"Edmundo Carmona Antoranz","fromEmail":"eantoranz@gmail.com","sentAt":"2022-04-15T18:46:12Z","receivedAt":"2022-04-15T18:46:27Z","isPatch":false,"sender":{"key":"eantoranz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1491018?v=4"},"body":"On Wed, Apr 13, 2022 at 7:05 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n>\n> Also, if this _were_ to allow recreating the shape of the history,\n> using updated tips of branches that were merged in the original\n> history, perhaps taking hints from \"Merge branch X into Y\" in the\n> original merge commit's log messages, that would be quite useful\n> addition to the rebase mechanism, but this is not that.\n\nOk.... I have taken some time to read it through in calm and this part\nis making my head scratch because it sounds like the stuff that I had\nin mind in replay (or if we move  it into rebase or any other place).\nSo, to clarify what you are asking, when replaying _a given commit_:\n\n- If a parent is included in the history of the _old_ base, you would\nlike to see it included as is (no changes) as a parent of the replayed\ncommit.\n- If a parent is not included in the history of the _old base_, it\nmeans it has been replayed and we need to take the equivalent replayed\ncommit of the parent.\n\nSo, coming back to the example:\n\ngit replay HEAD v2.35.0 v2.36.0-rc1\n\nWe would replay all commits that make up the range v2.35.0..v2.36.0-rc1\n\nNow, for any given commit being replayed, if a parent is in that\nrange, it would use the replayed commit of the parent. If the parent\nis not in that range (which means that it's part of the history of\nv2.35.0, which is not being replayed) then we need to take it as-is\nstraight no changes which means that _the original_ parent commit will\nbe  linked in the resulting replayed commit that we are working on at\nthe time therefore linking the original history. (sorry if it sounds a\nlittle bit reiterative).\n\nIs that what you mean? Because, if that is the case, that is what\nreplay does as of this patch.\n\nIf you gave it a try like in the eample, you should end up with a\ncommit (the one reported at the end of the execution) that has the\n_exact same_ history shape of v2.36.0-rc1 (even linking to  commits\nthat are part of the history of v2.35.0).\n\nBut I am probably wrong in terms of what I understand that you meant.\nCan you expand a little bit, if you don't mind?\n"},{"id":"453722","messageId":"xmqqbkx2ccj4.fsf@gitster.g","threadId":"57725","inReplyTo":"CAOc6etYvOhqQn3icWj3Ny1m+J_60h7aiqW-gvm=dQyDLgG=6NA@mail.gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-15T20:33:19Z","receivedAt":"2022-04-15T20:33:24Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Edmundo Carmona Antoranz <eantoranz@gmail.com> writes:\n\n> But I am probably wrong in terms of what I understand that you meant.\n> Can you expand a little bit, if you don't mind?\n\nWhat I had in mind is what I have to do every day multiple times.\n'master'..'seen' is a series of merges of tips of different topic\nbranches.\n\n                 ---T topic\n                     \\\n       \\    \\    \\    \\\n  --M---o----o----o----S seen\n    ^\n    master\n\n\nSome of the topic branches may get updated and 'master' may gain\nmore commits by merging some topics.  Now it is time to update the\n'master'..'seen' chain.\n\n                 ---T---P topic (updated)\n                     \\\n       \\    \\    \\    \\\n  --M---o----o----o----S seen\n     \\\n      o\n       \\\n        N\n       master\n\nIt would be wonderful if a single command like replay can be used to\nsay \"In the old history master..seen I have bunch of merges.  master\nused to be M but now it is at N.  Rebuild M..S on top of N _but_\nwith a bit of twist.  Some of the topics in M...S may have been\nmerged to 'master' between M..N and the replayed history on top of N\ndoes not want to have a merge from such 'already graduated' topics.\nMany topics are updated, either by adding a new commit on top or\ncompletely rewritten, and we want an updated tip of these topic\nbranches, not the old tip that I merged when I created M..S chain,\nwhen replaying the history on top of N.\"\n\nThat kind of operation is quite different from what \"rebase\" does,\nand deserves to be under a different name.\n\nCompared to that, \"replay exactly the same set of commits in the\nsame shape on top of a different commit whose tree happens to be the\nsame as the original\", is a mere special case of \"rebase\" that is\nnot all that interesting.  It may be a worthwhile thing to do to\nteach \"rebase\" capable of doing so reliably and more efficiently,\nbut that still falls into \"improving rebase\" category, not meriting\na separate command.\n\n\n"},{"id":"453759","messageId":"CAOc6etYwMtfytbw6iRfnJnsexJhe7UydVu0OFUbWP0byS9i=MQ@mail.gmail.com","threadId":"57725","inReplyTo":"xmqqbkx2ccj4.fsf@gitster.g","subject":"Re: [RFC] introducing git replay","fromName":"Edmundo Carmona Antoranz","fromEmail":"eantoranz@gmail.com","sentAt":"2022-04-16T05:35:46Z","receivedAt":"2022-04-16T05:36:05Z","isPatch":false,"sender":{"key":"eantoranz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1491018?v=4"},"body":"On Fri, Apr 15, 2022 at 10:33 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n>\n> It would be wonderful if a single command like replay can be used to\n> say \"In the old history master..seen I have bunch of merges.  master\n> used to be M but now it is at N.  Rebuild M..S on top of N _but_\n> with a bit of twist.  Some of the topics in M...S may have been\n> merged to 'master' between M..N and the replayed history on top of N\n> does not want to have a merge from such 'already graduated' topics.\n> Many topics are updated, either by adding a new commit on top or\n> completely rewritten, and we want an updated tip of these topic\n> branches, not the old tip that I merged when I created M..S chain,\n> when replaying the history on top of N.\"\n>\n> That kind of operation is quite different from what \"rebase\" does,\n> and deserves to be under a different name.\n>\n\nLet me work a little bit on your workflow to see what I can do. Tip:\nIt will probably come out in the shape of a script. We can talk about\nwhat to do with it later.\n\n> Compared to that, \"replay exactly the same set of commits in the\n> same shape on top of a different commit whose tree happens to be the\n> same as the original\", is a mere special case of \"rebase\" that is\n> not all that interesting.  It may be a worthwhile thing to do to\n> teach \"rebase\" capable of doing so reliably and more efficiently,\n> but that still falls into \"improving rebase\" category, not meriting\n> a separate command.\n>\n>\n\nI agree that it might not require a full separate command. I'll see if\nI am able to get it into rebase.\n\nThanks for the feedback.\n"},{"id":"453761","messageId":"xmqq7d7p7cs8.fsf@gitster.g","threadId":"57725","inReplyTo":"CAOc6etYwMtfytbw6iRfnJnsexJhe7UydVu0OFUbWP0byS9i=MQ@mail.gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-16T06:39:03Z","receivedAt":"2022-04-16T06:39:10Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Edmundo Carmona Antoranz <eantoranz@gmail.com> writes:\n\n> Let me work a little bit on your workflow to see what I can do. Tip:\n> It will probably come out in the shape of a script. We can talk about\n> what to do with it later.\n\nHeh, I have a script to do what I have to do every day multiple\ntimes already, can be viewed in my 'todo' branch.\n"},{"id":"453762","messageId":"CAOc6etayh+OtusrFgAXjR=w_DmhzCQxws2cvJVJoyoH=uPEbbQ@mail.gmail.com","threadId":"57725","inReplyTo":"xmqq7d7p7cs8.fsf@gitster.g","subject":"Re: [RFC] introducing git replay","fromName":"Edmundo Carmona Antoranz","fromEmail":"eantoranz@gmail.com","sentAt":"2022-04-16T07:02:54Z","receivedAt":"2022-04-16T07:03:11Z","isPatch":false,"sender":{"key":"eantoranz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1491018?v=4"},"body":"On Sat, Apr 16, 2022 at 8:39 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Edmundo Carmona Antoranz <eantoranz@gmail.com> writes:\n>\n> > Let me work a little bit on your workflow to see what I can do. Tip:\n> > It will probably come out in the shape of a script. We can talk about\n> > what to do with it later.\n>\n> Heh, I have a script to do what I have to do every day multiple\n> times already, can be viewed in my 'todo' branch.\n\n:-D I thought you were in desperate need.\n"},{"id":"453789","messageId":"CABPp-BE=H-OcvGNJKm2zTvV3jEcUV0L=6W76ctpwOewZg56FKg@mail.gmail.com","threadId":"57725","inReplyTo":"CAOc6etYwMtfytbw6iRfnJnsexJhe7UydVu0OFUbWP0byS9i=MQ@mail.gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-04-17T05:05:46Z","receivedAt":"2022-04-17T05:15:11Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Apr 15, 2022 at 10:41 PM Edmundo Carmona Antoranz\n<eantoranz@gmail.com> wrote:\n>\n> On Fri, Apr 15, 2022 at 10:33 PM Junio C Hamano <gitster@pobox.com> wrote:\n> >\n> > It would be wonderful if a single command like replay can be used to\n> > say \"In the old history master..seen I have bunch of merges.  master\n> > used to be M but now it is at N.  Rebuild M..S on top of N _but_\n> > with a bit of twist.  Some of the topics in M...S may have been\n> > merged to 'master' between M..N and the replayed history on top of N\n> > does not want to have a merge from such 'already graduated' topics.\n> > Many topics are updated, either by adding a new commit on top or\n> > completely rewritten, and we want an updated tip of these topic\n> > branches, not the old tip that I merged when I created M..S chain,\n> > when replaying the history on top of N.\"\n> >\n> > That kind of operation is quite different from what \"rebase\" does,\n> > and deserves to be under a different name.\n> >\n>\n> Let me work a little bit on your workflow to see what I can do.\n\nReplaying merges is something I've put a little thought into, so allow\nme to provide some pointers that may help.  Merges need special\nhandling for replaying, and in my opinion, doing either just a new\nmerge of the new trees (what rebase --rebase-merges does), or just\nreusing existing trees (what you proposed to start this thread) are\nboth suboptimal, though the former is likely to just be annoying and\nrequire potentially unnecessary user refixing, whereas the latter can\nsilently discard changes or reintroduce discarded changes and could be\ndangerous.  More details on both of these...\n\nAn important part about merges is they may have resolved conflicts --\nboth textual (the standard conflict markers people have to resolve)\nand semantic (e.g. one person changes the API of some function, and\nthe other branch being merged adds a caller of that function, so the\nmerge has to modify the new caller to use the new API).  We do not\njust want to do a new merge and re-use the commit message (as rebase\n--rebase-merges does), for two reasons: (1) the user either has to\nre-resolve the textual conflict resolutions by hand, or use rerere\nwhich requires a working tree (and we'd like replays to proceed\nwithout a working tree where possible), and (2) it tosses semantic\nmerge conflict resolutions entirely.  We also do not just want to use\nexisting trees as-is (as you started with in your patch), for three\nreasons: (1) when we move to a new base the new merge needs to include\nthe changes from the newer base, (2) the topic might have additional\nchanges added (or removed) during the \"rebase\" which need to be\nreflected in the merge as well, and (3) the merge may have had\nadditional changes stuffed directly into it to solve semantic\nconflicts which we want \"ported\" to the new merge commit.    So, for\nhandling merges, we should avoid both of these overly simplistic\nmechanisms, and do something that tries to handle forward-porting\nthese conflict resolutions.  I have outlined steps to do so at\nhttps://lore.kernel.org/git/CABPp-BHp+d62dCyAaJfh1cZ8xVpGyb97mZryd02aCOX=Qn=Ltw@mail.gmail.com/\n\n> Tip: It will probably come out in the shape of a script. We can talk about\n> what to do with it later.\n\nNote that we've worked hard to replace scripts with builtins in git,\nespecially in the case of rebase.  Scripts are great for prototyping,\nand I fully support that, but I'd rather that scripts remained a\nprototype.  I'd be sad to see us regress and return to scripts for\nrebase.\n\n> > Compared to that, \"replay exactly the same set of commits in the\n> > same shape on top of a different commit whose tree happens to be the\n> > same as the original\", is a mere special case of \"rebase\" that is\n> > not all that interesting.  It may be a worthwhile thing to do to\n> > teach \"rebase\" capable of doing so reliably and more efficiently,\n> > but that still falls into \"improving rebase\" category, not meriting\n> > a separate command.\n>\n> I agree that it might not require a full separate command. I'll see if\n> I am able to get it into rebase.\n\nIf you'd like to try out some of the ideas in the link above for\nhandling replaying of merges, feel free.  I've done a little bit of\nplaying with this idea, and would be especially interested to learn of\nany new testcases or challenges you come up with.  I think a full\nimplementation requires changes to the merge machinery (at each of the\nmerge-ort.c, ll-merge.c, and maybe even xdiff/ levels), on top of the\nmerge machinery changes being driven by the merge-tree changes.\n\n[1] https://lore.kernel.org/git/CABPp-BGW39_5r8Lbt3ymR+F_=hWJcf=2e7O75vFNJ=3CEL5s=g@mail.gmail.com/\n\nI'd also like to mention that I have a git-replay command, as\nmentioned previously at various places (see [2,3,4,5,6,7] and probably\nelsewhere).  It's far from complete (I was busy with sparse-checkout,\nmerge-tree, etc., and then disappeared for over a month on top of\nthat), but I still intend to complete it.  I would like it to cover\nseveral usecases, including better replaying of merges, and I think\nusescases like the one Junio points out here should be in scope.  If\nyou'd like to play around with my git-replay (not even alpha quality\nyet, though it can do some things), feel free to take a look:\nhttps://github.com/newren/git/tree/replay\n\n[2] https://lore.kernel.org/git/nycvar.QRO.7.76.6.2110211147490.56@tvgsbejvaqbjf.bet/\n[3] https://lore.kernel.org/git/CABPp-BH_TiJaDpn2+VVjCb83NEFjL9teSk06+YiZyFGiTu8Lpg@mail.gmail.com/\n[4] https://lore.kernel.org/git/CABPp-BHpK8hPsiuHoYsf5D_rjcGLSW-_faL3ODoh56pG_2Luwg@mail.gmail.com/\n[5] https://lore.kernel.org/git/CABPp-BFQv+mrWj8iH0Vo5Pr5L922v=ZsVthFjofy5pm1Sx8x5Q@mail.gmail.com/\n[6] https://lore.kernel.org/git/CABPp-BE+DaBkis0r7pqs-kaChCvFhCEsyDg=gs3=QjWOPERaXQ@mail.gmail.com/\n[7] https://lore.kernel.org/git/pull.1122.v5.git.1645340082.gitgitgadget@gmail.com/\n"},{"id":"453790","messageId":"CAOc6etb7fmO2FAv09+wHsDBwnLsBi+B-CwRarm2tfYS-aUWcfg@mail.gmail.com","threadId":"57725","inReplyTo":"CABPp-BE=H-OcvGNJKm2zTvV3jEcUV0L=6W76ctpwOewZg56FKg@mail.gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Edmundo Carmona Antoranz","fromEmail":"eantoranz@gmail.com","sentAt":"2022-04-17T05:37:27Z","receivedAt":"2022-04-17T05:37:46Z","isPatch":false,"sender":{"key":"eantoranz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1491018?v=4"},"body":"On Sun, Apr 17, 2022 at 7:05 AM Elijah Newren <newren@gmail.com> wrote:\n>\n>\n> Replaying merges is something I've put a little thought into, so allow\n> me to provide some pointers that may help.  Merges need special\n> handling for replaying, and in my opinion, doing either just a new\n> merge of the new trees (what rebase --rebase-merges does), or just\n> reusing existing trees (what you proposed to start this thread) are\n> both suboptimal, though the former is likely to just be annoying and\n> require potentially unnecessary user refixing, whereas the latter can\n> silently discard changes or reintroduce discarded changes and could be\n> dangerous.  More details on both of these...\n>\n> An important part about merges is they may have resolved conflicts --\n> both textual (the standard conflict markers people have to resolve)\n> and semantic (e.g. one person changes the API of some function, and\n> the other branch being merged adds a caller of that function, so the\n> merge has to modify the new caller to use the new API).  We do not\n> just want to do a new merge and re-use the commit message (as rebase\n> --rebase-merges does), for two reasons: (1) the user either has to\n> re-resolve the textual conflict resolutions by hand, or use rerere\n> which requires a working tree (and we'd like replays to proceed\n> without a working tree where possible), and (2) it tosses semantic\n> merge conflict resolutions entirely.  We also do not just want to use\n> existing trees as-is (as you started with in your patch), for three\n> reasons: (1) when we move to a new base the new merge needs to include\n> the changes from the newer base, (2) the topic might have additional\n> changes added (or removed) during the \"rebase\" which need to be\n> reflected in the merge as well, and (3) the merge may have had\n> additional changes stuffed directly into it to solve semantic\n> conflicts which we want \"ported\" to the new merge commit.    So, for\n> handling merges, we should avoid both of these overly simplistic\n> mechanisms, and do something that tries to handle forward-porting\n> these conflict resolutions.  I have outlined steps to do so at\n> https://lore.kernel.org/git/CABPp-BHp+d62dCyAaJfh1cZ8xVpGyb97mZryd02aCOX=Qn=Ltw@mail.gmail.com/\n>\n\nHey, Elijah! Thanks for taking the time and the feedback.\n\nForget about me introducing replay as a separate command as a \"real\"\nproposal. My intent (and which I saw most simple to be able to show\nit) was to present the idea of an optimization (if you will) to the\nrebase mechanism under certain rather narrow conditions:\n\ngit rebase --onto A B C\n\nif A^{tree} == B^{tree} that means that we could create an equivalent\ncommit for the segment B..C on top of A without much hassle by reusing\nthe same trees from that segment (no need to calculate new trees...and\nno need to move along the working tree as we are creating those\ncommits).\n\nMy impression from reading your feedback is that you have a much\nbroader scope in terms of what you want to achieve.So, for the time\nbeing, I will work on trying to get the optimization in rebase and see\nhow far I am able to move it forward.... and you are able to keep\nreplay as a separate command if that is your will for the\nnot-so-distant future. :-)\n\nBR!\n\nPS I will be snooping around all of that material you are linking as I\nam sure there will be interesting stuff in there. And thanks, again!\n"},{"id":"453796","messageId":"CANiSa6g7ShxTXNEyJEyb==qCYNAMrNf30VkDPaydvOo0Bm+Onw@mail.gmail.com","threadId":"57725","inReplyTo":"CAOc6etb7fmO2FAv09+wHsDBwnLsBi+B-CwRarm2tfYS-aUWcfg@mail.gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Martin von Zweigbergk","fromEmail":"martinvonz@gmail.com","sentAt":"2022-04-17T17:22:54Z","receivedAt":"2022-04-17T17:23:12Z","isPatch":false,"sender":{"key":"martinvonz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/891642?v=4"},"body":"On Sun, Apr 17, 2022 at 5:30 AM Edmundo Carmona Antoranz\n<eantoranz@gmail.com> wrote:\n>\n> On Sun, Apr 17, 2022 at 7:05 AM Elijah Newren <newren@gmail.com> wrote:\n> >\n> >\n> > Replaying merges is something I've put a little thought into, so allow\n> > me to provide some pointers that may help.  Merges need special\n> > handling for replaying, and in my opinion, doing either just a new\n> > merge of the new trees (what rebase --rebase-merges does), or just\n> > reusing existing trees (what you proposed to start this thread) are\n> > both suboptimal, though the former is likely to just be annoying and\n> > require potentially unnecessary user refixing, whereas the latter can\n> > silently discard changes or reintroduce discarded changes and could be\n> > dangerous.  More details on both of these...\n> >\n> > An important part about merges is they may have resolved conflicts --\n> > both textual (the standard conflict markers people have to resolve)\n> > and semantic (e.g. one person changes the API of some function, and\n> > the other branch being merged adds a caller of that function, so the\n> > merge has to modify the new caller to use the new API).  We do not\n> > just want to do a new merge and re-use the commit message (as rebase\n> > --rebase-merges does), for two reasons: (1) the user either has to\n> > re-resolve the textual conflict resolutions by hand, or use rerere\n> > which requires a working tree (and we'd like replays to proceed\n> > without a working tree where possible), and (2) it tosses semantic\n> > merge conflict resolutions entirely.  We also do not just want to use\n> > existing trees as-is (as you started with in your patch), for three\n> > reasons: (1) when we move to a new base the new merge needs to include\n> > the changes from the newer base, (2) the topic might have additional\n> > changes added (or removed) during the \"rebase\" which need to be\n> > reflected in the merge as well, and (3) the merge may have had\n> > additional changes stuffed directly into it to solve semantic\n> > conflicts which we want \"ported\" to the new merge commit.    So, for\n> > handling merges, we should avoid both of these overly simplistic\n> > mechanisms, and do something that tries to handle forward-porting\n> > these conflict resolutions.  I have outlined steps to do so at\n> > https://lore.kernel.org/git/CABPp-BHp+d62dCyAaJfh1cZ8xVpGyb97mZryd02aCOX=Qn=Ltw@mail.gmail.com/\n> >\n>\n> Hey, Elijah! Thanks for taking the time and the feedback.\n>\n> Forget about me introducing replay as a separate command as a \"real\"\n> proposal. My intent (and which I saw most simple to be able to show\n> it) was to present the idea of an optimization (if you will) to the\n> rebase mechanism under certain rather narrow conditions:\n>\n> git rebase --onto A B C\n>\n> if A^{tree} == B^{tree} that means that we could create an equivalent\n> commit for the segment B..C on top of A without much hassle by reusing\n> the same trees from that segment (no need to calculate new trees...and\n> no need to move along the working tree as we are creating those\n> commits).\n>\n> My impression from reading your feedback is that you have a much\n> broader scope in terms of what you want to achieve.So, for the time\n> being, I will work on trying to get the optimization in rebase and see\n> how far I am able to move it forward.... and you are able to keep\n> replay as a separate command if that is your will for the\n> not-so-distant future. :-)\n\nMy (Git-compatible) VCS [1] is very relevant to this thread. It always\ntreats the contents of a merge commit as the diff compared to the\nre-merge (auto-merged) parents. That applies to diffs (like\n--remerge-diff) and rebases (what Elijah suggested in that link above)\n. An important part of the solution I went with is to store\ninformation about conflicts in the commits. Note that it's a more\nhigh-level representation of the conflicts - not conflict *markers* -\nthat's stored in the commits [2]. Adding a new kind of object type is\nobviously a huge step to take for Git, but perhaps you can consider it\nas long as these objects are not exchanged. Also, as you have probably\nnoticed with your `git replay` command, this kind of rebasing without\ntouching the working copy or trees can get pretty fast. I didn't see\nany performance numbers in your original message, but you are probably\nable to rebase >1k commits per second in the git.git repo [3].\n\n[1] https://github.com/martinvonz/jj\n[2] https://github.com/martinvonz/jj/blob/main/docs/technical/conflicts.md\n[3] https://github.com/martinvonz/jj/discussions/49\n"},{"id":"453800","messageId":"CAOc6etZmLusw4fxTx=9Gm6W2L4cSdOKQP_eX+23HGpdd=r1omA@mail.gmail.com","threadId":"57725","inReplyTo":"CANiSa6g7ShxTXNEyJEyb==qCYNAMrNf30VkDPaydvOo0Bm+Onw@mail.gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Edmundo Carmona Antoranz","fromEmail":"eantoranz@gmail.com","sentAt":"2022-04-18T07:04:36Z","receivedAt":"2022-04-18T07:04:53Z","isPatch":false,"sender":{"key":"eantoranz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1491018?v=4"},"body":"On Sun, Apr 17, 2022 at 7:23 PM Martin von Zweigbergk\n<martinvonz@gmail.com> wrote:\n>\n\n>\n> My (Git-compatible) VCS [1] is very relevant to this thread. It always\n> treats the contents of a merge commit as the diff compared to the\n> re-merge (auto-merged) parents. That applies to diffs (like\n> --remerge-diff) and rebases (what Elijah suggested in that link above)\n> . An important part of the solution I went with is to store\n> information about conflicts in the commits. Note that it's a more\n> high-level representation of the conflicts - not conflict *markers* -\n> that's stored in the commits [2]. Adding a new kind of object type is\n> obviously a huge step to take for Git, but perhaps you can consider it\n> as long as these objects are not exchanged. Also, as you have probably\n> noticed with your `git replay` command, this kind of rebasing without\n> touching the working copy or trees can get pretty fast. I didn't see\n> any performance numbers in your original message, but you are probably\n> able to rebase >1k commits per second in the git.git repo [3].\n>\n\nThanks for all your input (I mean, everybody who has jumped into the\nconversation). In my last experiment to get this into rebase (and\nstill without moving the working tree), the 900+ commits that make up\nthe segment v2.35.0..v2.36.0-rc1 are converted in some 143 ms in my\ncomputer, which is an aging dinosaur.\n"},{"id":"453801","messageId":"87lew226iw.fsf@osv.gnss.ru","threadId":"57725","inReplyTo":"CABPp-BE=H-OcvGNJKm2zTvV3jEcUV0L=6W76ctpwOewZg56FKg@mail.gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Sergey Organov","fromEmail":"sorganov@gmail.com","sentAt":"2022-04-18T07:29:59Z","receivedAt":"2022-04-18T07:33:14Z","isPatch":false,"sender":{"key":"sorganov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8501568?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> On Fri, Apr 15, 2022 at 10:41 PM Edmundo Carmona Antoranz\n> <eantoranz@gmail.com> wrote:\n>>\n>> On Fri, Apr 15, 2022 at 10:33 PM Junio C Hamano <gitster@pobox.com> wrote:\n>> >\n>> > It would be wonderful if a single command like replay can be used to\n>> > say \"In the old history master..seen I have bunch of merges.  master\n>> > used to be M but now it is at N.  Rebuild M..S on top of N _but_\n>> > with a bit of twist.  Some of the topics in M...S may have been\n>> > merged to 'master' between M..N and the replayed history on top of N\n>> > does not want to have a merge from such 'already graduated' topics.\n>> > Many topics are updated, either by adding a new commit on top or\n>> > completely rewritten, and we want an updated tip of these topic\n>> > branches, not the old tip that I merged when I created M..S chain,\n>> > when replaying the history on top of N.\"\n>> >\n>> > That kind of operation is quite different from what \"rebase\" does,\n>> > and deserves to be under a different name.\n>> >\n>>\n>> Let me work a little bit on your workflow to see what I can do.\n>\n> Replaying merges is something I've put a little thought into, so allow\n> me to provide some pointers that may help.  Merges need special\n> handling for replaying, and in my opinion, doing either just a new\n> merge of the new trees (what rebase --rebase-merges does), or just\n> reusing existing trees (what you proposed to start this thread) are\n> both suboptimal, though the former is likely to just be annoying and\n> require potentially unnecessary user refixing,\n\nIt silently drops user changes as well, and that's the worst thing about\nit, not annoyance.\n\n> whereas the latter can silently discard changes or reintroduce\n> discarded changes and could be dangerous. More details on both of\n> these...\n\nPlease consider yet another option:\n\nhttps://public-inbox.org/git/87r2oxe3o1.fsf@javad.com/\n\nthat at least is safe with respect to user changes.\n\n-- Sergey Organov\n"},{"id":"453814","messageId":"CABPp-BGQSN2iRWco4pQCVKA3AM6J0L0vyFMnYdrOgK0Pa26tWw@mail.gmail.com","threadId":"57725","inReplyTo":"87lew226iw.fsf@osv.gnss.ru","subject":"Re: [RFC] introducing git replay","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-04-18T16:27:54Z","receivedAt":"2022-04-18T16:28:37Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Hi Sergey,\n\nOn Mon, Apr 18, 2022 at 12:30 AM Sergey Organov <sorganov@gmail.com> wrote:\n>\n> Elijah Newren <newren@gmail.com> writes:\n>\n[...]\n> > Replaying merges is something I've put a little thought into, so allow\n> > me to provide some pointers that may help.  Merges need special\n> > handling for replaying, and in my opinion, doing either just a new\n> > merge of the new trees (what rebase --rebase-merges does), or just\n> > reusing existing trees (what you proposed to start this thread) are\n> > both suboptimal, though the former is likely to just be annoying and\n> > require potentially unnecessary user refixing,\n>\n> It silently drops user changes as well, and that's the worst thing about\n> it, not annoyance.\n\nYes, I mentioned that later in the email, but omitted it in the\nsummary you highlight here just because the fixed-tree case was so\nmuch more likely to do it.  Anyway, sorry for the inaccuracy in the\nsummarized version.\n\n> > whereas the latter can silently discard changes or reintroduce\n> > discarded changes and could be dangerous. More details on both of\n> > these...\n>\n> Please consider yet another option:\n\nI linked to where I had given another option.\n\n> https://public-inbox.org/git/87r2oxe3o1.fsf@javad.com/\n>\n> that at least is safe with respect to user changes.\n\nIf you read the suggestion I made (which I'll reinclude here at [1]),\nyou'll note that I read the old thread you link to with both your and\nPhillips' suggestions.  I dug into them with some examples, and came\nto the conclusion that we needed something better, as I briefly\ncommented when proposing my suggested alternative (at [1]).  I\nappreciate your suggestion and the time you put into it, but based on\nmy earlier investigation, I believe my suggestion would be a better\nway of preserving user changes in merges and I'll be implementing it.\nThe fact that Martin (in this thread) independently came up with the\nsame basic idea and implemented it in jj (though he apparently has\nsome further tweaks around the object model) and it works well\nsuggests to me that the idea has some real world testing too that\ngives me further confidence in the idea.\n\n[1] https://lore.kernel.org/git/CABPp-BGW39_5r8Lbt3ymR+F_=hWJcf=2e7O75vFNJ=3CEL5s=g@mail.gmail.com/\n"},{"id":"453893","messageId":"87czhewb3m.fsf@osv.gnss.ru","threadId":"57725","inReplyTo":"CABPp-BGQSN2iRWco4pQCVKA3AM6J0L0vyFMnYdrOgK0Pa26tWw@mail.gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Sergey Organov","fromEmail":"sorganov@gmail.com","sentAt":"2022-04-18T17:33:01Z","receivedAt":"2022-04-18T17:33:08Z","isPatch":false,"sender":{"key":"sorganov@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8501568?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> Hi Sergey,\n>\n> On Mon, Apr 18, 2022 at 12:30 AM Sergey Organov <sorganov@gmail.com> wrote:\n>>\n>> Elijah Newren <newren@gmail.com> writes:\n>>\n> [...]\n>> > Replaying merges is something I've put a little thought into, so allow\n>> > me to provide some pointers that may help.  Merges need special\n>> > handling for replaying, and in my opinion, doing either just a new\n>> > merge of the new trees (what rebase --rebase-merges does), or just\n>> > reusing existing trees (what you proposed to start this thread) are\n>> > both suboptimal, though the former is likely to just be annoying and\n>> > require potentially unnecessary user refixing,\n>>\n>> It silently drops user changes as well, and that's the worst thing about\n>> it, not annoyance.\n>\n> Yes, I mentioned that later in the email, but omitted it in the\n> summary you highlight here just because the fixed-tree case was so\n> much more likely to do it.  Anyway, sorry for the inaccuracy in the\n> summarized version.\n>\n>> > whereas the latter can silently discard changes or reintroduce\n>> > discarded changes and could be dangerous. More details on both of\n>> > these...\n>>\n>> Please consider yet another option:\n>\n> I linked to where I had given another option.\n>\n>> https://public-inbox.org/git/87r2oxe3o1.fsf@javad.com/\n>>\n>> that at least is safe with respect to user changes.\n>\n> If you read the suggestion I made (which I'll reinclude here at [1]),\n> you'll note that I read the old thread you link to with both your and\n> Phillips' suggestions.  I dug into them with some examples, and came\n> to the conclusion that we needed something better, as I briefly\n> commented when proposing my suggested alternative (at [1]).  I\n> appreciate your suggestion and the time you put into it, but based on\n> my earlier investigation, I believe my suggestion would be a better\n> way of preserving user changes in merges and I'll be implementing it.\n> The fact that Martin (in this thread) independently came up with the\n> same basic idea and implemented it in jj (though he apparently has\n> some further tweaks around the object model) and it works well\n> suggests to me that the idea has some real world testing too that\n> gives me further confidence in the idea.\n\nYep, whoever is going to actually implement something always wins, and\nthat's a good thing. I'm looking forward for the outcome of all this\nwith a hope.\n\nThanks,\n-- Sergey Organov\n"},{"id":"453972","messageId":"CAPMMpoh9-sm57D_OSVpo4A3KdJypNZbZ2KTWURvcOW0690eviA@mail.gmail.com","threadId":"57725","inReplyTo":"CABPp-BGQSN2iRWco4pQCVKA3AM6J0L0vyFMnYdrOgK0Pa26tWw@mail.gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-04-20T11:27:07Z","receivedAt":"2022-04-20T11:27:26Z","isPatch":false,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Mon, Apr 18, 2022 at 6:28 PM Elijah Newren <newren@gmail.com> wrote:\n>\n> If you read the suggestion I made (which I'll reinclude here at [1]),\n> you'll note that I read the old thread you link to with both your and\n> Phillips' suggestions.  I dug into them with some examples, and came\n> to the conclusion that we needed something better, as I briefly\n> commented when proposing my suggested alternative (at [1]).  I\n> appreciate your suggestion and the time you put into it, but based on\n> my earlier investigation, I believe my suggestion would be a better\n> way of preserving user changes in merges and I'll be implementing it.\n> The fact that Martin (in this thread) independently came up with the\n> same basic idea and implemented it in jj (though he apparently has\n> some further tweaks around the object model) and it works well\n> suggests to me that the idea has some real world testing too that\n> gives me further confidence in the idea.\n>\n> [1] https://lore.kernel.org/git/CABPp-BGW39_5r8Lbt3ymR+F_=hWJcf=2e7O75vFNJ=3CEL5s=g@mail.gmail.com/\n\nThank you for the clarification, and sorry I'm clearly missing\nsomething here - the link you provided is to a deeply threaded\nconversation about \"[PATCH 08/12] merge-ort: provide a\nmerge_get_conflicted_files() helper function\", in the context of a\nserver-side merge support patchset... I can't figure out how to relate\nthat conversation to the \"how can safely reusing previous merge\noutcomes when rebasing a merge work well?\" topic I thought you had\nintroduced here :(\n"},{"id":"454046","messageId":"CABPp-BFh-=3E-yMDaU1TgigwriFDbd-CwXe0PJKkc422LCSP6Q@mail.gmail.com","threadId":"57725","inReplyTo":"CAPMMpoh9-sm57D_OSVpo4A3KdJypNZbZ2KTWURvcOW0690eviA@mail.gmail.com","subject":"Re: [RFC] introducing git replay","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2022-04-21T02:33:44Z","receivedAt":"2022-04-21T02:33:59Z","isPatch":false,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Wed, Apr 20, 2022 at 4:27 AM Tao Klerks <tao@klerks.biz> wrote:\n>\n> On Mon, Apr 18, 2022 at 6:28 PM Elijah Newren <newren@gmail.com> wrote:\n> >\n> > If you read the suggestion I made (which I'll reinclude here at [1]),\n> > you'll note that I read the old thread you link to with both your and\n> > Phillips' suggestions.  I dug into them with some examples, and came\n> > to the conclusion that we needed something better, as I briefly\n> > commented when proposing my suggested alternative (at [1]).  I\n> > appreciate your suggestion and the time you put into it, but based on\n> > my earlier investigation, I believe my suggestion would be a better\n> > way of preserving user changes in merges and I'll be implementing it.\n> > The fact that Martin (in this thread) independently came up with the\n> > same basic idea and implemented it in jj (though he apparently has\n> > some further tweaks around the object model) and it works well\n> > suggests to me that the idea has some real world testing too that\n> > gives me further confidence in the idea.\n> >\n> > [1] https://lore.kernel.org/git/CABPp-BGW39_5r8Lbt3ymR+F_=hWJcf=2e7O75vFNJ=3CEL5s=g@mail.gmail.com/\n>\n> Thank you for the clarification, and sorry I'm clearly missing\n> something here - the link you provided is to a deeply threaded\n> conversation about \"[PATCH 08/12] merge-ort: provide a\n> merge_get_conflicted_files() helper function\", in the context of a\n> server-side merge support patchset... I can't figure out how to relate\n> that conversation to the \"how can safely reusing previous merge\n> outcomes when rebasing a merge work well?\" topic I thought you had\n> introduced here :(\n\nSorry, I entered the wrong link, and assumed it was right when I\ncopied it into the response email.  Whoops.  The link for [1] was\nsupposed to be https://lore.kernel.org/git/CABPp-BHp+d62dCyAaJfh1cZ8xVpGyb97mZryd02aCOX=Qn=Ltw@mail.gmail.com/\n\nBut as has been said elsewhere, what you're asking for doesn't exist\nyet.  That other email was where I outlined a bunch of details about\nwhat someone could do to implement it, and where I pointed out that I\nplanned to eventually implement it if Dscho didn't beat me to it.\nI've since started on it.\n\nI also linked to my tree a few times where anyone can look at what I\nhave done so far (which isn't useful to users yet).  If you want to\ntake what I've implemented and implement the rest before I can, go\nahead.  If you want to take the steps I outlined on how to do it in\nthe email link I provided above, and implement it from scratch then go\nahead.  But otherwise, as Junio already pointed out in this thread, it\njust doesn't exist today.\n"}]}