rebase-merges: try and use branch names as labels

When interactively rebasing merge commits, the commit message is parsed to extract a probably meaningful label name. For instance if the merge commit is “Merge branch 'feature0'”, then the rebase script will have thes lines: ``` label feature0 merge -C $sha feature0 # “Merge branch 'feature0' ``` This heuristic fails in the case of octopus merges or when the merge commit message is actually unrelated to the parent commits. An example that combines both is: ``` *---. 967bfa4 (HEAD -> integration) Integration |\ \ \ | | | * 2135be1 (feature2, feat2) Feature 2 | |_|/ |/| | | | * c88b01a Feature 1 | |/ |/| | * 75f3139 (feat0) Feature 0 |/ * 25c86d0 (main) Initial commit ``` yields the labels Integration, Integration-2 and Integration-3. Fix this by using a branch name for each merge commit's parent that is the tip of at least one branch, and falling back to a label derived from the merge commit message otherwise. In the example above, the labels become feat0, Integration and feature2. Signed-off-by: Nicolas Guichard <nicolas@guichard.eu> Acked-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nicolas Guichard committed Oct 9, 2024 at 07:58 UTC 436892123dd9d442fe4f534bba6f7ead635db06c
3 files changed +25 -16
sequencer.c
+17 -8
@@ -5819,7 +5819,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
5819 int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
5820 int skipped_commit = 0;
5821 struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
5822 - struct strbuf label = STRBUF_INIT;
5822 + struct strbuf label_from_message = STRBUF_INIT;
5823 struct commit_list *commits = NULL, **tail = &commits, *iter;
5824 struct commit_list *tips = NULL, **tips_tail = &tips;
5825 struct commit *commit;
@@ -5842,6 +5842,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
5842 oidmap_init(&state.commit2label, 0);
5843 hashmap_init(&state.labels, labels_cmp, NULL, 0);
5844 strbuf_init(&state.buf, 32);
5845 + load_branch_decorations();
5846
5847 if (revs->cmdline.nr && (revs->cmdline.rev[0].flags & BOTTOM)) {
5848 struct labels_entry *onto_label_entry;
@@ -5902,18 +5903,18 @@ static int make_script_with_merges(struct pretty_print_context *pp,
5903 continue;
5904 }
5905
5905 - /* Create a label */
5906 - strbuf_reset(&label);
5906 + /* Create a label from the commit message */
5907 + strbuf_reset(&label_from_message);
5908 if (skip_prefix(oneline.buf, "Merge ", &p1) &&
5909 (p1 = strchr(p1, '\'')) &&
5910 (p2 = strchr(++p1, '\'')))
5910 - strbuf_add(&label, p1, p2 - p1);
5911 + strbuf_add(&label_from_message, p1, p2 - p1);
5912 else if (skip_prefix(oneline.buf, "Merge pull request ",
5913 &p1) &&
5914 (p1 = strstr(p1, " from ")))
5914 - strbuf_addstr(&label, p1 + strlen(" from "));
5915 + strbuf_addstr(&label_from_message, p1 + strlen(" from "));
5916 else
5916 - strbuf_addbuf(&label, &oneline);
5917 + strbuf_addbuf(&label_from_message, &oneline);
5918
5919 strbuf_reset(&buf);
5920 strbuf_addf(&buf, "%s -C %s",
@@ -5921,6 +5922,14 @@ static int make_script_with_merges(struct pretty_print_context *pp,
5922
5923 /* label the tips of merged branches */
5924 for (; to_merge; to_merge = to_merge->next) {
5925 + const char *label = label_from_message.buf;
5926 + const struct name_decoration *decoration =
5927 + get_name_decoration(&to_merge->item->object);
5928 +
5929 + if (decoration)
5930 + skip_prefix(decoration->name, "refs/heads/",
5931 + &label);
5932 +
5933 oid = &to_merge->item->object.oid;
5934 strbuf_addch(&buf, ' ');
5935
@@ -5933,7 +5942,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
5942 tips_tail = &commit_list_insert(to_merge->item,
5943 tips_tail)->next;
5944
5936 - strbuf_addstr(&buf, label_oid(oid, label.buf, &state));
5945 + strbuf_addstr(&buf, label_oid(oid, label, &state));
5946 }
5947 strbuf_addf(&buf, " # %s", oneline.buf);
5948
@@ -6041,7 +6050,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
6050 free_commit_list(commits);
6051 free_commit_list(tips);
6052
6044 - strbuf_release(&label);
6053 + strbuf_release(&label_from_message);
6054 strbuf_release(&oneline);
6055 strbuf_release(&buf);
6056
t/t3404-rebase-interactive.sh
+2 -2
@@ -1870,7 +1870,7 @@ test_expect_success '--update-refs adds commands with --rebase-merges' '
1870 pick $(git log -1 --format=%h branch2~1) F
1871 pick $(git log -1 --format=%h branch2) I
1872 update-ref refs/heads/branch2
1873 - label merge
1873 + label branch2
1874 reset onto
1875 pick $(git log -1 --format=%h refs/heads/second) J
1876 update-ref refs/heads/second
@@ -1881,7 +1881,7 @@ test_expect_success '--update-refs adds commands with --rebase-merges' '
1881 update-ref refs/heads/third
1882 pick $(git log -1 --format=%h HEAD~2) M
1883 update-ref refs/heads/no-conflict-branch
1884 - merge -C $(git log -1 --format=%h HEAD~1) merge # merge
1884 + merge -C $(git log -1 --format=%h HEAD~1) branch2 # merge
1885 update-ref refs/heads/merge-branch
1886 EOF
1887
t/t3430-rebase-merges.sh
+6 -6
@@ -108,19 +108,19 @@ test_expect_success 'generate correct todo list' '
108
109 reset onto
110 pick $b B
111 - label E
111 + label first
112
113 reset onto
114 pick $c C
115 label branch-point
116 pick $f F
117 pick $g G
118 - label H
118 + label second
119
120 reset branch-point # C
121 pick $d D
122 - merge -C $e E # E
123 - merge -C $h H # H
122 + merge -C $e first # E
123 + merge -C $h second # H
124
125 EOF
126
@@ -462,11 +462,11 @@ test_expect_success 'A root commit can be a cousin, treat it that way' '
462 '
463
464 test_expect_success 'labels that are object IDs are rewritten' '
465 - git checkout -b third B &&
465 + git checkout --detach B &&
466 test_commit I &&
467 third=$(git rev-parse HEAD) &&
468 git checkout -b labels main &&
469 - git merge --no-commit third &&
469 + git merge --no-commit $third &&
470 test_tick &&
471 git commit -m "Merge commit '\''$third'\'' into labels" &&
472 echo noop >script-from-scratch &&