git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] rebase-merges: try and use branch names for labels
@ 2024-09-21 18:37 Nicolas Guichard via GitGitGadget
  2024-09-21 18:37 ` [PATCH 1/2] sequencer.c: extract load_branch_decorations Nicolas Guichard via GitGitGadget
                   ` (2 more replies)
  0 siblings, 3 replies; 18+ messages in thread
From: Nicolas Guichard via GitGitGadget @ 2024-09-21 18:37 UTC (permalink / raw)
  To: git; +Cc: Nicolas Guichard

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 which 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.

Nicolas Guichard (2):
  sequencer.c: extract load_branch_decorations
  rebase-merges: try and use branch names as labels

 sequencer.c                   | 50 ++++++++++++++++++++++-------------
 t/t3404-rebase-interactive.sh |  4 +--
 t/t3430-rebase-merges.sh      | 12 ++++-----
 3 files changed, 40 insertions(+), 26 deletions(-)


base-commit: 94b60adee30619a05296cf5ed6addb0e6d4e25dc
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1784%2Fnicolas-guichard%2Fuse-branch-names-for-rebase-merges-labels-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1784/nicolas-guichard/use-branch-names-for-rebase-merges-labels-v1
Pull-Request: https://github.com/git/git/pull/1784
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH 1/2] sequencer.c: extract load_branch_decorations
  2024-09-21 18:37 [PATCH 0/2] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
