summaryrefslogtreecommitdiff
AgeCommit message (Collapse)AuthorFilesLines
2026-01-16t9700/test.pl: fix path type expectation on cygwinRamsay Jones1-1/+1
Commit 4ec7ac101b ("t9700: accommodate for Windows paths", 2025-12-17) changed the type of the absolute path to the git directory from unix to win32 for both GfW and cygwin. This fixed the test for GfW but causes new failures on cygwin, since the test expectation is that it uses unix paths on cygwin. In order to not break cygwin, disable the new code by removing the "or $^O eq 'cygwin'" sub-expression from the conditional part of the fix. Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-16Merge a handful more topics after -rc0Junio C Hamano1-0/+13
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-16Merge branch 'ml/doc-blame-markup'Junio C Hamano2-95/+97
Doc mark-up update. * ml/doc-blame-markup: doc: git-blame: convert to new doc format doc: blame-options: convert to new doc format
2026-01-16Merge branch 'kh/doc-patch-id'Junio C Hamano2-12/+12
"git patch-id" documentation updates. * kh/doc-patch-id: doc: patch-id: --verbatim locks in --stable doc: patch-id: spell out the git-diff-tree(1) form doc: patch-id: use definite article for the result patch-id: use “patch ID” throughout doc: patch-id: capitalize Git version doc: patch-id: don’t use semicolon between bullet points
2026-01-16Merge branch 'bc/doc-stash-import-export'Junio C Hamano1-11/+28
Update a FAQ entry on synching two separate repositories using the "git stash export/import" recently introduced. * bc/doc-stash-import-export: gitfaq: document using stash import/export to sync working tree
2026-01-16Merge branch 'kj/t7101-modernize'Junio C Hamano1-9/+9
Test update. * kj/t7101-modernize: t7101: modernize test path checks
2026-01-16Merge branch 'ds/builtin-doc-update'Junio C Hamano1-1/+25
Update in-code comment doc to match the current API. * ds/builtin-doc-update: builtin.h: update documentation
2026-01-16Merge branch 'ac/t1420-use-more-direct-check'Junio C Hamano1-3/+6
Test update. * ac/t1420-use-more-direct-check: t1420: modernize the lost-found test
2026-01-16Merge branch 'jk/cat-file-avoid-bitmap-when-unneeded'Junio C Hamano2-3/+19
Fix for a performance regression in "git cat-file". * jk/cat-file-avoid-bitmap-when-unneeded: cat-file: only use bitmaps when filtering
2026-01-16Merge branch 'jk/t-perf-fixes'Junio C Hamano2-1/+12
Perf-test fixes. * jk/t-perf-fixes: t/perf/run: preserve GIT_PERF_* from environment t/perf/perf-lib: fix assignment of TEST_OUTPUT_DIRECTORY
2026-01-16diff: improve scaling of filenames in diffstat to handle UTF-8 charsLorenzoPegorari1-11/+6
The `show_stats()` function tries to scale the filenames in the diffstat to ensure they don't exceed the given `name-width`. It does so by calculating the "display width" of the characters to be dropped, but then advances the filename pointer by that number of bytes. However, the "display width" of a character is not always equal to its byte count. The result is that sometimes, when displaying UTF-8 characters, filenames exceed the given `name-width`, and frequently the bytes of the UTF-8 characters are truncated. The following is an example of the issue, where the 2 files are "HelloHi" and "Hello你好", and `name-width=6`: ...oHi | 0 ...<BD><A0>好 | 0 Make the filename pointer move by the actual number of bytes of the characters to drop from the filename, rather than their display width, using the `utf8_width()` function. Force `len` to not be less than 0 (this happens if the given `name-width` is 2 or less), otherwise an infinite loop is entered. Signed-off-by: LorenzoPegorari <lorenzo.pegorari2002@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-15cocci: remove obsolete the_repository rulesRené Scharfe1-117/+2
035c7de9e9e (cocci: apply the "revision.h" part of "the_repository.pending", 2023-03-28) removed the last of the repo-less functions and macros mentioned in the_repository.cocci at the time. No stragglers appeared since then. Remove the applied rules now that they have outlived their usefulness. Also add a reminder to eventually remove the just added rules for tree.h. Suggested-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-15Revert "Merge branch 'ar/run-command-hook'"Junio C Hamano13-584/+290
This reverts commit f406b8955295d01089ba2baf35eceadff2d11cae, reversing changes made to 1627809eeff75e6ec936fc609e7be46d5eb2fa9e. It seems to have caused a few regressions, two of the three known ones we have proposed solutions for. Let's give ourselves a bit more room to maneuver during the pre-release freeze period and restart once the 2.53 ships.
2026-01-15Git 2.53-rc0v2.53.0-rc0Junio C Hamano2-1/+8
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-15Merge branch 'ps/clar-integers'Junio C Hamano14-70/+520
Import newer version of "clar", unit testing framework. * ps/clar-integers: gitattributes: disable blank-at-eof errors for clar test expectations t/unit-tests: demonstrate use of integer comparison assertions t/unit-tests: update clar to 39f11fe
2026-01-15Merge branch 'kh/replay-invalid-onto-advance'Junio C Hamano2-62/+79
Improve the error message when a bad argument is given to the `--onto` option of "git replay". Test coverage of "git replay" has been improved. * kh/replay-invalid-onto-advance: t3650: add more regression tests for failure conditions replay: die if we cannot parse object replay: improve code comment and die message replay: die descriptively when invalid commit-ish is given replay: find *onto only after testing for ref name replay: remove dead code and rearrange
2026-01-15Merge branch 'ps/odb-misc-fixes'Junio C Hamano3-4/+31
Miscellaneous fixes on object database layer. * ps/odb-misc-fixes: odb: properly close sources before freeing them builtin/gc: fix condition for whether to write commit graphs
2026-01-15Merge branch 'pt/t7800-difftool-test-racefix'Junio C Hamano1-3/+3
Test fixup. * pt/t7800-difftool-test-racefix: t7800: fix racy "difftool --dir-diff syncs worktree" test
2026-01-15Documentation/config: fix replacement for --get-urlmatchPushkar Singh1-1/+1
The documentation claims that --get-urlmatch is replaced by git config get --all --show-names --url=<URL> <name> However, --url cannot be combined with --all, and this command fails in practice. Update the replacement to use only --url, which matches the actual behavior of --get-urlmatch. Signed-off-by: Pushkar Singh <pushkarkumarsingh1970@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-15Merge branch 'ps/packfile-store-in-odb-source' into ps/odb-for-each-objectJunio C Hamano15-253/+359
* ps/packfile-store-in-odb-source: packfile: move MIDX into packfile store packfile: refactor `find_pack_entry()` to work on the packfile store packfile: inline `find_kept_pack_entry()` packfile: only prepare owning store in `packfile_store_prepare()` packfile: only prepare owning store in `packfile_store_get_packs()` packfile: move packfile store into object source packfile: refactor misleading code when unusing pack windows packfile: refactor kept-pack cache to work with packfile stores packfile: pass source to `prepare_pack()` packfile: create store via its owning source odb: properly close sources before freeing them builtin/gc: fix condition for whether to write commit graphs
2026-01-15Merge branch 'ps/read-object-info-improvements' into ps/odb-for-each-objectJunio C Hamano9-79/+158
* ps/read-object-info-improvements: packfile: drop repository parameter from `packed_object_info()` packfile: skip unpacking object header for disk size requests packfile: disentangle return value of `packed_object_info()` packfile: always populate pack-specific info when reading object info packfile: extend `is_delta` field to allow for "unknown" state packfile: always declare object info to be OI_PACKED object-file: always set OI_LOOSE when reading object info
2026-01-15commit: rename `free_commit_list()` to conform to coding guidelinesPatrick Steinhardt40-131/+136
Our coding guidelines say that: Functions that operate on `struct S` are named `S_<verb>()` and should generally receive a pointer to `struct S` as first parameter. While most of the functions related to `struct commit_list` already follow that naming schema, `free_commit_list()` doesn't. Rename the function to address this and adjust all of its callers. Add a compatibility wrapper for the old function name to ease the transition and avoid any semantic conflicts with in-flight patch series. This wrapper will be removed once Git 2.53 has been released. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-15commit: rename `reverse_commit_list()` to conform to coding guidelinesPatrick Steinhardt6-6/+11
Our coding guidelines say that: Functions that operate on `struct S` are named `S_<verb>()` and should generally receive a pointer to `struct S` as first parameter. While most of the functions related to `struct commit_list` already follow that naming schema, `reverse_commit_list()` doesn't. Rename the function to address this and adjust all of its callers. Add a compatibility wrapper for the old function name to ease the transition and avoid any semantic conflicts with in-flight patch series. This wrapper will be removed once Git 2.53 has been released. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-15commit: rename `copy_commit_list()` to conform to coding guidelinesPatrick Steinhardt6-7/+16
Our coding guidelines say that: Functions that operate on `struct S` are named `S_<verb>()` and should generally receive a pointer to `struct S` as first parameter. While most of the functions related to `struct commit_list` already follow that naming schema, `copy_commit_list()` doesn't. Rename the function to address this and adjust all of its callers. Add a compatibility wrapper for the old function name to ease the transition and avoid any semantic conflicts with in-flight patch series. This wrapper will be removed once Git 2.53 has been released. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-14remote-curl: use auth for probe_rpc() requests tooAaron Plattner2-0/+46
If a large request requires post_rpc() to call probe_rpc(), the latter does not use the authorization credentials used for other requests. If this fails with an HTTP 401 error and http_auth.multistage isn't set, then the whole request just fails. For example, using git-credential-msal [1], the following attempt to clone a large repository fails partway through because the initial request to download the commit history and promisor packs succeeds, but the subsequent request to download the blobs needed to construct the working tree fails with a 401 error and the checkout fails. (lines removed for brevity) git clone --filter=blob:none https://secure-server.example/repo 11:03:26.855369 git.c:502 trace: built-in: git clone --filter=blob:none https://secure-server.example/repo Cloning into 'sw'... warning: templates not found in /home/aaron/share/git-core/templates 11:03:26.857169 run-command.c:673 trace: run_command: git remote-https origin https://secure-server.example/repo 11:03:27.012104 http.c:849 => Send header: GET repo/info/refs?service=git-upload-pack HTTP/1.1 11:03:27.049243 http.c:849 <= Recv header: HTTP/1.1 401 Unauthorized 11:03:27.049270 http.c:849 <= Recv header: WWW-Authenticate: Bearer error="invalid_request", error_description="No bearer token found in the request", msal-tenant-id="<tenant>", msal-client-id="<client>" 11:03:27.053786 run-command.c:673 trace: run_command: 'git credential-msal get' 11:03:27.952830 http.c:849 => Send header: GET repo/info/refs?service=git-upload-pack HTTP/1.1 11:03:27.952849 http.c:849 => Send header: Authorization: Bearer <redacted> 11:03:27.995419 http.c:849 <= Recv header: HTTP/1.1 200 OK 11:03:28.230039 http.c:890 == Info: Reusing existing https: connection with host secure-server.example 11:03:28.230208 http.c:849 => Send header: POST repo/git-upload-pack HTTP/1.1 11:03:28.230216 http.c:849 => Send header: Content-Type: application/x-git-upload-pack-request 11:03:28.230221 http.c:849 => Send header: Authorization: Bearer <redacted> 11:03:28.269085 http.c:849 <= Recv header: HTTP/1.1 200 OK 11:03:28.684163 http.c:890 == Info: Reusing existing https: connection with host secure-server.example 11:03:28.684379 http.c:849 => Send header: POST repo/git-upload-pack HTTP/1.1 11:03:28.684391 http.c:849 => Send header: Accept: application/x-git-upload-pack-result 11:03:28.684393 http.c:849 => Send header: Authorization: Bearer <redacted> 11:03:28.869546 run-command.c:673 trace: run_command: git index-pack --stdin --fix-thin '--keep=fetch-pack 43856 on dgx-spark' --promisor 11:06:39.861237 run-command.c:673 trace: run_command: git -c fetch.negotiationAlgorithm=noop fetch origin --no-tags --no-write-fetch-head --recurse-submodules=no --filter=blob:none --stdin 11:06:39.865981 run-command.c:673 trace: run_command: git remote-https origin https://secure-server.example/repo 11:06:39.868039 run-command.c:673 trace: run_command: git-remote-https origin https://secure-server.example/repo 11:07:30.412575 http.c:849 => Send header: GET repo/info/refs?service=git-upload-pack HTTP/1.1 11:07:30.456285 http.c:849 <= Recv header: HTTP/1.1 401 Unauthorized 11:07:30.456318 http.c:849 <= Recv header: WWW-Authenticate: Bearer error="invalid_request", error_description="No bearer token found in the request", msal-tenant-id="<tenant>", msal-client-id="<client>" 11:07:30.456439 run-command.c:673 trace: run_command: 'git credential-cache get' 11:07:30.461266 http.c:849 => Send header: GET repo/info/refs?service=git-upload-pack HTTP/1.1 11:07:30.461282 http.c:849 => Send header: Authorization: Bearer <redacted> 11:07:30.501628 http.c:849 <= Recv header: HTTP/1.1 200 OK 11:07:34.725262 http.c:849 => Send header: POST repo/git-upload-pack HTTP/1.1 11:07:34.725279 http.c:849 => Send header: Content-Type: application/x-git-upload-pack-request 11:07:34.761407 http.c:849 <= Recv header: HTTP/1.1 401 Unauthorized 11:07:34.761443 http.c:890 == Info: Bearer authentication problem, ignoring. 11:07:34.761453 http.c:849 <= Recv header: WWW-Authenticate: Bearer error="invalid_request", error_description="No bearer token found in the request", msal-tenant-id="<tenant>", msal-client-id="<client>" 11:07:34.761509 http.c:890 == Info: The requested URL returned error: 401 11:07:34.761530 http.c:890 == Info: closing connection #0 11:07:34.761913 run-command.c:673 trace: run_command: 'git credential-cache erase' 11:07:34.761927 run-command.c:765 trace: start_command: /bin/sh -c 'git credential-cache erase' 'git credential-cache erase' 11:07:34.768069 git.c:502 trace: built-in: git credential-cache erase 11:07:34.768690 run-command.c:673 trace: run_command: 'git credential-msal erase' 11:07:34.768713 run-command.c:765 trace: start_command: /bin/sh -c 'git credential-msal erase' 'git credential-msal erase' 11:07:34.772742 git.c:808 trace: exec: git-credential-msal erase 11:07:34.772783 run-command.c:673 trace: run_command: git-credential-msal erase 11:07:34.772819 run-command.c:765 trace: start_command: /usr/bin/git-credential-msal erase error: RPC failed; HTTP 401 curl 22 The requested URL returned error: 401 fatal: unable to write request to remote: Broken pipe fatal: could not fetch c4fff0229c9be06ecf576356a4d39a8a755b8d81 from promisor remote warning: Clone succeeded, but checkout failed. You can inspect what was checked out with 'git status' and retry with 'git restore --source=HEAD :/' In this case, the HTTP_REAUTH retry logic is not used because the credential helper didn't set the 'continue' flag, so http_auth.multistage is false and handle_curl_result() fails with HTTP_NOAUTH instead. Fix the immediate problem by including the authorization headers in the probe_rpc() request as well. Add a test for this scenario: 1. Create a repository with two thousand refs. 2. Clone that into the web root used by t5563-simple-http-auth.sh. 3. Configure http.postBuffer to be very small in order to trigger the probe_rpc() path that fails. 4. Clone using a valid Bearer token. [1] https://github.com/Binary-Eater/git-credential-msal Tested-by: Lucas De Marchi <demarchi@kernel.org> Signed-off-by: Aaron Plattner <aplattner@nvidia.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-14builtin/repack: handle promisor packs with geometric repackingPatrick Steinhardt5-5/+123
When performing a fetch with an object filter, we mark the resulting packfile as a promisor pack. An object part of such a pack may miss any of its referenced objects, and Git knows to handle this case by fetching any such missing objects from the promisor remote. The "promisor" property needs to be retained going forward. So every time we pack a promisor object, the resulting pack must be marked as a promisor pack. git-repack(1) does this already: when a repository has a promisor remote, it knows to pass "--exclude-promisor-objects" to the git-pack-objects(1) child process. Promisor packs are written separately when doing an all-into-one repack via `repack_promisor_objects()`. But we don't support promisor objects when doing a geometric repack yet. Promisor packs do not get any special treatment there, as we simply merge promisor and non-promisor packs. The resulting pack is not even marked as a promisor pack, which essentially corrupts the repository. This corruption couldn't happen in the real world though: we pass both "--exclude-promisor-objects" and "--stdin-packs" to git-pack-objects(1) if a repository has a promisor remote, but as those options are mutually exclusive we always end up dying. And while we made those flags compatible with one another in a preceding commit, we still end up dying in case git-pack-objects(1) is asked to repack a promisor pack. There's multiple ways to fix this: - We can exclude promisor packs from the geometric progression altogether. This would have the consequence that we never repack promisor packs at all. But in a partial clone it is quite likely that the user generates a bunch of promisor packs over time, as every backfill fetch would create another one. So this doesn't really feel like a sensible option. - We can adapt git-pack-objects(1) to support repacking promisor packs and include them in the normal geometric progression. But this would mean that the set of promisor objects expands over time as the packs are merged with normal packs. - We can use a separate geometric progression to repack promisor packs. The first two options both have significant downsides, so they aren't really feasible. But the third option fixes both of these downsides: we make sure that promisor packs get merged, and at the same time we never expand the set of promisor objects beyond the set of objects that are already marked as promisor objects. Implement this strategy so that geometric repacking works in partial clones. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-14repack-promisor: extract function to remove redundant packsPatrick Steinhardt1-6/+16
We're about to add a second caller that wants to remove redundant packs after a geometric repack. Split out the function which does this to prepare for that. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-14repack-promisor: extract function to finalize repackingPatrick Steinhardt1-30/+39
We're about to add a second caller that wants to finalize repacking of promisor objects. Split out the function which does this to prepare for that. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-14repack-geometry: extract function to compute repacking splitPatrick Steinhardt1-18/+21
We're about to add a second caller that wants to compute the repacking split for a set of packfiles. Split out the function that computes this split to prepare for that. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-14builtin/pack-objects: exclude promisor objects with "--stdin-packs"Patrick Steinhardt2-3/+50
It is currently not possible to combine "--exclude-promisor-objects" with "--stdin-packs" because both flags want to set up a revision walk to enumerate the objects to pack. In a subsequent commit though we want to extend geometric repacks to support promisor objects, and for that we need to handle the combination of both flags. There are two cases we have to think about here: - "--stdin-packs" asks us to pack exactly the objects part of the specified packfiles. It is somewhat questionable what to do in the case where the user asks us to exclude promisor objects, but at the same time explicitly passes a promisor pack to us. For now, we simply abort the request as it is self-contradicting. As we have also been dying before this commit there is no regression here. - "--stdin-packs=follow" does the same as the first flag, but it also asks us to include all objects transitively reachable from any object in the packs we are about to repack. This is done by doing the revision walk mentioned further up. Luckily, fixing this case is trivial: we only need to modify the revision walk to also set the `exclude_promisor_objects` field. Note that we do not support the "--exclude-promisor-objects-best-effort" flag for now as we don't need it to support geometric repacking with promisor objects. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-14gitk: use config settings for head/tag colorsShannon Barber1-4/+7
The drawtags procedure currently uses headfgcolor for all label text, ignoring the tagfgcolor setting. The call to create the outline polygon for (non-tag) heads currently has the color for headoutlinecolor hardcoded to black. This patch maintains the variables for the non-tag refs so that heads are colored differently from non-head (non-tag) refs. The outline and fill colors for the non-head refs remain hardcoded to the prior values, black & #ddddff. Signed-off-by: Shannon Barber <sgbarber@gmail.com> Signed-off-by: Johannes Sixt <j6t@kdbg.org>
2026-01-13replay: drop commits that become emptyPhillip Wood3-4/+31
If the changes in a commit being replayed are already in the branch that the commits are being replayed onto, then "git replay" creates an empty commit. This is confusing because the commit message no longer matches the contents of the commit. Drop the commit instead. Commits that start off empty are not dropped. This matches the behavior of "git rebase --reapply-cherry-pick --empty=drop" and "git cherry-pick --empty-drop". If a branch points to a commit that is dropped it will be updated to point to the last commit that was not dropped. This can be seen in the new test where "topic1" is updated to point to the rebased "C" as "F" is dropped because it is already upstream. While this is a breaking change, "git replay" is marked as experimental to allow improvements like this that change the behavior. Helped-by: Elijah Newren <newren@gmail.com> Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-13Merge branch 'ps/history' into pw/replay-drop-emptyJunio C Hamano187-2586/+5794
* ps/history: (186 commits) builtin/history: implement "reword" subcommand builtin: add new "history" command wt-status: provide function to expose status for trees replay: support updating detached HEAD replay: support empty commit ranges replay: small set of cleanups builtin/replay: move core logic into "libgit.a" builtin/replay: extract core logic to replay revisions The 15th batch t3650: add more regression tests for failure conditions replay: die if we cannot parse object replay: improve code comment and die message replay: die descriptively when invalid commit-ish is given replay: find *onto only after testing for ref name replay: remove dead code and rearrange The 14th batch The 13th batch config: use git_parse_int() in git_config_get_expiry_in_days() receive-pack: convert receive hooks to hook API receive-pack: convert update hooks to new API ...
2026-01-13builtin/history: implement "reword" subcommandPatrick Steinhardt5-10/+823
Implement a new "reword" subcommand for git-history(1). This subcommand is similar to the user performing an interactive rebase with a single commit changed to use the "reword" instruction. The "reword" subcommand is built on top of the replay subsystem instead of the sequencer. This leads to some major differences compared to git-rebase(1): - We do not check out the commit that is to be reworded and instead perform the operation in-memory. This has the obvious benefit of being significantly faster compared to git-rebase(1), but even more importantly it allows the user to rewrite history even if there are local changes in the working tree or in the index. - We do not execute any hooks, even though we leave some room for changing this in the future. - By default, all local branches that contain the commit will be rewritten. This especially helps with workflows that use stacked branches. Helped-by: Elijah Newren <newren@gmail.com> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-13builtin: add new "history" commandPatrick Steinhardt11-0/+104
When rewriting history via git-rebase(1) there are a few very common use cases: - The ordering of two commits should be reversed. - A commit should be split up into two commits. - A commit should be dropped from the history completely. - Multiple commits should be squashed into one. - Editing an existing commit that is not the tip of the current branch. While these operations are all doable, it often feels needlessly kludgey to do so by doing an interactive rebase, using the editor to say what one wants, and then perform the actions. Also, some operations like splitting up a commit into two are way more involved than that and require a whole series of commands. Rebases also do not update dependent branches. The use of stacked branches has grown quite common with competing version control systems like Jujutsu though, so it clearly is a need that users have. While rebases _can_ serve this use case if one always works on the latest stacked branch, it is somewhat awkward and very easy to get wrong. Add a new "history" command to plug these gaps. This command will have several different subcommands to imperatively rewrite history for common use cases like the above. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-13wt-status: provide function to expose status for treesPatrick Steinhardt2-0/+33
The "wt-status" subsystem is responsible for printing status information around the current state of the working tree. This most importantly includes information around whether the working tree or the index have any changes. We're about to introduce a new command where the changes in neither of them are actually relevant to us. Instead, what we want is to format the changes between two different trees. While it is a little bit of a stretch to add this as functionality to _working tree_ status, it doesn't make any sense to open-code this functionality, either. Implement a new function `wt_status_collect_changes_trees()` that diffs two trees and formats the status accordingly. This function is not yet used, but will be in a subsequent commit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-13replay: support updating detached HEADPatrick Steinhardt2-14/+42
In a subsequent commit we're about to introduce a new git-history(1) command, which will by default work on all local branches and HEAD. This is already well-supported by the replay machinery for most of the part: updating branches is one of its prime use cases, and the HEAD ref is also updated in case it points to any of the branches. However, what's not supported yet is to update HEAD in case it is not a symbolic ref. We determine the refs that need to be updated by iterating through the decorations of the original commit, but we only update those refs that are `DECORATION_REF_LOCAL`, which covers local branches. Address this gap by also handling `DECORATION_REF_HEAD`. Note though that this needs to only happen in case we're working on a detached HEAD. If HEAD is pointing to a branch, then we'd already update that branch via `DECORATION_REF_LOCAL`. Refactor the loop that iterates through the decorations a bit to make the individual conditions easier to understand. Based-on-patch-by: Elijah Newren <newren@gmail.com> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-13replay: support empty commit rangesPatrick Steinhardt1-2/+3
In a subsequent commit we're about to introduce a new user of the replay subsystem. With that new user, the range of commits that we'll want to replay will be identified implicitly via a single commit, and will include all descendants of that commit to any branch. If that commit has no descendants (because it's the tip of some branch), then the range of revisions that we're asked to replay becomes empty. This case does not make sense with git-replay(1), but with the new command it will. This case is not currently supported by `replay_revisions()` though because we zero-initialize `struct merge_result`. This includes its `.clean` member, which indicates whether the merge ran into a conflict or not. But given that we don't have any revision to replay, we won't ever perform any merge at all, and consequently that member will never be set to `1`. We thus later think that there's been a merge conflict and return an error from `replay_commits()`. Address this issue by initializing the `.clean` member to `1`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-13replay: small set of cleanupsPatrick Steinhardt1-7/+4
Perform a small set of cleanups so that the "replay" logic compiles with "-Wsign-compare" and doesn't use `the_repository` anymore. Note that there are still some implicit dependencies on `the_repository`, e.g. because we use `get_commit_output_encoding()`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-13builtin/replay: move core logic into "libgit.a"Patrick Steinhardt5-365/+418
Move the core logic used to replay commits into "libgit.a" so that it can be easily reused by other commands. It will be used in a subsequent commit where we're about to introduce a new git-history(1) command. Note that with this change we have no sign-comparison warnings anymore, and neither do we depend on `the_repository`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-13builtin/replay: extract core logic to replay revisionsPatrick Steinhardt1-107/+162
We're about to move the core logic used to replay revisions onto a new base into the "libgit.a" library. Prepare for this by pulling out the logic into a new function `replay_revisions()` that: 1. Takes a set of revisions to replay and some options that tell it how it ought to replay the revisions. 2. Replays the commits. 3. Records any reference updates that would be caused by replaying the commits in a structure that is owned by the caller. The logic itself will be moved into a separate file in the next commit. This change is not expected to cause user-visible change in behaviour. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-13midx-write.c: assume checksum-invalid MIDXs require an updateTaylor Blau2-1/+15
In 6ce9d558ced (midx-write: skip rewriting MIDX with `--stdin-packs` unless needed, 2025-12-10), the MIDX machinery learned how to optimize out unnecessary writes with "--stdin-packs". In order to do this, it compares the contents of the in-progress write against a MIDX loaded directly from the object store. We load a separate MIDX (as opposed to checking our update relative to "ctx.m") because the MIDX code does not reuse an existing MIDX with --stdin-packs, and always leaves "ctx.m" as NULL. See commit 0c5a62f14bc (midx-write.c: do not read existing MIDX with `packs_to_include`, 2024-06-11) for details on why. If "ctx.m" is non-NULL, however, it is guaranteed to be checksum-valid, since we only assign "ctx.m" when "midx_checksum_valid()" returns true. Since the same guard does not exist for the MIDX we pass to "midx_needs_update()", we may ignore on-disk corruption when determining whether or not we can optimize out the write. Add a similar guard within "midx_needs_update()" to prevent such an issue. A more robust fix would involve revising 0c5a62f14bc and teaching the MIDX generation code how to reuse an existing MIDX even when invoked with "--stdin-packs", such that we could avoid side-loading the MIDX directly from the object store in order to call "midx_needs_update()". For now, pursue the minimal fix. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-13t/t5319-multi-pack-index.sh: drop early 'test_done'Taylor Blau1-3/+1
In 6ce9d558ced (midx-write: skip rewriting MIDX with `--stdin-packs` unless needed, 2025-12-10), an extra 'test_done' was added, causing the test script to finish before having run all of its tests. Dropping this extraneous 'test_done' exposes a bug from commit 6ce9d558ced that causes a subsequent test to fail. Mark that test with a 'test_expect_failure' for now, and the subsequent commit will explain and fix the bug. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-13Merge branch 'ps/repack-avoid-noop-midx-rewrite' into ↵Junio C Hamano5-20/+193
tb/midx-write-corrupt-checksum-fix * ps/repack-avoid-noop-midx-rewrite: midx-write: skip rewriting MIDX with `--stdin-packs` unless needed midx-write: extract function to test whether MIDX needs updating midx: fix `BUG()` when getting preferred pack without a reverse index
2026-01-12doc: MyFirstContribution: fix missing dependencies and clarify build stepsShreyansh Paliwal1-6/+9
Fix issues in the MyFirstContribution guide that can lead to confusion or test failures when following the documented steps. * Add missing header includes in code examples (environment.h and strbuf.h). * Correct manpage synopsis formatting to prevent failing documentation tests. * Specify the use of parallel test execution with -j$(nproc), noting that it runs tests using all available CPUs and may be adjusted. These updates improve documentation accuracy and make the first-time contributor journey smoother. Signed-off-by: Shreyansh Paliwal <shreyanshpaliwalcmsmn@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-12utf8.c: enable workaround for iconv under macOS 14/15Torsten Bögershausen2-0/+17
The previous commit introduced a workaround in utf8.c to deal with broken iconv implementations. It is enabled when a MacOS version is used that has a buggy iconv library and there is no external library provided (and linked against) from neither MacPorts nor Homebrew nor Fink. For Homebrew, MacPorts and Fink we check if libiconv exist. Introduce 2 new macros: HAS_GOOD_LIBICONV and NEEDS_GOOD_LIBICONV. For Homebrew HAS_GOOD_LIBICONV is set when the libiconv directory exist. MacPorts can be installed with or without libiconv, so check if libiconv.dylib exists (which is a softlink) Fink compiles and installs libiconv by default. Note that a fresh installation of Fink now defaults to /opt/sw. Older versions used /sw as default, so leave the check and setting of BASIC_CFLAGS and BASIC_LDFLAGS as is. For the new default check for the existance of /opt/sw as well. Add a check for /opt/sw/lib/libiconv.dylib which sets HAS_GOOD_LIBICONV Signed-off-by: Torsten Bögershausen <tboegi@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-12utf8.c: prepare workaround for iconv under macOS 14/15Torsten Bögershausen1-0/+13
MacOS14 (Sonoma) has started to ship an iconv library with bugs. The same bugs exists even in MacOS 15 (Sequoia) A bug report running the Git test suite says: three tests of t3900 fail on macOS 26.1 for me: not ok 17 - ISO-2022-JP should be shown in UTF-8 now not ok 25 - ISO-2022-JP should be shown in UTF-8 now not ok 38 - commit --fixup into ISO-2022-JP from UTF-8 Here's the verbose output of the first one: ================= expecting success of 3900.17 'ISO-2022-JP should be shown in UTF-8 now': compare_with ISO-2022-JP "$TEST_DIRECTORY"/t3900/2-UTF-8.txt --- /Users/x/src/git/t/t3900/2-UTF-8.txt 2024-10-01 19:43:24.605230684 +0000 +++ current 2025-12-08 21:52:45.786161909 +0000 @@ -1,5 +1,5 @@ はれひほふ しているのが、いるので。 -濱浜ほれぷりぽれまびぐりろへ。 +濱浜ほれぷりぽれまび$0$j$m$X!# not ok 17 - ISO-2022-JP should be shown in UTF-8 now 1..17 ================= compare_with runs git show to display a commit message, which in this case here was encoded using ISO-2022-JP and is supposed to be reencoded to UTF-8, but git show only does that half-way -- the "$0$j$m$X!#" part is from the original ISO-2022-JP representation. That botched conversion is done by utf8.c::reencode_string_iconv(). It calls iconv(3) to do the actual work, initially with an output buffer of the same size as the input. If the output needs more space the function enlarges the buffer and calls iconv(3) again. iconv(3) won't tell us how much space it needs, but it will report what part it already managed to convert, so we can increase the buffer and continue from there. ISO-2022-JP has escape codes for switching between character sets, so it's a stateful encoding. I guess the iconv(3) on my machine forgets the state at the end of part one and then messes up part two. [end of citation] Working around the buggy iconv shipped with the OS can be done in two ways: a) Link Git against a different version of iconv b) Improve the handling when iconv needs a larger output buffer a) is already done by default when either Fink [1] or MacPorts [2] or Homebrew [3] is installed. b) is implemented here, in case that no fixed iconv is available: When the output buffer is too short, increase it (as before) and start from scratch (this is new). This workound needs to be enabled with '#define ICONV_RESTART_RESET' and a makefile knob will be added in the next commit Suggested-by: René Scharfe <l.s.r@web.de> Signed-off-by: Torsten Bögershausen <tboegi@web.de> [1] https://www.finkproject.org/ [2] https://www.macports.org/ [3] https://brew.sh/ Signed-off-by: Torsten Bögershausen <tboegi@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-12t5403: use test_cmp for post-checkout argument checksDeveshi Dwivedi1-4/+4
Update check_post_checkout and the post-checkout hook implementation to use test_cmp instead of individual test commands. This provides better error messages when tests fail, making it easier to debug which specific argument (old ref, new ref, or flag) was incorrect. The hook now outputs in key=value format which test_cmp can display clearly when there's a mismatch. Signed-off-by: Deveshi Dwivedi <deveshigurgaon@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-12t5403: introduce check_post_checkout helper functionDeveshi Dwivedi1-21/+28
The test file repeatedly uses the same four-line pattern to validate post-checkout hook arguments: read the args file, then test each of the three values individually. Introduce a check_post_checkout helper function that encapsulates this pattern. This patch does not change test behavior; it prepares the code for improvement in the next step. Additionally, the 'post-checkout hook is triggered by clone' test is improved to validate the hook arguments (old ref, new ref, and flag) rather than just checking that the hook file was created. Signed-off-by: Deveshi Dwivedi <deveshigurgaon@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-12submodule: detect conflicts with existing gitdir configsAdrian Ratiu2-0/+89
Credit goes to Emily and Josh for testing and noticing a corner-case which caused conflicts with existing gitdir configs to silently pass validation, then fail later in add_submodule() with a cryptic error: fatal: A git directory for 'nested%2fsub' is found locally with remote(s): origin /.../trash directory.t7425-submodule-gitdir-path-extension/sub This change ensures the validation step checks existing gitdirs for conflicts. We only have to do this for submodules having gitdirs, because those without submodule.%s.gitdir need to be migrated and will throw an error earlier in the submodule codepath. Quoting Josh: My testing setup has been as follows: * Using our locally-built Git with our downstream patch of [1] included: * create a repo "sub" * create a repo "super" * In "super": * mkdir nested * git submodule add ../sub nested/sub * Verify that the submodule's gitdir is .git/modules/nested%2fsub * Using a build of git from upstream `next` plus this series: * git config set --global extensions.submodulepathconfig true * git clone --recurse-submodules super super2 * create a repo "nested%2fsub" * In "super2": * git submodule add ../nested%2fsub At this point I'd expect the collision detection / encoding to take effect, but instead I get the error listed above. End quote Suggested-by: Josh Steadmon <steadmon@google.com> Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>