From 092bd1532c2cea6c257cb65981d2aabf6b62a191 Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Wed, 4 Jun 2025 18:25:13 +0530 Subject: [PATCH 01/15] send-email: fix bug resulting in broken threads if a message is edited Whenever we send a thread of emails using send-email, a message number is internally assigned to each email. This number is used to track the order of the emails in the thread. Whenever a new message is processed in a thread, the current script logic increments the message number by one, which is intended. But, if a message is edited and then resent, its message number again gets incremented. This is because the script uses the same logic to process the edited message, which it uses to send the next message. This minor bug is usually harmless, unless a special situations arises. That situation is when the first message in a thread is edited and resent, and an `--in-reply-to` argument is also passed to send-email. In this case, if the user has chosen shallow threading, the threading does not work as expected, and all messages become replies to the Message-ID specified in the `--in-reply-to` argument. The reason for this bug is hidden in the code for threading itself. if ($thread) { if ($message_was_sent && ($chain_reply_to || !defined $in_reply_to || length($in_reply_to) == 0 || $message_num == 1)) { $in_reply_to = $message_id; if (length $references > 0) { $references .= "\n $message_id"; } else { $references = "$message_id"; } } } Here `$message_num` is the current message number, and `$in_reply_to` is the Message-ID of the message to which the current message is a reply. In case `--in-reply-to` is specified, the `$in_reply_to` variable is set to the value of the `--in-reply-to` argument. Whenever this whole set of conditions is true, the script sets the `$in_reply_to` variable to the current message's ID. This is done to ensure that the next message in the thread is a reply to this message. In case we specify an `--in-reply-to` argument, and have shallow threading, the only condition that can make this true is `$message_num == 1`, which is true for the first message in a thread. Thus, the `$in_reply_to` variable gets set to the first message's ID. For subsequent messages, the `$message_num` variable is always greater than 1, and the whole set of conditions is false. Therefore, the `$in_reply_to` variable remains as the first message's ID. This is what we expect in shallow threading. But if the user edits the first message and resends it, the `$message_num` variable gets incremented by 1, and thus the condition `$message_num == 1` becomes false. This means that the `$in_reply_to` variable is not set to the first message's ID. As a result the next message in the thread is not a reply to the first message, but to the `--in-reply-to` argument, effectively breaking the threading. In case the user does not specify an `--in-reply-to` argument, the `!defined $in_reply_to` condition is true, and thus the `$in_reply_to` variable is set to the first message's ID, and the threading works as expected, regardless of the message number. To fix this bug, we need to ensure that the `$message_num` variable is not incremented by 1 when a message is edited and resent. We do this by decreasing the `$message_num` variable by 1 whenever the request to edit a message is received. This way, the next message in the thread will have the same message number as the edited message. Therefore the threading will work as expected. The same logic has also been applied in case the user drops a single message from the thread by choosing the "[n]o" option during confirmation. By doing this, the next message in the thread is assigned the message number of the dropped message, and thus the threading works as expected. Signed-off-by: Aditya Garg Signed-off-by: Junio C Hamano --- git-send-email.perl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/git-send-email.perl b/git-send-email.perl index 659e6c588bcf87..5bf4307442b57c 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1653,8 +1653,18 @@ sub send_message { default => $ask_default); die __("Send this email reply required") unless defined $_; if (/^n/i) { + # If we are skipping a message, we should make sure that + # the next message is treated as the successor to the + # previously sent message, and not the skipped message. + $message_num--; return 0; } elsif (/^e/i) { + # Since the same message will be sent again, we need to + # decrement the message number to the previous message. + # Otherwise, the edited message will be treated as a + # different message sent after the original non-edited + # message. + $message_num--; return -1; } elsif (/^q/i) { cleanup_compose_files(); From 2cc27b3501064231c5ae190cf2158786e4bfc87b Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Wed, 4 Jun 2025 18:25:14 +0530 Subject: [PATCH 02/15] send-email: show the new message id assigned by outlook in the logs Whenever an email is sent, send-email shows a log at last, which contains all the headers of the email that were received by the receipients. In case outlook changes the Message-ID, a log for the same is shown to the user, but that change is not reflected when the log containing all the headers is displayed. Here is an example of the log that is shown when outlook changes the Message-ID: Outlook reassigned Message-ID to: OK. Log says: Server: smtp.office365.com MAIL FROM: RCPT TO: From: Aditya Garg To: negahe7142@nomrista.com Subject: [PATCH] send-email: show the new message id assigned by outlook in the logs Date: Mon, 26 May 2025 20:28:36 +0530 Message-ID: <20250526145836.4825-1-gargaditya08@live.com> X-Mailer: git-send-email @GIT_VERSION@ MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Result: 250 Fix this by updating the $header variable, which has the message ID we internally assigned on the "Message-ID:" header, with the message ID the Outlook server assigned. It should look like this after this patch: OK. Log says: Server: smtp.office365.com MAIL FROM: RCPT TO: From: Aditya Garg To: negahe7142@nomrista.com Subject: [PATCH] send-email: show the new message id assigned by outlook in the logs Date: Mon, 26 May 2025 20:29:22 +0530 Message-ID: X-Mailer: git-send-email @GIT_VERSION@ MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Result: 250 Signed-off-by: Aditya Garg Signed-off-by: Junio C Hamano --- git-send-email.perl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-send-email.perl b/git-send-email.perl index 5bf4307442b57c..cb6dca2500c93f 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1788,7 +1788,8 @@ sub send_message { if (is_outlook($smtp_server)) { if ($smtp->message =~ /<([^>]+)>/) { $message_id = "<$1>"; - printf __("Outlook reassigned Message-ID to: %s\n"), $message_id; + $header =~ s/^(Message-ID:\s*).*\n/${1}$message_id\n/m; + printf __("Outlook reassigned Message-ID to: %s\n"), $message_id if $smtp->debug; } else { warn __("Warning: Could not retrieve Message-ID from server response.\n"); } From 7cd080acf65a96032824f3b5faad506346b69f5c Mon Sep 17 00:00:00 2001 From: Patrik Weiskircher Date: Wed, 4 Jun 2025 14:16:36 +0000 Subject: [PATCH 03/15] contrib/subtree: parse using --stuck-long Optional parameter handling only works unambiguous with git rev-parse --parseopt when using the --stuck-long option. To prepare for future commits which add flags with optional parameters, parse with --stuck-long. Signed-off-by: Patrik Weiskircher Signed-off-by: Junio C Hamano --- contrib/subtree/git-subtree.sh | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index 15ae86db1b277a..60b2431b8bbaef 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -115,7 +115,7 @@ main () { then set -- -h fi - set_args="$(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)" + set_args="$(echo "$OPTS_SPEC" | git rev-parse --parseopt --stuck-long -- "$@" || echo exit $?)" eval "$set_args" . git-sh-setup require_work_tree @@ -131,9 +131,6 @@ main () { opt="$1" shift case "$opt" in - --annotate|-b|-P|-m|--onto) - shift - ;; --rejoin) arg_split_rejoin=1 ;; @@ -177,42 +174,37 @@ main () { shift case "$opt" in - -q) + --quiet) arg_quiet=1 ;; - -d) + --debug) arg_debug=1 ;; - --annotate) + --annotate=*) test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" - arg_split_annotate="$1" - shift + arg_split_annotate="${opt#*=}" ;; --no-annotate) test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" arg_split_annotate= ;; - -b) + --branch=*) test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" - arg_split_branch="$1" - shift + arg_split_branch="${opt#*=}" ;; - -P) - arg_prefix="${1%/}" - shift + --prefix=*) + arg_prefix="${opt#*=}" ;; - -m) + --message=*) test -n "$allow_addmerge" || die_incompatible_opt "$opt" "$arg_command" - arg_addmerge_message="$1" - shift + arg_addmerge_message="${opt#*=}" ;; --no-prefix) arg_prefix= ;; - --onto) + --onto=*) test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" - arg_split_onto="$1" - shift + arg_split_onto="${opt#*=}" ;; --no-onto) test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" From fea50ce4118c38d74d05c3595abe7756cabee4e1 Mon Sep 17 00:00:00 2001 From: Patrik Weiskircher Date: Wed, 4 Jun 2025 14:16:37 +0000 Subject: [PATCH 04/15] contrib/subtree: add -S/--gpg-sign Allows optionally signing the commits that git subtree creates. This can be necessary when working in a repository that requires gpg signed commits. Signed-off-by: Patrik Weiskircher Signed-off-by: Junio C Hamano --- contrib/subtree/git-subtree.adoc | 19 +++-- contrib/subtree/git-subtree.sh | 32 ++++---- contrib/subtree/t/t7900-subtree.sh | 113 +++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 19 deletions(-) diff --git a/contrib/subtree/git-subtree.adoc b/contrib/subtree/git-subtree.adoc index 004abf415b8e5d..b2bcbcad0d05fc 100644 --- a/contrib/subtree/git-subtree.adoc +++ b/contrib/subtree/git-subtree.adoc @@ -9,14 +9,14 @@ git-subtree - Merge subtrees together and split repository into subtrees SYNOPSIS -------- [verse] -'git subtree' [] -P add -'git subtree' [] -P add -'git subtree' [] -P merge [] -'git subtree' [] -P split [] +'git subtree' [] -P [-S[]] add +'git subtree' [] -P [-S[]] add +'git subtree' [] -P [-S[]] merge [] +'git subtree' [] -P [-S[]] split [] [verse] -'git subtree' [] -P pull -'git subtree' [] -P push +'git subtree' [] -P [-S[]] pull +'git subtree' [] -P [-S[]] push DESCRIPTION ----------- @@ -149,6 +149,13 @@ OPTIONS FOR ALL COMMANDS want to manipulate. This option is mandatory for all commands. +-S[]:: +--gpg-sign[=]:: +--no-gpg-sign:: + GPG-sign commits. The `keyid` argument is optional and + defaults to the committer identity; `--no-gpg-sign` is useful to + countermand a `--gpg-sign` option given earlier on the command line. + OPTIONS FOR 'add' AND 'merge' (ALSO: 'pull', 'split --rejoin', AND 'push --rejoin') ----------------------------------------------------------------------------------- These options for 'add' and 'merge' may also be given to 'pull' (which diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index 60b2431b8bbaef..3fddba797cb92c 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -26,12 +26,12 @@ then fi OPTS_SPEC="\ -git subtree add --prefix= -git subtree add --prefix= -git subtree merge --prefix= -git subtree split --prefix= [] -git subtree pull --prefix= -git subtree push --prefix= +git subtree add --prefix= [-S[=]] +git subtree add --prefix= [-S[=]] +git subtree merge --prefix= [-S[=]] +git subtree split --prefix= [-S[=]] [] +git subtree pull --prefix= [-S[=]] +git subtree push --prefix= [-S[=]] -- h,help! show the help q,quiet! quiet @@ -46,6 +46,7 @@ rejoin merge the new branch back into HEAD options for 'add' and 'merge' (also: 'pull', 'split --rejoin', and 'push --rejoin') squash merge subtree changes as a single commit m,message!= use the given message as the commit message for the merge commit +S,gpg-sign?key-id GPG-sign commits. The keyid argument is optional and defaults to the committer identity " indent=0 @@ -168,6 +169,7 @@ main () { arg_split_annotate= arg_addmerge_squash= arg_addmerge_message= + arg_gpg_sign= while test $# -gt 0 do opt="$1" @@ -232,6 +234,9 @@ main () { test -n "$allow_addmerge" || die_incompatible_opt "$opt" "$arg_command" arg_addmerge_squash= ;; + --gpg-sign=* | --gpg-sign | --no-gpg-sign) + arg_gpg_sign="$opt" + ;; --) break ;; @@ -264,6 +269,7 @@ main () { debug "quiet: {$arg_quiet}" debug "dir: {$dir}" debug "opts: {$*}" + debug "gpg-sign: {$arg_gpg_sign}" debug "cmd_$arg_command" "$@" @@ -529,7 +535,7 @@ copy_commit () { printf "%s" "$arg_split_annotate" cat ) | - git commit-tree "$2" $3 # reads the rest of stdin + git commit-tree $arg_gpg_sign "$2" $3 # reads the rest of stdin ) || die "fatal: can't copy commit $1" } @@ -675,10 +681,10 @@ new_squash_commit () { if test -n "$old" then squash_msg "$dir" "$oldsub" "$newsub" | - git commit-tree "$tree" -p "$old" || exit $? + git commit-tree $arg_gpg_sign "$tree" -p "$old" || exit $? else squash_msg "$dir" "" "$newsub" | - git commit-tree "$tree" || exit $? + git commit-tree $arg_gpg_sign "$tree" || exit $? fi } @@ -917,11 +923,11 @@ cmd_add_commit () { then rev=$(new_squash_commit "" "" "$rev") || exit $? commit=$(add_squashed_msg "$rev" "$dir" | - git commit-tree "$tree" $headp -p "$rev") || exit $? + git commit-tree $arg_gpg_sign "$tree" $headp -p "$rev") || exit $? else revp=$(peel_committish "$rev") || exit $? commit=$(add_msg "$dir" $headrev "$rev" | - git commit-tree "$tree" $headp -p "$revp") || exit $? + git commit-tree $arg_gpg_sign "$tree" $headp -p "$revp") || exit $? fi git reset "$commit" || exit $? @@ -1072,9 +1078,9 @@ cmd_merge () { if test -n "$arg_addmerge_message" then git merge --no-ff -Xsubtree="$arg_prefix" \ - --message="$arg_addmerge_message" "$rev" + --message="$arg_addmerge_message" $arg_gpg_sign "$rev" else - git merge --no-ff -Xsubtree="$arg_prefix" $rev + git merge --no-ff -Xsubtree="$arg_prefix" $arg_gpg_sign $rev fi } diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh index 3c6103f6d27083..3edbb33af46971 100755 --- a/contrib/subtree/t/t7900-subtree.sh +++ b/contrib/subtree/t/t7900-subtree.sh @@ -11,6 +11,7 @@ and push subcommands of git subtree. TEST_DIRECTORY=$(pwd)/../../../t . "$TEST_DIRECTORY"/test-lib.sh +. "$TEST_DIRECTORY"/lib-gpg.sh # Use our own wrapper around test-lib.sh's test_create_repo, in order # to set log.date=relative. `git subtree` parses the output of `git @@ -1563,4 +1564,116 @@ test_expect_success 'subtree descendant check' ' ) ' +test_expect_success GPG 'add subproj with GPG signing using -S flag' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree add --prefix="sub dir" -S FETCH_HEAD && + git verify-commit HEAD && + test "$(last_commit_subject)" = "Add '\''sub dir/'\'' from commit '\''$(git rev-parse FETCH_HEAD)'\''" + ) +' + +test_expect_success GPG 'add subproj with GPG signing using --gpg-sign flag' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree add --prefix="sub dir" --gpg-sign FETCH_HEAD && + git verify-commit HEAD && + test "$(last_commit_subject)" = "Add '\''sub dir/'\'' from commit '\''$(git rev-parse FETCH_HEAD)'\''" + ) +' + +test_expect_success GPG 'add subproj with GPG signing using specific key ID' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree add --prefix="sub dir" -S"$GIT_COMMITTER_EMAIL" FETCH_HEAD && + git verify-commit HEAD && + test "$(last_commit_subject)" = "Add '\''sub dir/'\'' from commit '\''$(git rev-parse FETCH_HEAD)'\''" + ) +' + +test_expect_success GPG 'merge with GPG signing' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree add --prefix="sub dir" FETCH_HEAD + ) && + test_create_commit "$test_count/sub proj" sub2 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree merge --prefix="sub dir" -S FETCH_HEAD && + git verify-commit HEAD + ) +' + +test_expect_success GPG 'split with GPG signing and --rejoin' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree add --prefix="sub dir" FETCH_HEAD + ) && + test_create_commit "$test_count" "sub dir/main-sub1" && + ( + cd "$test_count" && + git subtree split --prefix="sub dir" --rejoin -S && + git verify-commit HEAD + ) +' + +test_expect_success GPG 'add with --squash and GPG signing' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree add --prefix="sub dir" --squash -S FETCH_HEAD && + git verify-commit HEAD && + # With --squash, the commit subject should reference the squash commit (first parent of merge) + squash_commit=$(git rev-parse HEAD^2) && + test "$(last_commit_subject)" = "Merge commit '\''$squash_commit'\'' as '\''sub dir'\''" + ) +' + +test_expect_success GPG 'pull with GPG signing' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git subtree add --prefix="sub dir" ./"sub proj" HEAD + ) && + test_create_commit "$test_count/sub proj" sub2 && + ( + cd "$test_count" && + git subtree pull --prefix="sub dir" -S ./"sub proj" HEAD && + git verify-commit HEAD + ) +' + test_done From e6659b77dfed6f3778e5c6870927be3da082d4f5 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Sat, 7 Jun 2025 10:45:25 +0100 Subject: [PATCH 05/15] stash: allow "git stash -p " to assume push again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Historically "git stash []" was assumed to mean "git stash save []". Since 1ada5020b38 (stash: use stash_push for no verb form, 2017-02-28) it is assumed to mean "git stash push []". As the push subcommand supports pathspecs, 9e140909f61 (stash: allow pathspecs in the no verb form, 2017-02-28) allowed "git stash -p " to mean "git stash push -p ". This was broken in 8c3713cede7 (stash: eliminate crude option parsing, 2020-02-17) which failed to account for "push" being added to the start of argv in cmd_stash() before it calls push_stash() and kept looking in argv[0] for "-p" after moving the code to push_stash(). Fix this by regression by checking argv[1] instead of argv[0] and add a couple of tests to prevent future regressions. Helped-by: Martin Ă…gren Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano --- builtin/stash.c | 2 +- t/t3903-stash.sh | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/builtin/stash.c b/builtin/stash.c index cfbd92852a6557..bc2c34fa048b9f 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1789,7 +1789,7 @@ static int push_stash(int argc, const char **argv, const char *prefix, int ret; if (argc) { - force_assume = !strcmp(argv[0], "-p"); + force_assume = argc > 1 && !strcmp(argv[1], "-p"); argc = parse_options(argc, argv, prefix, options, push_assumed ? git_stash_usage : git_stash_push_usage, diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 74666ff3e4b2b8..a99a746221e500 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -1177,6 +1177,28 @@ test_expect_success 'stash -- stashes and restores the file' ' test_path_is_file bar ' +test_expect_success 'stash -p stash and restores the file' ' + test_write_lines b c >file && + git commit -m "add a few lines" file && + test_write_lines a b c d >file && + test_write_lines b c d >expect-file && + echo changed-other-file >other-file && + test_write_lines s y n | git stash -p file && + test_cmp expect-file file && + echo changed-other-file >expect && + test_cmp expect other-file && + git checkout HEAD -- file && + git stash pop && + test_cmp expect other-file && + test_write_lines a b c >expect && + test_cmp expect file +' + +test_expect_success 'stash -p is rejected' ' + test_must_fail git stash file -p 2>err && + test_grep "subcommand wasn${SQ}t specified; ${SQ}push${SQ} can${SQ}t be assumed due to unexpected token ${SQ}file${SQ}" err +' + test_expect_success 'stash -- stashes in subdirectory' ' mkdir sub && >foo && From 468817bab2ab5fd9b71a8df49e02b8d5521b1631 Mon Sep 17 00:00:00 2001 From: Phillip Wood Date: Sat, 7 Jun 2025 10:45:26 +0100 Subject: [PATCH 06/15] stash: allow "git stash [] --patch " to assume push The support for assuming "push" when "-p" is given introduced in 9e140909f61 (stash: allow pathspecs in the no verb form, 2017-02-28) is very narrow, neither "git stash -m -p " nor "git stash --patch " imply "push" and die instead. Relax this by passing PARSE_OPT_STOP_AT_NON_OPTION when push is being assumed and then setting "force_assume" if "--patch" was present. This means "git stash -p" still dies so that it does not assume the user meant "push" if they mistype a subcommand name but "git stash -m -p " will now succeed. The test added in the last commit is adjusted to check that push is still assumed when "--patch" comes after other options on the command-line. Signed-off-by: Phillip Wood Signed-off-by: Junio C Hamano --- builtin/stash.c | 10 +++++++--- t/t3903-stash.sh | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/builtin/stash.c b/builtin/stash.c index bc2c34fa048b9f..b12fd6c40f100b 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1789,11 +1789,15 @@ static int push_stash(int argc, const char **argv, const char *prefix, int ret; if (argc) { - force_assume = argc > 1 && !strcmp(argv[1], "-p"); + int flags = PARSE_OPT_KEEP_DASHDASH; + + if (push_assumed) + flags |= PARSE_OPT_STOP_AT_NON_OPTION; + argc = parse_options(argc, argv, prefix, options, push_assumed ? git_stash_usage : - git_stash_push_usage, - PARSE_OPT_KEEP_DASHDASH); + git_stash_push_usage, flags); + force_assume |= patch_mode; } if (argc) { diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index a99a746221e500..2bba3baa10f979 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -1177,13 +1177,13 @@ test_expect_success 'stash -- stashes and restores the file' ' test_path_is_file bar ' -test_expect_success 'stash -p stash and restores the file' ' +test_expect_success 'stash --patch stash and restores the file' ' test_write_lines b c >file && git commit -m "add a few lines" file && test_write_lines a b c d >file && test_write_lines b c d >expect-file && echo changed-other-file >other-file && - test_write_lines s y n | git stash -p file && + test_write_lines s y n | git stash -m "stash bar" --patch file && test_cmp expect-file file && echo changed-other-file >expect && test_cmp expect other-file && From a3d278bb6457fa33461891a8f0a4ccffaf0f5296 Mon Sep 17 00:00:00 2001 From: Lidong Yan <502024330056@smail.nju.edu.cn> Date: Tue, 10 Jun 2025 00:37:59 +0000 Subject: [PATCH 07/15] revision: fix memory leak in prepare_show_merge() In revision.c:prepare_show_merge(), we allocated an array in prune but forget to free it. Since parse_pathspec is not responsible to free prune, we should add `free(prune)` in the end of prepare_show_merge(). Signed-off-by: Lidong Yan <502024330056@smail.nju.edu.cn> Signed-off-by: Junio C Hamano --- revision.c | 1 + t/t7007-show.sh | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/revision.c b/revision.c index c4390f0938cbde..b5db2d764a7571 100644 --- a/revision.c +++ b/revision.c @@ -2068,6 +2068,7 @@ static void prepare_show_merge(struct rev_info *revs) parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_PREFER_FULL | PATHSPEC_LITERAL_PATH, "", prune); revs->limited = 1; + free(prune); } static int dotdot_missing(const char *arg, char *dotdot, diff --git a/t/t7007-show.sh b/t/t7007-show.sh index d6cc69e0f2cbd5..2d322b53d16e18 100755 --- a/t/t7007-show.sh +++ b/t/t7007-show.sh @@ -167,4 +167,28 @@ test_expect_success 'show --graph is forbidden' ' test_must_fail git show --graph HEAD ' +test_expect_success 'show unmerged index' ' + git reset --hard && + + git switch -C base && + echo "base" >conflicting && + git add conflicting && + git commit -m "base" && + + git branch hello && + git branch goodbye && + + git switch hello && + echo "hello" >conflicting && + git commit -am "hello" && + + git switch goodbye && + echo "goodbye" >conflicting && + git commit -am "goodbye" && + + git switch hello && + test_must_fail git merge goodbye && + git show --merge HEAD +' + test_done From b1d47b464e553331c555f122c5e341dfbfb618bd Mon Sep 17 00:00:00 2001 From: Ayush Chandekar Date: Tue, 10 Jun 2025 18:32:20 +0530 Subject: [PATCH 08/15] environment: remove the global variable 'core_preload_index' The global variable 'core_preload_index' is used in a single function named 'preload_index()' in "preload-index.c". Move its declaration inside that function, removing unnecessary global state. This change is part of an ongoing effort to eliminate global variables, improve modularity and help libify the codebase. Mentored-by: Christian Couder Mentored-by: Ghanshyam Thakkar Signed-off-by: Ayush Chandekar Signed-off-by: Junio C Hamano --- config.c | 5 ----- environment.c | 3 --- environment.h | 1 - preload-index.c | 4 ++++ 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/config.c b/config.c index b18b5617fcd05d..eb60c293ab3133 100644 --- a/config.c +++ b/config.c @@ -1595,11 +1595,6 @@ static int git_default_core_config(const char *var, const char *value, return 0; } - if (!strcmp(var, "core.preloadindex")) { - core_preload_index = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "core.createobject")) { if (!value) return config_error_nonbool(var); diff --git a/environment.c b/environment.c index c61d773e7e8ff0..7bf0390a3355d3 100644 --- a/environment.c +++ b/environment.c @@ -113,9 +113,6 @@ const char *comment_line_str = "#"; char *comment_line_str_to_free; int auto_comment_line_char; -/* Parallel index stat data preload? */ -int core_preload_index = 1; - /* This is set by setup_git_directory_gently() and/or git_default_config() */ char *git_work_tree_cfg; diff --git a/environment.h b/environment.h index 3d98461a06f702..9a3d05d414a7ad 100644 --- a/environment.h +++ b/environment.h @@ -155,7 +155,6 @@ extern int pack_compression_level; extern unsigned long pack_size_limit_cfg; extern int max_allowed_tree_depth; -extern int core_preload_index; extern int precomposed_unicode; extern int protect_hfs; extern int protect_ntfs; diff --git a/preload-index.c b/preload-index.c index 40ab2abafb8de5..9fee4cc3aaeebf 100644 --- a/preload-index.c +++ b/preload-index.c @@ -19,6 +19,7 @@ #include "repository.h" #include "symlinks.h" #include "trace2.h" +#include "config.h" /* * Mostly randomly chosen maximum thread counts: we @@ -111,6 +112,9 @@ void preload_index(struct index_state *index, struct thread_data data[MAX_PARALLEL]; struct progress_data pd; int t2_sum_lstat = 0; + int core_preload_index = 1; + + repo_config_get_bool(the_repository, "core.preloadindex", &core_preload_index); if (!HAVE_THREADS || !core_preload_index) return; From 1fde1c5daf399bb2d645261e38a7f4b8b1de04c6 Mon Sep 17 00:00:00 2001 From: Ayush Chandekar Date: Tue, 10 Jun 2025 18:32:21 +0530 Subject: [PATCH 09/15] preload-index: stop depending on 'the_repository' Refactor "preload-index.c" to remove the dependency on the global 'the_repository'. Replace the occurrences of 'the_repository' with 'index->repo' and thus remove the definition '#define USE_THE_REPOSITORY_VARIABLE'. Mentored-by: Christian Couder Mentored-by: Ghanshyam Thakkar Signed-off-by: Ayush Chandekar Signed-off-by: Junio C Hamano --- preload-index.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/preload-index.c b/preload-index.c index 9fee4cc3aaeebf..b222821b448526 100644 --- a/preload-index.c +++ b/preload-index.c @@ -2,7 +2,6 @@ * Copyright (C) 2008 Linus Torvalds */ -#define USE_THE_REPOSITORY_VARIABLE #define DISABLE_SIGN_COMPARE_WARNINGS #include "git-compat-util.h" @@ -114,7 +113,7 @@ void preload_index(struct index_state *index, int t2_sum_lstat = 0; int core_preload_index = 1; - repo_config_get_bool(the_repository, "core.preloadindex", &core_preload_index); + repo_config_get_bool(index->repo, "core.preloadindex", &core_preload_index); if (!HAVE_THREADS || !core_preload_index) return; @@ -136,7 +135,7 @@ void preload_index(struct index_state *index, memset(&pd, 0, sizeof(pd)); if (refresh_flags & REFRESH_PROGRESS && isatty(2)) { - pd.progress = start_delayed_progress(the_repository, + pd.progress = start_delayed_progress(index->repo, _("Refreshing index"), index->cache_nr); pthread_mutex_init(&pd.mutex, NULL); From ffb36c64f2b39833f1ac95b79d39c881ed60de24 Mon Sep 17 00:00:00 2001 From: K Jayatheerth Date: Wed, 11 Jun 2025 07:12:03 +0530 Subject: [PATCH 10/15] stash: fix incorrect branch name in stash message When creating a stash, Git uses the current branch name of the superproject to construct the stash commit message. However, in repositories with submodules, the message may mistakenly display the submodule branch name instead. This is because `refs_resolve_ref_unsafe()` returns a pointer to a static buffer. Subsequent calls to the same function overwrite the buffer, corrupting the originally fetched `branch_name` used for the stash message. Use `xstrdup()` to duplicate the branch name immediately after resolving it, so that later buffer overwrites do not affect the stash message. Signed-off-by: K Jayatheerth Signed-off-by: Junio C Hamano --- builtin/stash.c | 10 ++++++++-- t/t3903-stash.sh | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/builtin/stash.c b/builtin/stash.c index dbaa999cf171a7..9c0dde0b2e36df 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1373,6 +1373,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b const char *head_short_sha1 = NULL; const char *branch_ref = NULL; const char *branch_name = "(no branch)"; + char *branch_name_buf = NULL; struct commit *head_commit = NULL; struct commit_list *parents = NULL; struct strbuf msg = STRBUF_INIT; @@ -1405,8 +1406,12 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b branch_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, &flags); - if (flags & REF_ISSYMREF) - skip_prefix(branch_ref, "refs/heads/", &branch_name); + + if (flags & REF_ISSYMREF) { + if (skip_prefix(branch_ref, "refs/heads/", &branch_name)) + branch_name = branch_name_buf = xstrdup(branch_name); + } + head_short_sha1 = repo_find_unique_abbrev(the_repository, &head_commit->object.oid, DEFAULT_ABBREV); @@ -1496,6 +1501,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b strbuf_release(&msg); strbuf_release(&untracked_files); free_commit_list(parents); + free(branch_name_buf); return ret; } diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 74666ff3e4b2b8..5678223cfb2a4e 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -1592,4 +1592,36 @@ test_expect_success 'stash apply reports a locked index' ' ) ' +test_expect_success 'submodules does not affect the branch recorded in stash message' ' + git init sub_project && + ( + cd sub_project && + echo "Initial content in sub_project" >sub_file.txt && + git add sub_file.txt && + git commit -m "Initial commit in sub_project" + ) && + + git init main_project && + ( + cd main_project && + echo "Initial content in main_project" >main_file.txt && + git add main_file.txt && + git commit -m "Initial commit in main_project" && + + git -c protocol.file.allow=always submodule add ../sub_project sub && + git commit -m "Added submodule sub_project" && + + git checkout -b feature_main && + git -C sub checkout -b feature_sub && + + git checkout -b work_branch && + echo "Important work to be stashed" >work_item.txt && + git add work_item.txt && + git stash push -m "custom stash for work_branch" && + + git stash list >../actual_stash_list.txt && + grep "On work_branch: custom stash for work_branch" ../actual_stash_list.txt + ) +' + test_done From fdbea0870e4abecfa28eb5b196104fcd18e1c6d1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 11 Jun 2025 14:16:58 -0700 Subject: [PATCH 11/15] CodingGuidelines: let BSS do its job We have mentioned this in various reviews, but I didn't see it mentioned in the CodingGuildelines document. Let's add it. Signed-off-by: Junio C Hamano --- Documentation/CodingGuidelines | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index a0e7041c54b497..4d1d52aa3710b5 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -315,6 +315,9 @@ For C programs: encouraged to have a blank line between the end of the declarations and the first statement in the block. + - Do not explicitly initialize global variables to 0 or NULL; + instead, let BSS take care of the zero initialization. + - NULL pointers shall be written as NULL, not as 0. - When declaring pointers, the star sides with the variable From abf94a283fd85e680f8d720241e6059dbb5f23f5 Mon Sep 17 00:00:00 2001 From: Siddharth Asthana Date: Fri, 13 Jun 2025 17:27:17 +0530 Subject: [PATCH 12/15] cat-file: fix mailmap application for different author and committer The git cat-file command with --mailmap option fails to apply mailmap transformations to the committer field when the author and committer identities are different. This occurs due to a missing newline handling in apply_mailmap_to_header() after processing each identity line. When rewrite_ident_line() processes an identity, it stops at the end of the identity data (e.g., "Author Name timestamp"), but doesn't account for the trailing newline. The current code adds the identity length to buf_offset but fails to advance past the newline character. This causes the next iteration to start parsing from the newline instead of the beginning of the next header line, making it impossible to match subsequent headers like "committer". Additionally, rewrite_ident_line() may reallocate the buffer during its operation. Any code using pointers into the old buffer would be using invalid memory after such a reallocation. This bug was introduced in e9c1b0e3 (revision: improve commit_rewrite_person(), 2022-07-19) when the much simpler version of commit_rewrite_person() that worked on one "person header" at a time was rewritten to use the current apply_mailmap_to_header() function. The original implementation processed author and committer separately, but the rewrite introduced this loop-based approach that failed to properly handle the transition between identity lines. Let's fix this by addressing both issues: 1. After processing an identity line, we now check if we're at a newline and advance past it, ensuring the next header line is parsed correctly. 2. We recompute the buffer position after rewrite_ident_line() to handle potential buffer reallocation. This ensures that all identity headers in commit and tag objects are consistently processed regardless of whether the author and committer are the same person. Reported-by: Vasilii Iakliushin Reviewed-by: Christian Couder Signed-off-by: Siddharth Asthana Signed-off-by: Junio C Hamano --- ident.c | 4 ++++ t/t4203-mailmap.sh | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/ident.c b/ident.c index cc7afdbf8197e9..5f14444f21c18a 100644 --- a/ident.c +++ b/ident.c @@ -412,6 +412,10 @@ void apply_mailmap_to_header(struct strbuf *buf, const char **header, found_header = 1; buf_offset += endp - line; buf_offset += rewrite_ident_line(person, endp - person, buf, mailmap); + /* Recompute endp after potential buffer reallocation */ + endp = buf->buf + buf_offset; + if (*endp == '\n') + buf_offset++; break; } diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh index 8a88dd7900ca8a..66a6c1d09ddfa9 100755 --- a/t/t4203-mailmap.sh +++ b/t/t4203-mailmap.sh @@ -1087,4 +1087,37 @@ test_expect_success 'git cat-file --batch-command returns correct size with --us test_cmp expect actual ' +test_expect_success 'git cat-file --mailmap works with different author and committer' ' + test_when_finished "rm .mailmap" && + cat >.mailmap <<-\EOF && + Mailmapped User C O Mitter + EOF + git commit --allow-empty -m "different author/committer" \ + --author="Different Author " && + cat >expect <<-\EOF && + author Different Author + committer Mailmapped User + EOF + git cat-file --mailmap commit HEAD >log && + sed -n -e "/^author /s/>.*/>/p" -e "/^committer /s/>.*/>/p" log >actual && + test_cmp expect actual +' + +test_expect_success 'git cat-file --mailmap maps both author and committer when both need mapping' ' + test_when_finished "rm .mailmap" && + cat >.mailmap <<-\EOF && + Mapped Author + Mapped Committer C O Mitter + EOF + git commit --allow-empty -m "both author and committer mapped" \ + --author="Different Author " && + cat >expect <<-\EOF && + author Mapped Author + committer Mapped Committer + EOF + git cat-file --mailmap commit HEAD >log && + sed -n -e "/^author /s/>.*/>/p" -e "/^committer /s/>.*/>/p" log >actual && + test_cmp expect actual +' + test_done From 2939494284909cdc1410944c9c1e00a4a6eff2e9 Mon Sep 17 00:00:00 2001 From: Lidong Yan Date: Mon, 16 Jun 2025 14:22:33 +0800 Subject: [PATCH 13/15] git.c: remove the_repository dependence in run_builtin() run_builtin() takes a repo parameter, so the use of the_repository is no longer necessary. Removed the usage of the_repository. Signed-off-by: Lidong Yan <502024330056@smail.nju.edu.cn> Signed-off-by: Junio C Hamano --- git.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git.c b/git.c index 77c435952232f6..8525ede550d520 100644 --- a/git.c +++ b/git.c @@ -462,12 +462,12 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct precompose_argv_prefix(argc, argv, NULL); if (use_pager == -1 && run_setup && !(p->option & DELAY_PAGER_CONFIG)) - use_pager = check_pager_config(the_repository, p->cmd); + use_pager = check_pager_config(repo, p->cmd); if (use_pager == -1 && p->option & USE_PAGER) use_pager = 1; if (run_setup && startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */ - trace_repo_setup(the_repository); + trace_repo_setup(repo); commit_pager_choice(); if (!help && p->option & NEED_WORK_TREE) From 855cfc65aeca5b2458a6bc100eb1280a119fdb87 Mon Sep 17 00:00:00 2001 From: Rodrigo Michelassi Date: Mon, 16 Jun 2025 21:29:39 -0300 Subject: [PATCH 14/15] t2400: replace 'test -[efd]' with 'test_path_is_*' 'test_path_is_file', 'test_path_is_dir' and 'test_file_is_missing' are test helpers used in Git's development, that emit useful diagnostic information when they detect a failing condition, while test -[efd] does not. Replace the basic shell commands 'test -f', 'test -d' and 'test -e', with these test helpers. Co-authored-by: Isabella Caselli Signed-off-by: Isabella Caselli Signed-off-by: Rodrigo Michelassi Signed-off-by: Junio C Hamano --- t/t2400-worktree-add.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index 90638fa886a22e..023e1301c8e68e 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh @@ -42,8 +42,8 @@ test_expect_success '"add" using - shorthand' ' test_expect_success '"add" refuses to checkout locked branch' ' test_must_fail git worktree add zere main && - ! test -d zere && - ! test -d .git/worktrees/zere + test_path_is_missing zere && + test_path_is_missing .git/worktrees/zere ' test_expect_success 'checking out paths not complaining about linked checkouts' ' @@ -70,14 +70,14 @@ test_expect_success '"add" worktree' ' test_expect_success '"add" worktree with lock' ' git worktree add --detach --lock here-with-lock main && test_when_finished "git worktree unlock here-with-lock || :" && - test -f .git/worktrees/here-with-lock/locked + test_path_is_file .git/worktrees/here-with-lock/locked ' test_expect_success '"add" worktree with lock and reason' ' lock_reason="why not" && git worktree add --detach --lock --reason "$lock_reason" here-with-lock-reason main && test_when_finished "git worktree unlock here-with-lock-reason || :" && - test -f .git/worktrees/here-with-lock-reason/locked && + test_path_is_file .git/worktrees/here-with-lock-reason/locked && echo "$lock_reason" >expect && test_cmp expect .git/worktrees/here-with-lock-reason/locked ' @@ -412,14 +412,14 @@ test_expect_success '"add --orphan" with empty repository' ' test_expect_success '"add" worktree with orphan branch and lock' ' git worktree add --lock --orphan -b orphanbr orphan-with-lock && test_when_finished "git worktree unlock orphan-with-lock || :" && - test -f .git/worktrees/orphan-with-lock/locked + test_path_is_file .git/worktrees/orphan-with-lock/locked ' test_expect_success '"add" worktree with orphan branch, lock, and reason' ' lock_reason="why not" && git worktree add --detach --lock --reason "$lock_reason" orphan-with-lock-reason main && test_when_finished "git worktree unlock orphan-with-lock-reason || :" && - test -f .git/worktrees/orphan-with-lock-reason/locked && + test_path_is_file .git/worktrees/orphan-with-lock-reason/locked && echo "$lock_reason" >expect && test_cmp expect .git/worktrees/orphan-with-lock-reason/locked ' @@ -474,7 +474,7 @@ test_expect_success 'local clone --shared from linked checkout' ' test_expect_success '"add" worktree with --no-checkout' ' git worktree add --no-checkout -b swamp swamp && - ! test -e swamp/init.t && + test_path_is_missing swamp/init.t && git -C swamp reset --hard && test_cmp init.t swamp/init.t ' @@ -497,7 +497,7 @@ test_expect_success 'put a worktree under rebase' ' test_expect_success 'add a worktree, checking out a rebased branch' ' test_must_fail git worktree add new-rebase under-rebase && - ! test -d new-rebase + test_path_is_missing new-rebase ' test_expect_success 'checking out a rebased branch from another worktree' ' @@ -535,7 +535,7 @@ test_expect_success 'checkout a branch under bisect' ' git worktree list >actual && grep "under-bisect.*detached HEAD" actual && test_must_fail git worktree add new-bisect under-bisect && - ! test -d new-bisect + test_path_is_missing new-bisect ) ' @@ -1165,7 +1165,7 @@ test_expect_success '"add" not tripped up by magic worktree matching"' ' test_expect_success FUNNYNAMES 'sanitize generated worktree name' ' git worktree add --detach ". weird*..?.lock.lock" && - test -d .git/worktrees/---weird-.- + test_path_is_dir .git/worktrees/---weird-.- ' test_expect_success '"add" should not fail because of another bad worktree' ' From f0135a9047ca37d4d117dcf21f7e3e89fad85d00 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 24 Jun 2025 09:47:25 -0700 Subject: [PATCH 15/15] The third batch Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.51.0.adoc | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Documentation/RelNotes/2.51.0.adoc b/Documentation/RelNotes/2.51.0.adoc index 4f2a34b47d44d1..89645da210a5fe 100644 --- a/Documentation/RelNotes/2.51.0.adoc +++ b/Documentation/RelNotes/2.51.0.adoc @@ -18,6 +18,8 @@ UI, Workflows & Features pathspec at the end of the command line, just like normal "git diff". + * "git subtree" (in contrib/) learned to grok GPG signing its commits. + Performance, Internal Implementation, Development Support etc. -------------------------------------------------------------- @@ -32,6 +34,9 @@ Performance, Internal Implementation, Development Support etc. * Meson-based build/test framework now understands TAP output generated by our tests. + * "Do not explicitly initialize to zero" rule has been clarified in + the CodingGuidelines document. + Fixes since v2.50 ----------------- @@ -47,6 +52,23 @@ Fixes since v2.50 corrected. (merge 3717a5775a jw/doc-txt-to-adoc-refs later to maint). + * "git stash -p " improvements. + (merge 468817bab2 pw/stash-p-pathspec-fixes later to maint). + + * "git send-email" incremented its internal message counter when a + message was edited, which made logic that treats the first message + specially misbehave, which has been corrected. + (merge 2cc27b3501 ag/send-email-edit-threading-fix later to maint). + + * "git stash" recorded a wrong branch name when submodules are + present in the current checkout, which has been corrected. + (merge ffb36c64f2 kj/stash-onbranch-submodule-fix later to maint). + + * When asking to apply mailmap to both author and committer field + while showing a commit object, the field that appears later was not + correctly parsed and replaced, which has been corrected. + (merge abf94a283f sa/multi-mailmap-fix later to maint). + * Other code cleanup, docfix, build fix, etc. (merge b257adb571 lo/my-first-ow-doc-update later to maint). (merge 8b34b6a220 ly/sequencer-update-squash-is-fixup-only later to maint). @@ -56,3 +78,7 @@ Fixes since v2.50 (merge bfc9f9cc64 ly/submodule-update-failure-leakfix later to maint). (merge 65dff89c6b ma/doc-diff-cc-headers later to maint). (merge efb61591ee jm/bundle-uri-debug-output-to-fp later to maint). + (merge a3d278bb64 ly/prepare-show-merge-leakfix later to maint). + (merge 1fde1c5daf ac/preload-index-wo-the-repository later to maint). + (merge 855cfc65ae rm/t2400-modernize later to maint). + (merge 2939494284 ly/run-builtin-use-passed-in-repo later to maint).