@ 2024-09-21 18:37 ` Nicolas Guichard via GitGitGadget
  2024-09-23 19:32   ` Junio C Hamano
  2024-09-21 18:38 ` [PATCH 2/2] rebase-merges: try and use branch names as labels Nicolas Guichard via GitGitGadget
  2024-10-04 23:22 ` [PATCH v2 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
  2 siblings, 1 reply; 18+ messages in thread
From: Nicolas Guichard via GitGitGadget @ 2024-09-21 18:37 UTC (permalink / raw)
  To: git; +Cc: Nicolas Guichard, Nicolas Guichard

From: Nicolas Guichard <nicolas@guichard.eu>

Extract load_branch_decorations from todo_list_add_update_ref_commands so
it can be re-used in make_script_with_merges.

Signed-off-by: Nicolas Guichard <nicolas@guichard.eu>
---
 sequencer.c | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 8d01cd50ac9..e5eb6f8cd76 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -5810,6 +5810,20 @@ static const char *label_oid(struct object_id *oid, const char *label,
 	return string_entry->string;
 }
 
+static void load_branch_decorations(void)
+{
+	static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
+	static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
+	static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
+	struct decoration_filter decoration_filter = {
+		.include_ref_pattern = &decorate_refs_include,
+		.exclude_ref_pattern = &decorate_refs_exclude,
+		.exclude_ref_config_pattern = &decorate_refs_exclude_config,
+	};
+	string_list_append(&decorate_refs_include, "refs/heads/");
+	load_ref_decorations(&decoration_filter, 0);
+}
+
 static int make_script_with_merges(struct pretty_print_context *pp,
 				   struct rev_info *revs, struct strbuf *out,
 				   unsigned flags)
@@ -6403,14 +6417,6 @@ static int add_decorations_to_list(const struct commit *commit,
 static int todo_list_add_update_ref_commands(struct todo_list *todo_list)
 {
 	int i, res;
-	static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
-	static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
-	static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
-	struct decoration_filter decoration_filter = {
-		.include_ref_pattern = &decorate_refs_include,
-		.exclude_ref_pattern = &decorate_refs_exclude,
-		.exclude_ref_config_pattern = &decorate_refs_exclude_config,
-	};
 	struct todo_add_branch_context ctx = {
 		.buf = &todo_list->buf,
 		.refs_to_oids = STRING_LIST_INIT_DUP,
@@ -6419,8 +6425,7 @@ static int todo_list_add_update_ref_commands(struct todo_list *todo_list)
 	ctx.items_alloc = 2 * todo_list->nr + 1;
 	ALLOC_ARRAY(ctx.items, ctx.items_alloc);
 
-	string_list_append(&decorate_refs_include, "refs/heads/");
-	load_ref_decorations(&decoration_filter, 0);
+	load_branch_decorations();
 
 	for (i = 0; i < todo_list->nr; ) {
 		struct todo_item *item = &todo_list->items[i];
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 2/2] rebase-merges: try and use branch names as labels
  2024-09-21 18:37 [PATCH 0/2] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
  2024-09-21 18:37 ` [PATCH 1/2] sequencer.c: extract load_branch_decorations Nicolas Guichard via GitGitGadget
@ 2024-09-21 18:38 ` Nicolas Guichard via GitGitGadget
  2024-09-23 19:38   ` Junio C Hamano
  2024-10-04 23:22 ` [PATCH v2 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
  2 siblings, 1 reply; 18+ messages in thread
From: Nicolas Guichard via GitGitGadget @ 2024-09-21 18:38 UTC (permalink / raw)
  To: git; +Cc: Nicolas Guichard, Nicolas Guichard

From: Nicolas Guichard <nicolas@guichard.eu>

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>
---
 sequencer.c                   | 25 +++++++++++++++++--------
 t/t3404-rebase-interactive.sh |  4 ++--
 t/t3430-rebase-merges.sh      | 12 ++++++------
 3 files changed, 25 insertions(+), 16 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index e5eb6f8cd76..a092bd05692 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -5833,7 +5833,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 	int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
 	int skipped_commit = 0;
 	struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
-	struct strbuf label = STRBUF_INIT;
+	struct strbuf label_from_message = STRBUF_INIT;
 	struct commit_list *commits = NULL, **tail = &commits, *iter;
 	struct commit_list *tips = NULL, **tips_tail = &tips;
 	struct commit *commit;
@@ -5856,6 +5856,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 	oidmap_init(&state.commit2label, 0);
 	hashmap_init(&state.labels, labels_cmp, NULL, 0);
 	strbuf_init(&state.buf, 32);
+	load_branch_decorations();
 
 	if (revs->cmdline.nr && (revs->cmdline.rev[0].flags & BOTTOM)) {
 		struct labels_entry *onto_label_entry;
@@ -5916,18 +5917,18 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 			continue;
 		}
 
-		/* Create a label */
-		strbuf_reset(&label);
+		/* Create a label from the commit message */
+		strbuf_reset(&label_from_message);
 		if (skip_prefix(oneline.buf, "Merge ", &p1) &&
 		    (p1 = strchr(p1, '\'')) &&
 		    (p2 = strchr(++p1, '\'')))
-			strbuf_add(&label, p1, p2 - p1);
+			strbuf_add(&label_from_message, p1, p2 - p1);
 		else if (skip_prefix(oneline.buf, "Merge pull request ",
 				     &p1) &&
 			 (p1 = strstr(p1, " from ")))
-			strbuf_addstr(&label, p1 + strlen(" from "));
+			strbuf_addstr(&label_from_message, p1 + strlen(" from "));
 		else
-			strbuf_addbuf(&label, &oneline);
+			strbuf_addbuf(&label_from_message, &oneline);
 
 		strbuf_reset(&buf);
 		strbuf_addf(&buf, "%s -C %s",
@@ -5935,6 +5936,14 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 
 		/* label the tips of merged branches */
 		for (; to_merge; to_merge = to_merge->next) {
+			const char *label = label_from_message.buf;
+			const struct name_decoration *decoration =
+				get_name_decoration(&to_merge->item->object);
+
+			if (decoration)
+				skip_prefix(decoration->name, "refs/heads/",
+					    &label);
+
 			oid = &to_merge->item->object.oid;
 			strbuf_addch(&buf, ' ');
 
@@ -5947,7 +5956,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 			tips_tail = &commit_list_insert(to_merge->item,
 							tips_tail)->next;
 
-			strbuf_addstr(&buf, label_oid(oid, label.buf, &state));
+			strbuf_addstr(&buf, label_oid(oid, label, &state));
 		}
 		strbuf_addf(&buf, " # %s", oneline.buf);
 
@@ -6055,7 +6064,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 	free_commit_list(commits);
 	free_commit_list(tips);
 
-	strbuf_release(&label);
+	strbuf_release(&label_from_message);
 	strbuf_release(&oneline);
 	strbuf_release(&buf);
 
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index f171af3061d..4896a801ee2 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1870,7 +1870,7 @@ test_expect_success '--update-refs adds commands with --rebase-merges' '
 		pick $(git log -1 --format=%h branch2~1) F
 		pick $(git log -1 --format=%h branch2) I
 		update-ref refs/heads/branch2
-		label merge
+		label branch2
 		reset onto
 		pick $(git log -1 --format=%h refs/heads/second) J
 		update-ref refs/heads/second
@@ -1881,7 +1881,7 @@ test_expect_success '--update-refs adds commands with --rebase-merges' '
 		update-ref refs/heads/third
 		pick $(git log -1 --format=%h HEAD~2) M
 		update-ref refs/heads/no-conflict-branch
-		merge -C $(git log -1 --format=%h HEAD~1) merge # merge
+		merge -C $(git log -1 --format=%h HEAD~1) branch2 # merge
 		update-ref refs/heads/merge-branch
 		EOF
 
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 2aa8593f77a..cb891eeb5fd 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -108,19 +108,19 @@ test_expect_success 'generate correct todo list' '
 
 	reset onto
 	pick $b B
-	label E
+	label first
 
 	reset onto
 	pick $c C
 	label branch-point
 	pick $f F
 	pick $g G
-	label H
+	label second
 
 	reset branch-point # C
 	pick $d D
-	merge -C $e E # E
-	merge -C $h H # H
+	merge -C $e first # E
+	merge -C $h second # H
 
 	EOF
 
@@ -462,11 +462,11 @@ test_expect_success 'A root commit can be a cousin, treat it that way' '
 '
 
 test_expect_success 'labels that are object IDs are rewritten' '
-	git checkout -b third B &&
+	git checkout --detach B &&
 	test_commit I &&
 	third=$(git rev-parse HEAD) &&
 	git checkout -b labels main &&
-	git merge --no-commit third &&
+	git merge --no-commit $third &&
 	test_tick &&
 	git commit -m "Merge commit '\''$third'\'' into labels" &&
 	echo noop >script-from-scratch &&
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH 1/2] sequencer.c: extract load_branch_decorations
  2024-09-21 18:37 ` [PATCH 1/2] sequencer.c: extract load_branch_decorations Nicolas Guichard via GitGitGadget
@ 2024-09-23 19:32   ` Junio C Hamano
  0 siblings, 0 replies; 18+ messages in thread
From: Junio C Hamano @ 2024-09-23 19:32 UTC (permalink / raw)
  To: Nicolas Guichard via GitGitGadget; +Cc: git, Nicolas Guichard

"Nicolas Guichard via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Nicolas Guichard <nicolas@guichard.eu>
>
> Extract load_branch_decorations from todo_list_add_update_ref_commands so
> it can be re-used in make_script_with_merges.
>
> Signed-off-by: Nicolas Guichard <nicolas@guichard.eu>
> ---
>  sequencer.c | 25 +++++++++++++++----------
>  1 file changed, 15 insertions(+), 10 deletions(-)

This intends no behaviour change, and it indeed does not make any
behaviour change.  Good.

> diff --git a/sequencer.c b/sequencer.c
> index 8d01cd50ac9..e5eb6f8cd76 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -5810,6 +5810,20 @@ static const char *label_oid(struct object_id *oid, const char *label,
>  	return string_entry->string;
>  }
>  
> +static void load_branch_decorations(void)
> +{
> +	static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
> +	static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
> +	static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
> +	struct decoration_filter decoration_filter = {
> +		.include_ref_pattern = &decorate_refs_include,
> +		.exclude_ref_pattern = &decorate_refs_exclude,
> +		.exclude_ref_config_pattern = &decorate_refs_exclude_config,
> +	};
> +	string_list_append(&decorate_refs_include, "refs/heads/");
> +	load_ref_decorations(&decoration_filter, 0);
> +}

The load_ref_decorations() function can be called only once per
process, so it does not matter whatever garbage your second and
later invocations throw at it.

But if this function is ever called N times, you'll add N copies of
"refs/heads/" in the _include list, and because the variable is
static, it is not reported as a leak.

It may have been OK back when this implementation was inside
todo_list_add_update_ref_commands(), which was called by
complete_action() only once before the process exited.

But now you made it a "reusable" function, we should clean up after
ourselves, perhaps by dropping "static" from these three string_list
instances and clearing them after calling load_ref_decorations().

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/2] rebase-merges: try and use branch names as labels
  2024-09-21 18:38 ` [PATCH 2/2] rebase-merges: try and use branch names as labels Nicolas Guichard via GitGitGadget
@ 2024-09-23 19:38   ` Junio C Hamano
  0 siblings, 0 replies; 18+ messages in thread
From: Junio C Hamano @ 2024-09-23 19:38 UTC (permalink / raw)
  To: Nicolas Guichard via GitGitGadget, Johannes Schindelin
  Cc: git, Nicolas Guichard

Running

    git blame -L'/^static int make_script_with_merges/,/^}/' sequencer.c

tells me that the code range this patch touches and the function
whose feature this patch extends was mostly introduced in 1644c73c
(rebase-helper --make-script: introduce a flag to rebase merges,
2018-04-25) by Johannes Schindelin (CC'ed), so let's ask input from
him as an area expert.

Thanks.

"Nicolas Guichard via GitGitGadget" <gitgitgadget@gmail.com> writes:
> From: Nicolas Guichard <nicolas@guichard.eu>
>
> 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>
> ---
>  sequencer.c                   | 25 +++++++++++++++++--------
>  t/t3404-rebase-interactive.sh |  4 ++--
>  t/t3430-rebase-merges.sh      | 12 ++++++------
>  3 files changed, 25 insertions(+), 16 deletions(-)
>
> diff --git a/sequencer.c b/sequencer.c
> index e5eb6f8cd76..a092bd05692 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -5833,7 +5833,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
>  	int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
>  	int skipped_commit = 0;
>  	struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
> -	struct strbuf label = STRBUF_INIT;
> +	struct strbuf label_from_message = STRBUF_INIT;
>  	struct commit_list *commits = NULL, **tail = &commits, *iter;
>  	struct commit_list *tips = NULL, **tips_tail = &tips;
>  	struct commit *commit;
> @@ -5856,6 +5856,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
>  	oidmap_init(&state.commit2label, 0);
>  	hashmap_init(&state.labels, labels_cmp, NULL, 0);
>  	strbuf_init(&state.buf, 32);
> +	load_branch_decorations();
>  
>  	if (revs->cmdline.nr && (revs->cmdline.rev[0].flags & BOTTOM)) {
>  		struct labels_entry *onto_label_entry;
> @@ -5916,18 +5917,18 @@ static int make_script_with_merges(struct pretty_print_context *pp,
>  			continue;
>  		}
>  
> -		/* Create a label */
> -		strbuf_reset(&label);
> +		/* Create a label from the commit message */
> +		strbuf_reset(&label_from_message);
>  		if (skip_prefix(oneline.buf, "Merge ", &p1) &&
>  		    (p1 = strchr(p1, '\'')) &&
>  		    (p2 = strchr(++p1, '\'')))
> -			strbuf_add(&label, p1, p2 - p1);
> +			strbuf_add(&label_from_message, p1, p2 - p1);
>  		else if (skip_prefix(oneline.buf, "Merge pull request ",
>  				     &p1) &&
>  			 (p1 = strstr(p1, " from ")))
> -			strbuf_addstr(&label, p1 + strlen(" from "));
> +			strbuf_addstr(&label_from_message, p1 + strlen(" from "));
>  		else
> -			strbuf_addbuf(&label, &oneline);
> +			strbuf_addbuf(&label_from_message, &oneline);
>  
>  		strbuf_reset(&buf);
>  		strbuf_addf(&buf, "%s -C %s",
> @@ -5935,6 +5936,14 @@ static int make_script_with_merges(struct pretty_print_context *pp,
>  
>  		/* label the tips of merged branches */
>  		for (; to_merge; to_merge = to_merge->next) {
> +			const char *label = label_from_message.buf;
> +			const struct name_decoration *decoration =
> +				get_name_decoration(&to_merge->item->object);
> +
> +			if (decoration)
> +				skip_prefix(decoration->name, "refs/heads/",
> +					    &label);
> +
>  			oid = &to_merge->item->object.oid;
>  			strbuf_addch(&buf, ' ');
>  
> @@ -5947,7 +5956,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
>  			tips_tail = &commit_list_insert(to_merge->item,
>  							tips_tail)->next;
>  
> -			strbuf_addstr(&buf, label_oid(oid, label.buf, &state));
> +			strbuf_addstr(&buf, label_oid(oid, label, &state));
>  		}
>  		strbuf_addf(&buf, " # %s", oneline.buf);
>  
> @@ -6055,7 +6064,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
>  	free_commit_list(commits);
>  	free_commit_list(tips);
>  
> -	strbuf_release(&label);
> +	strbuf_release(&label_from_message);
>  	strbuf_release(&oneline);
>  	strbuf_release(&buf);
>  
> diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
> index f171af3061d..4896a801ee2 100755
> --- a/t/t3404-rebase-interactive.sh
> +++ b/t/t3404-rebase-interactive.sh
> @@ -1870,7 +1870,7 @@ test_expect_success '--update-refs adds commands with --rebase-merges' '
>  		pick $(git log -1 --format=%h branch2~1) F
>  		pick $(git log -1 --format=%h branch2) I
>  		update-ref refs/heads/branch2
> -		label merge
> +		label branch2
>  		reset onto
>  		pick $(git log -1 --format=%h refs/heads/second) J
>  		update-ref refs/heads/second
> @@ -1881,7 +1881,7 @@ test_expect_success '--update-refs adds commands with --rebase-merges' '
>  		update-ref refs/heads/third
>  		pick $(git log -1 --format=%h HEAD~2) M
>  		update-ref refs/heads/no-conflict-branch
> -		merge -C $(git log -1 --format=%h HEAD~1) merge # merge
> +		merge -C $(git log -1 --format=%h HEAD~1) branch2 # merge
>  		update-ref refs/heads/merge-branch
>  		EOF
>  
> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
> index 2aa8593f77a..cb891eeb5fd 100755
> --- a/t/t3430-rebase-merges.sh
> +++ b/t/t3430-rebase-merges.sh
> @@ -108,19 +108,19 @@ test_expect_success 'generate correct todo list' '
>  
>  	reset onto
>  	pick $b B
> -	label E
> +	label first
>  
>  	reset onto
>  	pick $c C
>  	label branch-point
>  	pick $f F
>  	pick $g G
> -	label H
> +	label second
>  
>  	reset branch-point # C
>  	pick $d D
> -	merge -C $e E # E
> -	merge -C $h H # H
> +	merge -C $e first # E
> +	merge -C $h second # H
>  
>  	EOF
>  
> @@ -462,11 +462,11 @@ test_expect_success 'A root commit can be a cousin, treat it that way' '
>  '
>  
>  test_expect_success 'labels that are object IDs are rewritten' '
> -	git checkout -b third B &&
> +	git checkout --detach B &&
>  	test_commit I &&
>  	third=$(git rev-parse HEAD) &&
>  	git checkout -b labels main &&
> -	git merge --no-commit third &&
> +	git merge --no-commit $third &&
>  	test_tick &&
>  	git commit -m "Merge commit '\''$third'\'' into labels" &&
>  	echo noop >script-from-scratch &&

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v2 0/3] rebase-merges: try and use branch names for labels
  2024-09-21 18:37 [PATCH 0/2] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
  2024-09-21 18:37 ` [PATCH 1/2] sequencer.c: extract load_branch_decorations Nicolas Guichard via GitGitGadget
  2024-09-21 18:38 ` [PATCH 2/2] rebase-merges: try and use branch names as labels Nicolas Guichard via GitGitGadget
@ 2024-10-04 23:22 ` Nicolas Guichard via GitGitGadget
  2024-10-04 23:22   ` [PATCH v2 1/3] load_branch_decorations: fix memory leak with non-static filters Nicolas Guichard via GitGitGadget
                     ` (3 more replies)
  2 siblings, 4 replies; 18+ messages in thread
From: Nicolas Guichard via GitGitGadget @ 2024-10-04 23:22 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Nicolas Guichard

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


which 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.

Changes since v1:

 * moved load_branch_decorations to re-use the decoration_loaded guard and
   avoid pointlessly appending "refs/heads/" to a static string_list, as
   pointed out by Junio C Hamano (thanks!)
 * fixed a leak in load_branch_decorations found by making the filter
   string_lists non-static

Nicolas Guichard (3):
  load_branch_decorations: fix memory leak with non-static filters
  rebase-update-refs: extract load_branch_decorations
  rebase-merges: try and use branch names as labels

 log-tree.c                    | 26 +++++++++++++++++++++++++
 log-tree.h                    |  1 +
 sequencer.c                   | 36 +++++++++++++++++------------------
 t/t3404-rebase-interactive.sh |  4 ++--
 t/t3430-rebase-merges.sh      | 12 ++++++------
 5 files changed, 53 insertions(+), 26 deletions(-)


base-commit: 8895aca99693e22ec4856744d5b2c25b8736a111
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1784%2Fnicolas-guichard%2Fuse-branch-names-for-rebase-merges-labels-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1784/nicolas-guichard/use-branch-names-for-rebase-merges-labels-v2
Pull-Request: https://github.com/git/git/pull/1784

Range-diff vs v1:

 -:  ----------- > 1:  6250a7f6d6c load_branch_decorations: fix memory leak with non-static filters
 1:  7f3d5e5da35 ! 2:  167418d10d1 sequencer.c: extract load_branch_decorations
     @@ Metadata
      Author: Nicolas Guichard <nicolas@guichard.eu>
      
       ## Commit message ##
     -    sequencer.c: extract load_branch_decorations
     +    rebase-update-refs: extract load_branch_decorations
      
          Extract load_branch_decorations from todo_list_add_update_ref_commands so
          it can be re-used in make_script_with_merges.
      
     +    Since it can now be called multiple times, use non-static lists and place
     +    it next to load_ref_decorations to re-use the decoration_loaded guard.
     +
          Signed-off-by: Nicolas Guichard <nicolas@guichard.eu>
      
     - ## sequencer.c ##
     -@@ sequencer.c: static const char *label_oid(struct object_id *oid, const char *label,
     - 	return string_entry->string;
     + ## log-tree.c ##
     +@@ log-tree.c: void load_ref_decorations(struct decoration_filter *filter, int flags)
     + 	}
       }
       
     -+static void load_branch_decorations(void)
     ++void load_branch_decorations(void)
      +{
     -+	static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
     -+	static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
     -+	static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
     -+	struct decoration_filter decoration_filter = {
     -+		.include_ref_pattern = &decorate_refs_include,
     -+		.exclude_ref_pattern = &decorate_refs_exclude,
     -+		.exclude_ref_config_pattern = &decorate_refs_exclude_config,
     -+	};
     -+	string_list_append(&decorate_refs_include, "refs/heads/");
     -+	load_ref_decorations(&decoration_filter, 0);
     ++	if (!decoration_loaded) {
     ++		struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
     ++		struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
     ++		struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
     ++		struct decoration_filter decoration_filter = {
     ++			.include_ref_pattern = &decorate_refs_include,
     ++			.exclude_ref_pattern = &decorate_refs_exclude,
     ++			.exclude_ref_config_pattern = &decorate_refs_exclude_config,
     ++		};
     ++
     ++		string_list_append(&decorate_refs_include, "refs/heads/");
     ++		load_ref_decorations(&decoration_filter, 0);
     ++
     ++		string_list_clear(&decorate_refs_exclude, false);
     ++		string_list_clear(&decorate_refs_exclude_config, false);
     ++		string_list_clear(&decorate_refs_include, false);
     ++	}
      +}
      +
     - static int make_script_with_merges(struct pretty_print_context *pp,
     - 				   struct rev_info *revs, struct strbuf *out,
     - 				   unsigned flags)
     + static void show_parents(struct commit *commit, int abbrev, FILE *file)
     + {
     + 	struct commit_list *p;
     +
     + ## log-tree.h ##
     +@@ log-tree.h: void log_write_email_headers(struct rev_info *opt, struct commit *commit,
     + 			     int *need_8bit_cte_p,
     + 			     int maybe_multipart);
     + void load_ref_decorations(struct decoration_filter *filter, int flags);
     ++void load_branch_decorations(void);
     + 
     + void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *);
     + void fmt_output_subject(struct strbuf *, const char *subject, struct rev_info *);
     +
     + ## sequencer.c ##
      @@ sequencer.c: static int add_decorations_to_list(const struct commit *commit,
       static int todo_list_add_update_ref_commands(struct todo_list *todo_list)
       {
 2:  9afe428927d = 3:  dfa1f0a7648 rebase-merges: try and use branch names as labels

-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v2 1/3] load_branch_decorations: fix memory leak with non-static filters
  2024-10-04 23:22 ` [PATCH v2 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
@ 2024-10-04 23:22   ` Nicolas Guichard via GitGitGadget
  2024-10-05  3:43     ` Eric Sunshine
  2024-10-04 23:22   ` [PATCH v2 2/3] rebase-update-refs: extract load_branch_decorations Nicolas Guichard via GitGitGadget
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 18+ messages in thread
From: Nicolas Guichard via GitGitGadget @ 2024-10-04 23:22 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Nicolas Guichard,
	Nicolas Guichard

From: Nicolas Guichard <nicolas@guichard.eu>

load_branch_decorations calls normalize_glob_ref on each string of filter's
string_lists. This effectively replaces the potentially non-owning char* of
those items with an owning char*.

Set the strdup_string flag on those string_lists.

This was not caught until now because:
- when passing string_lists already with the strdup_string already set, the
  behaviour was correct
- when passing static string_lists, the new char* remain reachable until
  program exit

Signed-off-by: Nicolas Guichard <nicolas@guichard.eu>
---
 log-tree.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/log-tree.c b/log-tree.c
index 3758e0d3b8e..cd57de2424e 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -232,6 +232,11 @@ void load_ref_decorations(struct decoration_filter *filter, int flags)
 			for_each_string_list_item(item, filter->exclude_ref_config_pattern) {
 				normalize_glob_ref(item, NULL, item->string);
 			}
+
+			// normalize_glob_ref duplicates the strings
+			filter->exclude_ref_pattern->strdup_strings = true;
+			filter->include_ref_pattern->strdup_strings = true;
+			filter->exclude_ref_config_pattern->strdup_strings = true;
 		}
 		decoration_loaded = 1;
 		decoration_flags = flags;
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 2/3] rebase-update-refs: extract load_branch_decorations
  2024-10-04 23:22 ` [PATCH v2 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
  2024-10-04 23:22   ` [PATCH v2 1/3] load_branch_decorations: fix memory leak with non-static filters Nicolas Guichard via GitGitGadget
@ 2024-10-04 23:22   ` Nicolas Guichard via GitGitGadget
  2024-10-05  3:44     ` Eric Sunshine
  2024-10-04 23:22   ` [PATCH v2 3/3] rebase-merges: try and use branch names as labels Nicolas Guichard via GitGitGadget
  2024-10-09  7:58   ` [PATCH v3 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
  3 siblings, 1 reply; 18+ messages in thread
From: Nicolas Guichard via GitGitGadget @ 2024-10-04 23:22 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Nicolas Guichard,
	Nicolas Guichard

From: Nicolas Guichard <nicolas@guichard.eu>

Extract load_branch_decorations from todo_list_add_update_ref_commands so
it can be re-used in make_script_with_merges.

Since it can now be called multiple times, use non-static lists and place
it next to load_ref_decorations to re-use the decoration_loaded guard.

Signed-off-by: Nicolas Guichard <nicolas@guichard.eu>
---
 log-tree.c  | 21 +++++++++++++++++++++
 log-tree.h  |  1 +
 sequencer.c | 11 +----------
 3 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/log-tree.c b/log-tree.c
index cd57de2424e..5c17aff2265 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -248,6 +248,27 @@ void load_ref_decorations(struct decoration_filter *filter, int flags)
 	}
 }
 
+void load_branch_decorations(void)
+{
+	if (!decoration_loaded) {
+		struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
+		struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
+		struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
+		struct decoration_filter decoration_filter = {
+			.include_ref_pattern = &decorate_refs_include,
+			.exclude_ref_pattern = &decorate_refs_exclude,
+			.exclude_ref_config_pattern = &decorate_refs_exclude_config,
+		};
+
+		string_list_append(&decorate_refs_include, "refs/heads/");
+		load_ref_decorations(&decoration_filter, 0);
+
+		string_list_clear(&decorate_refs_exclude, false);
+		string_list_clear(&decorate_refs_exclude_config, false);
+		string_list_clear(&decorate_refs_include, false);
+	}
+}
+
 static void show_parents(struct commit *commit, int abbrev, FILE *file)
 {
 	struct commit_list *p;
diff --git a/log-tree.h b/log-tree.h
index 94978e2c838..ebe491c543c 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -33,6 +33,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
 			     int *need_8bit_cte_p,
 			     int maybe_multipart);
 void load_ref_decorations(struct decoration_filter *filter, int flags);
+void load_branch_decorations(void);
 
 void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *);
 void fmt_output_subject(struct strbuf *, const char *subject, struct rev_info *);
diff --git a/sequencer.c b/sequencer.c
index 8d01cd50ac9..97959036b50 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -6403,14 +6403,6 @@ static int add_decorations_to_list(const struct commit *commit,
 static int todo_list_add_update_ref_commands(struct todo_list *todo_list)
 {
 	int i, res;
-	static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
-	static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
-	static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
-	struct decoration_filter decoration_filter = {
-		.include_ref_pattern = &decorate_refs_include,
-		.exclude_ref_pattern = &decorate_refs_exclude,
-		.exclude_ref_config_pattern = &decorate_refs_exclude_config,
-	};
 	struct todo_add_branch_context ctx = {
 		.buf = &todo_list->buf,
 		.refs_to_oids = STRING_LIST_INIT_DUP,
@@ -6419,8 +6411,7 @@ static int todo_list_add_update_ref_commands(struct todo_list *todo_list)
 	ctx.items_alloc = 2 * todo_list->nr + 1;
 	ALLOC_ARRAY(ctx.items, ctx.items_alloc);
 
-	string_list_append(&decorate_refs_include, "refs/heads/");
-	load_ref_decorations(&decoration_filter, 0);
+	load_branch_decorations();
 
 	for (i = 0; i < todo_list->nr; ) {
 		struct todo_item *item = &todo_list->items[i];
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 3/3] rebase-merges: try and use branch names as labels
  2024-10-04 23:22 ` [PATCH v2 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
  2024-10-04 23:22   ` [PATCH v2 1/3] load_branch_decorations: fix memory leak with non-static filters Nicolas Guichard via GitGitGadget
  2024-10-04 23:22   ` [PATCH v2 2/3] rebase-update-refs: extract load_branch_decorations Nicolas Guichard via GitGitGadget
@ 2024-10-04 23:22   ` Nicolas Guichard via GitGitGadget
  2024-10-09  7:58   ` [PATCH v3 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
  3 siblings, 0 replies; 18+ messages in thread
From: Nicolas Guichard via GitGitGadget @ 2024-10-04 23:22 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Nicolas Guichard,
	Nicolas Guichard

From: Nicolas Guichard <nicolas@guichard.eu>

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>
---
 sequencer.c                   | 25 +++++++++++++++++--------
 t/t3404-rebase-interactive.sh |  4 ++--
 t/t3430-rebase-merges.sh      | 12 ++++++------
 3 files changed, 25 insertions(+), 16 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 97959036b50..353d804999b 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -5819,7 +5819,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 	int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
 	int skipped_commit = 0;
 	struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
-	struct strbuf label = STRBUF_INIT;
+	struct strbuf label_from_message = STRBUF_INIT;
 	struct commit_list *commits = NULL, **tail = &commits, *iter;
 	struct commit_list *tips = NULL, **tips_tail = &tips;
 	struct commit *commit;
@@ -5842,6 +5842,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 	oidmap_init(&state.commit2label, 0);
 	hashmap_init(&state.labels, labels_cmp, NULL, 0);
 	strbuf_init(&state.buf, 32);
+	load_branch_decorations();
 
 	if (revs->cmdline.nr && (revs->cmdline.rev[0].flags & BOTTOM)) {
 		struct labels_entry *onto_label_entry;
@@ -5902,18 +5903,18 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 			continue;
 		}
 
-		/* Create a label */
-		strbuf_reset(&label);
+		/* Create a label from the commit message */
+		strbuf_reset(&label_from_message);
 		if (skip_prefix(oneline.buf, "Merge ", &p1) &&
 		    (p1 = strchr(p1, '\'')) &&
 		    (p2 = strchr(++p1, '\'')))
-			strbuf_add(&label, p1, p2 - p1);
+			strbuf_add(&label_from_message, p1, p2 - p1);
 		else if (skip_prefix(oneline.buf, "Merge pull request ",
 				     &p1) &&
 			 (p1 = strstr(p1, " from ")))
-			strbuf_addstr(&label, p1 + strlen(" from "));
+			strbuf_addstr(&label_from_message, p1 + strlen(" from "));
 		else
-			strbuf_addbuf(&label, &oneline);
+			strbuf_addbuf(&label_from_message, &oneline);
 
 		strbuf_reset(&buf);
 		strbuf_addf(&buf, "%s -C %s",
@@ -5921,6 +5922,14 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 
 		/* label the tips of merged branches */
 		for (; to_merge; to_merge = to_merge->next) {
+			const char *label = label_from_message.buf;
+			const struct name_decoration *decoration =
+				get_name_decoration(&to_merge->item->object);
+
+			if (decoration)
+				skip_prefix(decoration->name, "refs/heads/",
+					    &label);
+
 			oid = &to_merge->item->object.oid;
 			strbuf_addch(&buf, ' ');
 
@@ -5933,7 +5942,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 			tips_tail = &commit_list_insert(to_merge->item,
 							tips_tail)->next;
 
-			strbuf_addstr(&buf, label_oid(oid, label.buf, &state));
+			strbuf_addstr(&buf, label_oid(oid, label, &state));
 		}
 		strbuf_addf(&buf, " # %s", oneline.buf);
 
@@ -6041,7 +6050,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 	free_commit_list(commits);
 	free_commit_list(tips);
 
-	strbuf_release(&label);
+	strbuf_release(&label_from_message);
 	strbuf_release(&oneline);
 	strbuf_release(&buf);
 
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index f171af3061d..4896a801ee2 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1870,7 +1870,7 @@ test_expect_success '--update-refs adds commands with --rebase-merges' '
 		pick $(git log -1 --format=%h branch2~1) F
 		pick $(git log -1 --format=%h branch2) I
 		update-ref refs/heads/branch2
-		label merge
+		label branch2
 		reset onto
 		pick $(git log -1 --format=%h refs/heads/second) J
 		update-ref refs/heads/second
@@ -1881,7 +1881,7 @@ test_expect_success '--update-refs adds commands with --rebase-merges' '
 		update-ref refs/heads/third
 		pick $(git log -1 --format=%h HEAD~2) M
 		update-ref refs/heads/no-conflict-branch
-		merge -C $(git log -1 --format=%h HEAD~1) merge # merge
+		merge -C $(git log -1 --format=%h HEAD~1) branch2 # merge
 		update-ref refs/heads/merge-branch
 		EOF
 
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 2aa8593f77a..cb891eeb5fd 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -108,19 +108,19 @@ test_expect_success 'generate correct todo list' '
 
 	reset onto
 	pick $b B
-	label E
+	label first
 
 	reset onto
 	pick $c C
 	label branch-point
 	pick $f F
 	pick $g G
-	label H
+	label second
 
 	reset branch-point # C
 	pick $d D
-	merge -C $e E # E
-	merge -C $h H # H
+	merge -C $e first # E
+	merge -C $h second # H
 
 	EOF
 
@@ -462,11 +462,11 @@ test_expect_success 'A root commit can be a cousin, treat it that way' '
 '
 
 test_expect_success 'labels that are object IDs are rewritten' '
-	git checkout -b third B &&
+	git checkout --detach B &&
 	test_commit I &&
 	third=$(git rev-parse HEAD) &&
 	git checkout -b labels main &&
-	git merge --no-commit third &&
+	git merge --no-commit $third &&
 	test_tick &&
 	git commit -m "Merge commit '\''$third'\'' into labels" &&
 	echo noop >script-from-scratch &&
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 1/3] load_branch_decorations: fix memory leak with non-static filters
  2024-10-04 23:22   ` [PATCH v2 1/3] load_branch_decorations: fix memory leak with non-static filters Nicolas Guichard via GitGitGadget
@ 2024-10-05  3:43     ` Eric Sunshine
  0 siblings, 0 replies; 18+ messages in thread
From: Eric Sunshine @ 2024-10-05  3:43 UTC (permalink / raw)
  To: Nicolas Guichard via GitGitGadget
  Cc: git, Junio C Hamano, Johannes Schindelin, Nicolas Guichard

On Fri, Oct 4, 2024 at 7:22 PM Nicolas Guichard via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> load_branch_decorations calls normalize_glob_ref on each string of filter's
> string_lists. This effectively replaces the potentially non-owning char* of
> those items with an owning char*.
>
> Set the strdup_string flag on those string_lists.
>
> This was not caught until now because:
> - when passing string_lists already with the strdup_string already set, the
>   behaviour was correct
> - when passing static string_lists, the new char* remain reachable until
>   program exit
>
> Signed-off-by: Nicolas Guichard <nicolas@guichard.eu>

Not a proper review; just a couple style nits below...

> diff --git a/log-tree.c b/log-tree.c
> @@ -232,6 +232,11 @@ void load_ref_decorations(struct decoration_filter *filter, int flags)
> +                       // normalize_glob_ref duplicates the strings
> +                       filter->exclude_ref_pattern->strdup_strings = true;
> +                       filter->include_ref_pattern->strdup_strings = true;
> +                       filter->exclude_ref_config_pattern->strdup_strings = true;

For the sake of older platforms and older compilers, we're still
avoiding certain modernisms on this project. Instead:

* use /* ... */ style comments instead of // style
* use 1 instead of `true` (and 0 instead of `false`)

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 2/3] rebase-update-refs: extract load_branch_decorations
  2024-10-04 23:22   ` [PATCH v2 2/3] rebase-update-refs: extract load_branch_decorations Nicolas Guichard via GitGitGadget
@ 2024-10-05  3:44     ` Eric Sunshine
  0 siblings, 0 replies; 18+ messages in thread
From: Eric Sunshine @ 2024-10-05  3:44 UTC (permalink / raw)
  To: Nicolas Guichard via GitGitGadget
  Cc: git, Junio C Hamano, Johannes Schindelin, Nicolas Guichard

On Fri, Oct 4, 2024 at 7:22 PM Nicolas Guichard via GitGitGadget
<gitgitgadget@gmail.com> wrote:
> Extract load_branch_decorations from todo_list_add_update_ref_commands so
> it can be re-used in make_script_with_merges.
>
> Since it can now be called multiple times, use non-static lists and place
> it next to load_ref_decorations to re-use the decoration_loaded guard.
>
> Signed-off-by: Nicolas Guichard <nicolas@guichard.eu>
> ---
> diff --git a/log-tree.c b/log-tree.c
> @@ -248,6 +248,27 @@ void load_ref_decorations(struct decoration_filter *filter, int flags)
> +void load_branch_decorations(void)
> +{
> +       if (!decoration_loaded) {
> +               struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
> +               struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
> +               struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
> +               struct decoration_filter decoration_filter = {
> +                       .include_ref_pattern = &decorate_refs_include,
> +                       .exclude_ref_pattern = &decorate_refs_exclude,
> +                       .exclude_ref_config_pattern = &decorate_refs_exclude_config,
> +               };
> +
> +               string_list_append(&decorate_refs_include, "refs/heads/");
> +               load_ref_decorations(&decoration_filter, 0);
> +
> +               string_list_clear(&decorate_refs_exclude, false);
> +               string_list_clear(&decorate_refs_exclude_config, false);
> +               string_list_clear(&decorate_refs_include, false);
> +       }

Same minor style nit as previous patch:

* use 0 instead of `false`

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v3 0/3] rebase-merges: try and use branch names for labels
  2024-10-04 23:22 ` [PATCH v2 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
                     ` (2 preceding siblings ...)
  2024-10-04 23:22   ` [PATCH v2 3/3] rebase-merges: try and use branch names as labels Nicolas Guichard via GitGitGadget
@ 2024-10-09  7:58   ` Nicolas Guichard via GitGitGadget
  2024-10-09  7:58     ` [PATCH v3 1/3] load_branch_decorations: fix memory leak with non-static filters Nicolas Guichard via GitGitGadget
                       ` (3 more replies)
  3 siblings, 4 replies; 18+ messages in thread
From: Nicolas Guichard via GitGitGadget @ 2024-10-09  7:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Johannes Schindelin, Eric Sunshine,
	Nicolas Guichard

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


which 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.

Changes since v1:

 * moved load_branch_decorations to re-use the decoration_loaded guard and
   avoid pointlessly appending "refs/heads/" to a static string_list, as
   pointed out by Junio C Hamano (thanks!)
 * fixed a leak in load_branch_decorations found by making the filter
   string_lists non-static

Changes since v2:

 * style changes (true/false -> 1/0 and // -> /* */)

Nicolas Guichard (3):
  load_branch_decorations: fix memory leak with non-static filters
  rebase-update-refs: extract load_branch_decorations
  rebase-merges: try and use branch names as labels

 log-tree.c                    | 26 +++++++++++++++++++++++++
 log-tree.h                    |  1 +
 sequencer.c                   | 36 +++++++++++++++++------------------
 t/t3404-rebase-interactive.sh |  4 ++--
 t/t3430-rebase-merges.sh      | 12 ++++++------
 5 files changed, 53 insertions(+), 26 deletions(-)


base-commit: 777489f9e09c8d0dd6b12f9d90de6376330577a2
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1784%2Fnicolas-guichard%2Fuse-branch-names-for-rebase-merges-labels-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1784/nicolas-guichard/use-branch-names-for-rebase-merges-labels-v3
Pull-Request: https://github.com/git/git/pull/1784

Range-diff vs v2:

 1:  6250a7f6d6c ! 1:  e030ddd91f3 load_branch_decorations: fix memory leak with non-static filters
     @@ log-tree.c: void load_ref_decorations(struct decoration_filter *filter, int flag
       				normalize_glob_ref(item, NULL, item->string);
       			}
      +
     -+			// normalize_glob_ref duplicates the strings
     -+			filter->exclude_ref_pattern->strdup_strings = true;
     -+			filter->include_ref_pattern->strdup_strings = true;
     -+			filter->exclude_ref_config_pattern->strdup_strings = true;
     ++			/* normalize_glob_ref duplicates the strings */
     ++			filter->exclude_ref_pattern->strdup_strings = 1;
     ++			filter->include_ref_pattern->strdup_strings = 1;
     ++			filter->exclude_ref_config_pattern->strdup_strings = 1;
       		}
       		decoration_loaded = 1;
       		decoration_flags = flags;
 2:  167418d10d1 ! 2:  1dad6096eb7 rebase-update-refs: extract load_branch_decorations
     @@ log-tree.c: void load_ref_decorations(struct decoration_filter *filter, int flag
      +		string_list_append(&decorate_refs_include, "refs/heads/");
      +		load_ref_decorations(&decoration_filter, 0);
      +
     -+		string_list_clear(&decorate_refs_exclude, false);
     -+		string_list_clear(&decorate_refs_exclude_config, false);
     -+		string_list_clear(&decorate_refs_include, false);
     ++		string_list_clear(&decorate_refs_exclude, 0);
     ++		string_list_clear(&decorate_refs_exclude_config, 0);
     ++		string_list_clear(&decorate_refs_include, 0);
      +	}
      +}
      +
 3:  dfa1f0a7648 = 3:  19d8253da07 rebase-merges: try and use branch names as labels

-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v3 1/3] load_branch_decorations: fix memory leak with non-static filters
  2024-10-09  7:58   ` [PATCH v3 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
@ 2024-10-09  7:58     ` Nicolas Guichard via GitGitGadget
  2024-10-09  7:58     ` [PATCH v3 2/3] rebase-update-refs: extract load_branch_decorations Nicolas Guichard via GitGitGadget
                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 18+ messages in thread
From: Nicolas Guichard via GitGitGadget @ 2024-10-09  7:58 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Eric Sunshine,
	Nicolas Guichard, Nicolas Guichard

From: Nicolas Guichard <nicolas@guichard.eu>

load_branch_decorations calls normalize_glob_ref on each string of filter's
string_lists. This effectively replaces the potentially non-owning char* of
those items with an owning char*.

Set the strdup_string flag on those string_lists.

This was not caught until now because:
- when passing string_lists already with the strdup_string already set, the
  behaviour was correct
- when passing static string_lists, the new char* remain reachable until
  program exit

Signed-off-by: Nicolas Guichard <nicolas@guichard.eu>
---
 log-tree.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/log-tree.c b/log-tree.c
index 3758e0d3b8e..c65ebd29202 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -232,6 +232,11 @@ void load_ref_decorations(struct decoration_filter *filter, int flags)
 			for_each_string_list_item(item, filter->exclude_ref_config_pattern) {
 				normalize_glob_ref(item, NULL, item->string);
 			}
+
+			/* normalize_glob_ref duplicates the strings */
+			filter->exclude_ref_pattern->strdup_strings = 1;
+			filter->include_ref_pattern->strdup_strings = 1;
+			filter->exclude_ref_config_pattern->strdup_strings = 1;
 		}
 		decoration_loaded = 1;
 		decoration_flags = flags;
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v3 2/3] rebase-update-refs: extract load_branch_decorations
  2024-10-09  7:58   ` [PATCH v3 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
  2024-10-09  7:58     ` [PATCH v3 1/3] load_branch_decorations: fix memory leak with non-static filters Nicolas Guichard via GitGitGadget
@ 2024-10-09  7:58     ` Nicolas Guichard via GitGitGadget
  2024-10-09  7:58     ` [PATCH v3 3/3] rebase-merges: try and use branch names as labels Nicolas Guichard via GitGitGadget
  2024-10-09 14:22     ` [PATCH v3 0/3] rebase-merges: try and use branch names for labels Phillip Wood
  3 siblings, 0 replies; 18+ messages in thread
From: Nicolas Guichard via GitGitGadget @ 2024-10-09  7:58 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Eric Sunshine,
	Nicolas Guichard, Nicolas Guichard

From: Nicolas Guichard <nicolas@guichard.eu>

Extract load_branch_decorations from todo_list_add_update_ref_commands so
it can be re-used in make_script_with_merges.

Since it can now be called multiple times, use non-static lists and place
it next to load_ref_decorations to re-use the decoration_loaded guard.

Signed-off-by: Nicolas Guichard <nicolas@guichard.eu>
---
 log-tree.c  | 21 +++++++++++++++++++++
 log-tree.h  |  1 +
 sequencer.c | 11 +----------
 3 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/log-tree.c b/log-tree.c
index c65ebd29202..bf6d2ae40c8 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -248,6 +248,27 @@ void load_ref_decorations(struct decoration_filter *filter, int flags)
 	}
 }
 
+void load_branch_decorations(void)
+{
+	if (!decoration_loaded) {
+		struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
+		struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
+		struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
+		struct decoration_filter decoration_filter = {
+			.include_ref_pattern = &decorate_refs_include,
+			.exclude_ref_pattern = &decorate_refs_exclude,
+			.exclude_ref_config_pattern = &decorate_refs_exclude_config,
+		};
+
+		string_list_append(&decorate_refs_include, "refs/heads/");
+		load_ref_decorations(&decoration_filter, 0);
+
+		string_list_clear(&decorate_refs_exclude, 0);
+		string_list_clear(&decorate_refs_exclude_config, 0);
+		string_list_clear(&decorate_refs_include, 0);
+	}
+}
+
 static void show_parents(struct commit *commit, int abbrev, FILE *file)
 {
 	struct commit_list *p;
diff --git a/log-tree.h b/log-tree.h
index 94978e2c838..ebe491c543c 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -33,6 +33,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
 			     int *need_8bit_cte_p,
 			     int maybe_multipart);
 void load_ref_decorations(struct decoration_filter *filter, int flags);
+void load_branch_decorations(void);
 
 void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *);
 void fmt_output_subject(struct strbuf *, const char *subject, struct rev_info *);
diff --git a/sequencer.c b/sequencer.c
index 8d01cd50ac9..97959036b50 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -6403,14 +6403,6 @@ static int add_decorations_to_list(const struct commit *commit,
 static int todo_list_add_update_ref_commands(struct todo_list *todo_list)
 {
 	int i, res;
-	static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
-	static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP;
-	static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
-	struct decoration_filter decoration_filter = {
-		.include_ref_pattern = &decorate_refs_include,
-		.exclude_ref_pattern = &decorate_refs_exclude,
-		.exclude_ref_config_pattern = &decorate_refs_exclude_config,
-	};
 	struct todo_add_branch_context ctx = {
 		.buf = &todo_list->buf,
 		.refs_to_oids = STRING_LIST_INIT_DUP,
@@ -6419,8 +6411,7 @@ static int todo_list_add_update_ref_commands(struct todo_list *todo_list)
 	ctx.items_alloc = 2 * todo_list->nr + 1;
 	ALLOC_ARRAY(ctx.items, ctx.items_alloc);
 
-	string_list_append(&decorate_refs_include, "refs/heads/");
-	load_ref_decorations(&decoration_filter, 0);
+	load_branch_decorations();
 
 	for (i = 0; i < todo_list->nr; ) {
 		struct todo_item *item = &todo_list->items[i];
-- 
gitgitgadget


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v3 3/3] rebase-merges: try and use branch names as labels
  2024-10-09  7:58   ` [PATCH v3 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
  2024-10-09  7:58     ` [PATCH v3 1/3] load_branch_decorations: fix memory leak with non-static filters Nicolas Guichard via GitGitGadget
  2024-10-09  7:58     ` [PATCH v3 2/3] rebase-update-refs: extract load_branch_decorations Nicolas Guichard via GitGitGadget
@ 2024-10-09  7:58     ` Nicolas Guichard via GitGitGadget
  2024-10-09 14:21       ` Phillip Wood
  2024-10-09 14:22     ` [PATCH v3 0/3] rebase-merges: try and use branch names for labels Phillip Wood
  3 siblings, 1 reply; 18+ messages in thread
From: Nicolas Guichard via GitGitGadget @ 2024-10-09  7:58 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Schindelin, Eric Sunshine,
	Nicolas Guichard, Nicolas Guichard

From: Nicolas Guichard <nicolas@guichard.eu>

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>
---
 sequencer.c                   | 25 +++++++++++++++++--------
 t/t3404-rebase-interactive.sh |  4 ++--
 t/t3430-rebase-merges.sh      | 12 ++++++------
 3 files changed, 25 insertions(+), 16 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 97959036b50..353d804999b 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -5819,7 +5819,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 	int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
 	int skipped_commit = 0;
 	struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
-	struct strbuf label = STRBUF_INIT;
+	struct strbuf label_from_message = STRBUF_INIT;
 	struct commit_list *commits = NULL, **tail = &commits, *iter;
 	struct commit_list *tips = NULL, **tips_tail = &tips;
 	struct commit *commit;
@@ -5842,6 +5842,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 	oidmap_init(&state.commit2label, 0);
 	hashmap_init(&state.labels, labels_cmp, NULL, 0);
 	strbuf_init(&state.buf, 32);
+	load_branch_decorations();
 
 	if (revs->cmdline.nr && (revs->cmdline.rev[0].flags & BOTTOM)) {
 		struct labels_entry *onto_label_entry;
@@ -5902,18 +5903,18 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 			continue;
 		}
 
-		/* Create a label */
-		strbuf_reset(&label);
+		/* Create a label from the commit message */
+		strbuf_reset(&label_from_message);
 		if (skip_prefix(oneline.buf, "Merge ", &p1) &&
 		    (p1 = strchr(p1, '\'')) &&
 		    (p2 = strchr(++p1, '\'')))
-			strbuf_add(&label, p1, p2 - p1);
+			strbuf_add(&label_from_message, p1, p2 - p1);
 		else if (skip_prefix(oneline.buf, "Merge pull request ",
 				     &p1) &&
 			 (p1 = strstr(p1, " from ")))
-			strbuf_addstr(&label, p1 + strlen(" from "));
+			strbuf_addstr(&label_from_message, p1 + strlen(" from "));
 		else
-			strbuf_addbuf(&label, &oneline);
+			strbuf_addbuf(&label_from_message, &oneline);
 
 		strbuf_reset(&buf);
 		strbuf_addf(&buf, "%s -C %s",
@@ -5921,6 +5922,14 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 
 		/* label the tips of merged branches */
 		for (; to_merge; to_merge = to_merge->next) {
+			const char *label = label_from_message.buf;
+			const struct name_decoration *decoration =
+				get_name_decoration(&to_merge->item->object);
+
+			if (decoration)
+				skip_prefix(decoration->name, "refs/heads/",
+					    &label);
+
 			oid = &to_merge->item->object.oid;
 			strbuf_addch(&buf, ' ');
 
@@ -5933,7 +5942,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 			tips_tail = &commit_list_insert(to_merge->item,
 							tips_tail)->next;
 
-			strbuf_addstr(&buf, label_oid(oid, label.buf, &state));
+			strbuf_addstr(&buf, label_oid(oid, label, &state));
 		}
 		strbuf_addf(&buf, " # %s", oneline.buf);
 
@@ -6041,7 +6050,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 	free_commit_list(commits);
 	free_commit_list(tips);
 
-	strbuf_release(&label);
+	strbuf_release(&label_from_message);
 	strbuf_release(&oneline);
 	strbuf_release(&buf);
 
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index f171af3061d..4896a801ee2 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1870,7 +1870,7 @@ test_expect_success '--update-refs adds commands with --rebase-merges' '
 		pick $(git log -1 --format=%h branch2~1) F
 		pick $(git log -1 --format=%h branch2) I
 		update-ref refs/heads/branch2
-		label merge
+		label branch2
 		reset onto
 		pick $(git log -1 --format=%h refs/heads/second) J
 		update-ref refs/heads/second
@@ -1881,7 +1881,7 @@ test_expect_success '--update-refs adds commands with --rebase-merges' '
 		update-ref refs/heads/third
 		pick $(git log -1 --format=%h HEAD~2) M
 		update-ref refs/heads/no-conflict-branch
-		merge -C $(git log -1 --format=%h HEAD~1) merge # merge
+		merge -C $(git log -1 --format=%h HEAD~1) branch2 # merge
 		update-ref refs/heads/merge-branch
 		EOF
 
diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
index 2aa8593f77a..cb891eeb5fd 100755
--- a/t/t3430-rebase-merges.sh
+++ b/t/t3430-rebase-merges.sh
@@ -108,19 +108,19 @@ test_expect_success 'generate correct todo list' '
 
 	reset onto
 	pick $b B
-	label E
+	label first
 
 	reset onto
 	pick $c C
 	label branch-point
 	pick $f F
 	pick $g G
-	label H
+	label second
 
 	reset branch-point # C
 	pick $d D
-	merge -C $e E # E
-	merge -C $h H # H
+	merge -C $e first # E
+	merge -C $h second # H
 
 	EOF
 
@@ -462,11 +462,11 @@ test_expect_success 'A root commit can be a cousin, treat it that way' '
 '
 
 test_expect_success 'labels that are object IDs are rewritten' '
-	git checkout -b third B &&
+	git checkout --detach B &&
 	test_commit I &&
 	third=$(git rev-parse HEAD) &&
 	git checkout -b labels main &&
-	git merge --no-commit third &&
+	git merge --no-commit $third &&
 	test_tick &&
 	git commit -m "Merge commit '\''$third'\'' into labels" &&
 	echo noop >script-from-scratch &&
-- 
gitgitgadget

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v3 3/3] rebase-merges: try and use branch names as labels
  2024-10-09  7:58     ` [PATCH v3 3/3] rebase-merges: try and use branch names as labels Nicolas Guichard via GitGitGadget
@ 2024-10-09 14:21       ` Phillip Wood
  2024-10-09 17:57         ` Junio C Hamano
  0 siblings, 1 reply; 18+ messages in thread
From: Phillip Wood @ 2024-10-09 14:21 UTC (permalink / raw)
  To: Nicolas Guichard via GitGitGadget, git
  Cc: Junio C Hamano, Johannes Schindelin, Eric Sunshine,
	Nicolas Guichard

Hi Nicolas

On 09/10/2024 08:58, Nicolas Guichard via GitGitGadget wrote:
> From: Nicolas Guichard <nicolas@guichard.eu>
> 
> 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.

This looks like a nicely described useful improvement, thank you for 
working on it. The way the code is structured means we always calculate 
the fallback label before seeing if there is a branch name we could use 
instead but as calculating the fallback is cheap I don't think that's a 
problem in practice.

Thanks

Phillip

> Signed-off-by: Nicolas Guichard <nicolas@guichard.eu>
> ---
>   sequencer.c                   | 25 +++++++++++++++++--------
>   t/t3404-rebase-interactive.sh |  4 ++--
>   t/t3430-rebase-merges.sh      | 12 ++++++------
>   3 files changed, 25 insertions(+), 16 deletions(-)
> 
> diff --git a/sequencer.c b/sequencer.c
> index 97959036b50..353d804999b 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -5819,7 +5819,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
>   	int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
>   	int skipped_commit = 0;
>   	struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
> -	struct strbuf label = STRBUF_INIT;
> +	struct strbuf label_from_message = STRBUF_INIT;
>   	struct commit_list *commits = NULL, **tail = &commits, *iter;
>   	struct commit_list *tips = NULL, **tips_tail = &tips;
>   	struct commit *commit;
> @@ -5842,6 +5842,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
>   	oidmap_init(&state.commit2label, 0);
>   	hashmap_init(&state.labels, labels_cmp, NULL, 0);
>   	strbuf_init(&state.buf, 32);
> +	load_branch_decorations();
>   
>   	if (revs->cmdline.nr && (revs->cmdline.rev[0].flags & BOTTOM)) {
>   		struct labels_entry *onto_label_entry;
> @@ -5902,18 +5903,18 @@ static int make_script_with_merges(struct pretty_print_context *pp,
>   			continue;
>   		}
>   
> -		/* Create a label */
> -		strbuf_reset(&label);
> +		/* Create a label from the commit message */
> +		strbuf_reset(&label_from_message);
>   		if (skip_prefix(oneline.buf, "Merge ", &p1) &&
>   		    (p1 = strchr(p1, '\'')) &&
>   		    (p2 = strchr(++p1, '\'')))
> -			strbuf_add(&label, p1, p2 - p1);
> +			strbuf_add(&label_from_message, p1, p2 - p1);
>   		else if (skip_prefix(oneline.buf, "Merge pull request ",
>   				     &p1) &&
>   			 (p1 = strstr(p1, " from ")))
> -			strbuf_addstr(&label, p1 + strlen(" from "));
> +			strbuf_addstr(&label_from_message, p1 + strlen(" from "));
>   		else
> -			strbuf_addbuf(&label, &oneline);
> +			strbuf_addbuf(&label_from_message, &oneline);
>   
>   		strbuf_reset(&buf);
>   		strbuf_addf(&buf, "%s -C %s",
> @@ -5921,6 +5922,14 @@ static int make_script_with_merges(struct pretty_print_context *pp,
>   
>   		/* label the tips of merged branches */
>   		for (; to_merge; to_merge = to_merge->next) {
> +			const char *label = label_from_message.buf;
> +			const struct name_decoration *decoration =
> +				get_name_decoration(&to_merge->item->object);
> +
> +			if (decoration)
> +				skip_prefix(decoration->name, "refs/heads/",
> +					    &label);
> +
>   			oid = &to_merge->item->object.oid;
>   			strbuf_addch(&buf, ' ');
>   
> @@ -5933,7 +5942,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
>   			tips_tail = &commit_list_insert(to_merge->item,
>   							tips_tail)->next;
>   
> -			strbuf_addstr(&buf, label_oid(oid, label.buf, &state));
> +			strbuf_addstr(&buf, label_oid(oid, label, &state));
>   		}
>   		strbuf_addf(&buf, " # %s", oneline.buf);
>   
> @@ -6041,7 +6050,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
>   	free_commit_list(commits);
>   	free_commit_list(tips);
>   
> -	strbuf_release(&label);
> +	strbuf_release(&label_from_message);
>   	strbuf_release(&oneline);
>   	strbuf_release(&buf);
>   
> diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
> index f171af3061d..4896a801ee2 100755
> --- a/t/t3404-rebase-interactive.sh
> +++ b/t/t3404-rebase-interactive.sh
> @@ -1870,7 +1870,7 @@ test_expect_success '--update-refs adds commands with --rebase-merges' '
>   		pick $(git log -1 --format=%h branch2~1) F
>   		pick $(git log -1 --format=%h branch2) I
>   		update-ref refs/heads/branch2
> -		label merge
> +		label branch2
>   		reset onto
>   		pick $(git log -1 --format=%h refs/heads/second) J
>   		update-ref refs/heads/second
> @@ -1881,7 +1881,7 @@ test_expect_success '--update-refs adds commands with --rebase-merges' '
>   		update-ref refs/heads/third
>   		pick $(git log -1 --format=%h HEAD~2) M
>   		update-ref refs/heads/no-conflict-branch
> -		merge -C $(git log -1 --format=%h HEAD~1) merge # merge
> +		merge -C $(git log -1 --format=%h HEAD~1) branch2 # merge
>   		update-ref refs/heads/merge-branch
>   		EOF
>   
> diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh
> index 2aa8593f77a..cb891eeb5fd 100755
> --- a/t/t3430-rebase-merges.sh
> +++ b/t/t3430-rebase-merges.sh
> @@ -108,19 +108,19 @@ test_expect_success 'generate correct todo list' '
>   
>   	reset onto
>   	pick $b B
> -	label E
> +	label first
>   
>   	reset onto
>   	pick $c C
>   	label branch-point
>   	pick $f F
>   	pick $g G
> -	label H
> +	label second
>   
>   	reset branch-point # C
>   	pick $d D
> -	merge -C $e E # E
> -	merge -C $h H # H
> +	merge -C $e first # E
> +	merge -C $h second # H
>   
>   	EOF
>   
> @@ -462,11 +462,11 @@ test_expect_success 'A root commit can be a cousin, treat it that way' '
>   '
>   
>   test_expect_success 'labels that are object IDs are rewritten' '
> -	git checkout -b third B &&
> +	git checkout --detach B &&
>   	test_commit I &&
>   	third=$(git rev-parse HEAD) &&
>   	git checkout -b labels main &&
> -	git merge --no-commit third &&
> +	git merge --no-commit $third &&
>   	test_tick &&
>   	git commit -m "Merge commit '\''$third'\'' into labels" &&
>   	echo noop >script-from-scratch &&


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v3 0/3] rebase-merges: try and use branch names for labels
  2024-10-09  7:58   ` [PATCH v3 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
                       ` (2 preceding siblings ...)
  2024-10-09  7:58     ` [PATCH v3 3/3] rebase-merges: try and use branch names as labels Nicolas Guichard via GitGitGadget
@ 2024-10-09 14:22     ` Phillip Wood
  3 siblings, 0 replies; 18+ messages in thread
From: Phillip Wood @ 2024-10-09 14:22 UTC (permalink / raw)
  To: Nicolas Guichard via GitGitGadget, git
  Cc: Junio C Hamano, Johannes Schindelin, Eric Sunshine,
	Nicolas Guichard

Hi Nicolas

Thanks for working on this. This version looks good to me

Thanks

Phillip

On 09/10/2024 08:58, Nicolas Guichard via GitGitGadget wrote:
> 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
> 
> 
> which 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.
> 
> Changes since v1:
> 
>   * moved load_branch_decorations to re-use the decoration_loaded guard and
>     avoid pointlessly appending "refs/heads/" to a static string_list, as
>     pointed out by Junio C Hamano (thanks!)
>   * fixed a leak in load_branch_decorations found by making the filter
>     string_lists non-static
> 
> Changes since v2:
> 
>   * style changes (true/false -> 1/0 and // -> /* */)
> 
> Nicolas Guichard (3):
>    load_branch_decorations: fix memory leak with non-static filters
>    rebase-update-refs: extract load_branch_decorations
>    rebase-merges: try and use branch names as labels
> 
>   log-tree.c                    | 26 +++++++++++++++++++++++++
>   log-tree.h                    |  1 +
>   sequencer.c                   | 36 +++++++++++++++++------------------
>   t/t3404-rebase-interactive.sh |  4 ++--
>   t/t3430-rebase-merges.sh      | 12 ++++++------
>   5 files changed, 53 insertions(+), 26 deletions(-)
> 
> 
> base-commit: 777489f9e09c8d0dd6b12f9d90de6376330577a2
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1784%2Fnicolas-guichard%2Fuse-branch-names-for-rebase-merges-labels-v3
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1784/nicolas-guichard/use-branch-names-for-rebase-merges-labels-v3
> Pull-Request: https://github.com/git/git/pull/1784
> 
> Range-diff vs v2:
> 
>   1:  6250a7f6d6c ! 1:  e030ddd91f3 load_branch_decorations: fix memory leak with non-static filters
>       @@ log-tree.c: void load_ref_decorations(struct decoration_filter *filter, int flag
>         				normalize_glob_ref(item, NULL, item->string);
>         			}
>        +
>       -+			// normalize_glob_ref duplicates the strings
>       -+			filter->exclude_ref_pattern->strdup_strings = true;
>       -+			filter->include_ref_pattern->strdup_strings = true;
>       -+			filter->exclude_ref_config_pattern->strdup_strings = true;
>       ++			/* normalize_glob_ref duplicates the strings */
>       ++			filter->exclude_ref_pattern->strdup_strings = 1;
>       ++			filter->include_ref_pattern->strdup_strings = 1;
>       ++			filter->exclude_ref_config_pattern->strdup_strings = 1;
>         		}
>         		decoration_loaded = 1;
>         		decoration_flags = flags;
>   2:  167418d10d1 ! 2:  1dad6096eb7 rebase-update-refs: extract load_branch_decorations
>       @@ log-tree.c: void load_ref_decorations(struct decoration_filter *filter, int flag
>        +		string_list_append(&decorate_refs_include, "refs/heads/");
>        +		load_ref_decorations(&decoration_filter, 0);
>        +
>       -+		string_list_clear(&decorate_refs_exclude, false);
>       -+		string_list_clear(&decorate_refs_exclude_config, false);
>       -+		string_list_clear(&decorate_refs_include, false);
>       ++		string_list_clear(&decorate_refs_exclude, 0);
>       ++		string_list_clear(&decorate_refs_exclude_config, 0);
>       ++		string_list_clear(&decorate_refs_include, 0);
>        +	}
>        +}
>        +
>   3:  dfa1f0a7648 = 3:  19d8253da07 rebase-merges: try and use branch names as labels
> 


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v3 3/3] rebase-merges: try and use branch names as labels
  2024-10-09 14:21       ` Phillip Wood
@ 2024-10-09 17:57         ` Junio C Hamano
  0 siblings, 0 replies; 18+ messages in thread
From: Junio C Hamano @ 2024-10-09 17:57 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Nicolas Guichard via GitGitGadget, git, Johannes Schindelin,
	Eric Sunshine, Nicolas Guichard

Phillip Wood <phillip.wood123@gmail.com> writes:

> Hi Nicolas
>
> On 09/10/2024 08:58, Nicolas Guichard via GitGitGadget wrote:
>> From: Nicolas Guichard <nicolas@guichard.eu>
>> 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.
>
> This looks like a nicely described useful improvement, thank you for
> working on it. The way the code is structured means we always
> calculate the fallback label before seeing if there is a branch name
> we could use instead but as calculating the fallback is cheap I don't
> think that's a problem in practice.

Thanks, both of you.

Will queue.

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2024-10-09 17:57 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-21 18:37 [PATCH 0/2] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
2024-09-21 18:37 ` [PATCH 1/2] sequencer.c: extract load_branch_decorations Nicolas Guichard via GitGitGadget
2024-09-23 19:32   ` Junio C Hamano
2024-09-21 18:38 ` [PATCH 2/2] rebase-merges: try and use branch names as labels Nicolas Guichard via GitGitGadget
2024-09-23 19:38   ` Junio C Hamano
2024-10-04 23:22 ` [PATCH v2 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
2024-10-04 23:22   ` [PATCH v2 1/3] load_branch_decorations: fix memory leak with non-static filters Nicolas Guichard via GitGitGadget
2024-10-05  3:43     ` Eric Sunshine
2024-10-04 23:22   ` [PATCH v2 2/3] rebase-update-refs: extract load_branch_decorations Nicolas Guichard via GitGitGadget
2024-10-05  3:44     ` Eric Sunshine
2024-10-04 23:22   ` [PATCH v2 3/3] rebase-merges: try and use branch names as labels Nicolas Guichard via GitGitGadget
2024-10-09  7:58   ` [PATCH v3 0/3] rebase-merges: try and use branch names for labels Nicolas Guichard via GitGitGadget
2024-10-09  7:58     ` [PATCH v3 1/3] load_branch_decorations: fix memory leak with non-static filters Nicolas Guichard via GitGitGadget
2024-10-09  7:58     ` [PATCH v3 2/3] rebase-update-refs: extract load_branch_decorations Nicolas Guichard via GitGitGadget
2024-10-09  7:58     ` [PATCH v3 3/3] rebase-merges: try and use branch names as labels Nicolas Guichard via GitGitGadget
2024-10-09 14:21       ` Phillip Wood
2024-10-09 17:57         ` Junio C Hamano
2024-10-09 14:22     ` [PATCH v3 0/3] rebase-merges: try and use branch names for labels Phillip Wood

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 NNTP newsgroup(s).