Commit Graph

81981 Commits (d8636bd7448abb516ca137565962a45e69fbe608)

Author SHA1 Message Date
Junio C Hamano d8636bd744 Merge branch 'jk/diff-relative-cached-unmerged-more' into next
The code path that deals with relative paths in the diff-lib has
been cleaned up.

* jk/diff-relative-cached-unmerged-more:
  diff-lib: skip paths outside prefix in oneway_diff()
  diff-lib: drop stale comment about advancing o->pos
2026-07-30 10:44:56 -07:00
Junio C Hamano 250fe7f194 Merge branch 'js/mingw-symlink-net-share-leak' into next
Git for Windows has been updated to avoid auto-detecting the symlink
type if the target path starts with a slash, preventing NTLM
credential leaks when checking out repositories with crafted
symbolic links pointing to network shares.

* js/mingw-symlink-net-share-leak:
  mingw: skip symlink type auto-detection for network share targets
2026-07-29 11:46:33 -07:00
Junio C Hamano 39a86632e6 Merge branch 'jk/t0014-dynamic-deprecated-cmds' into next
The alias tests in t/t0014-alias.sh have been updated to dynamically
query the list of deprecated commands using 'git
--list-cmds=deprecated' to avoid test failures when running with
WITH_BREAKING_CHANGES in a build directory that contains stale
executables of formerly deprecated commands.

* jk/t0014-dynamic-deprecated-cmds:
  t0014: generate deprecated command names dynamically
  t0014: factor out choice of deprecated commands
2026-07-29 11:46:33 -07:00
Junio C Hamano 457a175861 Merge branch 'jk/ci-static-analysis-image-bump' into next
The image version used by the static-analysis CI job has been bumped
to ubuntu-latest (Ubuntu 24.04), which brings in a newer Coccinelle
version that resolves a severe performance regression.  A false
positive warning from the CHECK_ASSERTION_SIDE_EFFECTS build with
GCC 15 in the Bloom filter code has also been silenced to facilitate
the image upgrade.

* jk/ci-static-analysis-image-bump:
  ci: bump ubuntu image version for static-analysis job
  bloom: silence CHECK_ASSERTION_SIDE_EFFECTS false positive
2026-07-29 11:46:33 -07:00
Junio C Hamano a29fc00619 Revert "Merge branch 'hn/bisect-reset-when-found' into next"
This reverts commit 1dc394ad9b, reversing
changes made to 968a116891.

cf. <faa22968-54ac-4e4f-8324-3326ffb00c5b@kdbg.org>
2026-07-29 11:40:13 -07:00
Junio C Hamano 36b42465bf Revert "Merge branch 'hn/branch-delete-merged' into next"
This reverts commit c61a87bdf5, reversing
changes made to 57c0e98bfb.

cf. <80bd230e-7b8c-41d3-af1c-fa84b0c7b1c4@gmail.com>
2026-07-29 08:53:32 -07:00
Junio C Hamano 11cd3fbfc2 Revert "Merge branch 'hn/history-squash' into next"
This reverts commit 2790c83e45, reversing
changes made to 3f0d502094.

cf. <f5f7af53-df3e-4902-b350-8fcf8ccb02ad@gmail.com>
2026-07-29 08:48:02 -07:00
Junio C Hamano c0e15e73b6 Merge branch 'rs/branch-delete-bisect-warning' into next
'git branch -d' has been taught to report when a branch cannot be
deleted because it is being used in an active bisect run.

* rs/branch-delete-bisect-warning:
  branch: report active bisect run when rejecting delete
2026-07-28 16:26:44 -07:00
Jeff King bc57ecb915 t0014: generate deprecated command names dynamically
We have a few tests related to aliasing of deprecated commands. They use
whatchanged and pack-redundant because those are the only two deprecated
commands we have. Eventually those commands will be removed, at which
point these tests will be checking nothing useful (they'll just be
regular aliases, which we already cover in other tests).

We could remove them at that point, but the code to handle deprecated
commands will still remain. We probably do want to keep the tests around
for the eventual day that we deprecate more commands. So let's ask Git
for its list of deprecated commands, and if we don't have any, skip
those tests.

This also prevents an annoying corner case when your build directory
contains old build products. Right now those commands are marked as
deprecated builtins and treated specially; we allow aliases and never
look for them as dashed external commands. But after they are removed,
they aren't special anymore. If your directory happens to contain
hardlinks from the build of an older version, that confuses Git: it sees
the old hardlinks in place, thinks those are actual external commands,
and refuses to allow aliasing.

You can see that today like this:

  make
  make WITH_BREAKING_CHANGES=1 test

The first "make" creates git-whatchanged as a hardlink to Git, and the
second does not clean it up (it doesn't know about the whatchanged
command at all anymore). t0014 fails because Git won't create an alias
to the "external" whatchanged command.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-28 15:43:32 -07:00
Jeff King b678bb7283 t0014: factor out choice of deprecated commands
We have a few tests related to aliasing deprecated commands which use
"whatchanged" and "pack-redundant", as these are the only two deprecated
commands we have. Let's pull those names into variables so that we can
refactor the tests without relying on the specific names.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-28 15:43:32 -07:00
Johannes Schindelin c57c052ae8 mingw: skip symlink type auto-detection for network share targets
On Windows, symbolic links come in two flavors: file symlinks and
directory symlinks.  Since Git was born on Linux where this distinction
does not exist, Git for Windows has to auto-detect the type by looking
at the target.  When the target does not yet exist at symlink creation
time, Git for Windows creates a "phantom" file symlink and later, once
checkout is complete, calls `CreateFileW()` on the target to check
whether it is actually a directory.

If the symlink target is a UNC path (e.g. `\\attacker\share`), this
auto-detection triggers an SMB connection to the remote host.  Windows
performs NTLM authentication by default for such connections, which
means a crafted repository can exfiltrate the cloning user's NTLMv2 hash
to an attacker-controlled server without any user interaction beyond
`git clone -c core.symlinks=true <url>`.

There are ways to specify UNC paths that start with only a single
backslash (e.g. `\??\UNC\host\share`); All of them do start like that,
though, so let's use that as a tell-tale that we should skip the
auto-detection in `process_phantom_symlink()`. The symlink is then left
as a file symlink (the `mklink` default), and a warning is emitted
suggesting the user set the `symlink` gitattribute to `dir` if a
directory symlink is needed.  When the attribute is already set,
auto-detection is never invoked in the first place, so that code path is
unaffected.

This is the same class of vulnerability as CVE-2025-66413
(https://github.com/git-for-windows/git/security/advisories/GHSA-hv9c-4jm9-jh3x)
and follows the same general mitigation pattern that MinTTY adopted for
ANSI escape sequences referencing network share paths
(https://github.com/mintty/mintty/security/advisories/GHSA-jf4m-m6rv-p6c5).

Note that there are legitimate paths starting with a single backslash
that are _not_ network paths: drive-less absolute paths are interpreted
as relative to the current working directory's drive. In practice, these
are highly uncommon (and brittle, just one working directory change away
from breaking). In any case, the only consequence is now that the
symlink type of those has to be specified via Git attributes, is all.

Reported-by: Justin Lee <jessdhoctor@gmail.com>
Addresses: CVE-2026-32631
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-28 15:39:59 -07:00
Junio C Hamano 1e622f0e29 Merge branch 'ps/odb-pluggable-housekeeping' into next
Object database housekeeping in 'git gc' and 'git maintenance' has
been refactored to be pluggable.  The files-backend-specific logic,
including incremental and geometric repacking as well as object
pruning, has been moved out of the command implementation and into the
files object database source, enabling future alternative object
database backends to implement their own housekeeping services.

* ps/odb-pluggable-housekeeping:
  odb: make optimizations pluggable
  builtin/gc: fix signedness issues in ODB-related functionality
  builtin/gc: refactor ODB optimizations to operate on "files" source
  builtin/gc: introduce `odb_optimize_required()`
  builtin/gc: move geometric repacking into `odb_optimize()`
  builtin/gc: introduce object database optimization options
  builtin/gc: inline config values specific to the "files" backend
  builtin/gc: make repack arguments self-contained
  builtin/gc: extract object database optimizations into separate function
  builtin/gc: move worktree and rerere tasks before object optimizations
  odb: run "pre-auto-gc" hook for all maintenance tasks
  t7900: simplify how we check for maintenance tasks
2026-07-28 12:15:03 -07:00
Junio C Hamano 736307fae5 Merge branch 'pw/rebase-fixup-fixes' into next
Two bugs in how 'git rebase' handles skipped 'fixup' and 'squash'
commands have been fixed.  One bug caused an incorrect commit count to
be shown in the template message when multiple commands were skipped,
and another prevented the editor from opening when the final command
in a chain containing 'fixup -c' was skipped.

* pw/rebase-fixup-fixes:
  rebase: remember fixup -c after skipping fixup/squash
  rebase -i: fix counting of fixups after rebase --skip
2026-07-28 12:15:03 -07:00
Junio C Hamano 1ac873b5f9 Merge branch 'jk/diff-relative-cached-unmerged' into next
'git diff --relative' running with '--cached' has been corrected to
avoid a segfault when encountering unmerged paths outside the
prefix.

* jk/diff-relative-cached-unmerged:
  diff-lib: add idx/tree sanity check to oneway_diff
2026-07-28 12:15:02 -07:00
Junio C Hamano b3b5e47172 Revert "Merge branch 'tc/replay-linearize' into next"
This reverts commit 371c2e9c3b,
reversing changes made to fd2b979b73,
to re-queue the v8 iteration of the topic.
2026-07-28 11:26:59 -07:00
Jeff King 151726d3ca diff-lib: skip paths outside prefix in oneway_diff()
Commit 8174627b3d (diff-lib: ignore paths that are outside $cwd if
--relative asked, 2021-08-22) taught run_diff_files() to skip entries
outside the requested prefix before processing them.

Do the same in oneway_diff(), which handles the diff-index code path.
The lower-level diff queue functions already reject such paths, but
checking here avoids unnecessary work and keeps them out of every
do_oneway_diff() code path.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-28 09:28:09 -07:00
Jeff King 640177a987 diff-lib: drop stale comment about advancing o->pos
The comment above oneway_diff() claims that the callback must advance
o->pos to skip index entries it has already processed. That stopped
being true in da165f470e (unpack-trees.c: prepare for looking ahead in
the index, 2010-01-07), which moved that bookkeeping into
unpack_trees().

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-28 09:17:27 -07:00
Junio C Hamano 8d01be9868 Merge branch 'jk/diff-relative-cached-unmerged' into jk/diff-relative-cached-unmerged-more
* jk/diff-relative-cached-unmerged:
  diff-lib: add idx/tree sanity check to oneway_diff
  diff: ignore unmerged paths outside prefix with --relative --cached
2026-07-28 09:17:12 -07:00
Jeff King 447126ed7d diff-lib: add idx/tree sanity check to oneway_diff
When looking just at the code in oneway_diff(), it seems possible for
both "idx" and "tree" to be NULL, in which case we'd potentially
segfault while checking the relative prefix.

But if you consider what these items actually mean, it shouldn't be
possible for both to be NULL. Let's add an assertion and a comment
documenting this. It might help human readers, but should also silence
static analyzers like Coverity which complain about the potential
segfault.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-28 09:12:24 -07:00
Junio C Hamano 2080f0d119 Sync with 'master' 2026-07-27 09:11:05 -07:00
Junio C Hamano a1cbd7aa11 Merge branch 'en/submodule-insteadof-remote-match' into next
The remote-matching logic for submodules has been corrected to
resolve 'url.*.insteadOf' aliases before comparing the inventoried
URL from '.gitmodules' with the URLs of configured remotes.

* en/submodule-insteadof-remote-match:
  submodule: resolve insteadOf aliases when matching remote
2026-07-27 09:10:56 -07:00
Junio C Hamano 8cf88af88b Merge branch 'jc/remote-insteadof-leakfix' into next
rewrites_release() in 'remote.c' has been updated to free 'struct
rewrite' instances and their '.instead_of' arrays, and their contents.

* jc/remote-insteadof-leakfix:
  remote: plug memory leaks
2026-07-27 09:10:56 -07:00
Junio C Hamano c61a87bdf5 Merge branch 'hn/branch-delete-merged' into next
The 'git branch' command has been taught the '--delete-merged' option
to remove local branches that are already merged into their tracked
remote-tracking branches.

* hn/branch-delete-merged:
  branch: add --dry-run for --delete-merged
  branch: add branch.<name>.deleteMerged opt-out
  branch: add --delete-merged <branch>
  branch: prepare delete_branches for a bulk caller
  branch: let delete_branches skip unmerged branches on bulk refusal
  branch: convert delete_branches() to a flags argument
  branch: add --forked filter for --list mode
2026-07-27 09:10:56 -07:00
Junio C Hamano 57c0e98bfb Merge branch 'af/clone-revision-v0-segfault-fix' into next
'git clone --revision' talking to a server that does not support
protocol v2 (falling back to protocol v0) segfaulted, which has
been corrected.

* af/clone-revision-v0-segfault-fix:
  builtin/clone: fix segfault when using --revision with protocol v0
2026-07-27 09:10:55 -07:00
Junio C Hamano 13c7afec21 The 8th batch
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-27 09:09:01 -07:00
Junio C Hamano 47ea9e4c6e Merge branch 'bc/rust-hash-cleanups'
A few memory problems in the Rust interface to C hash functions have
been corrected.  The 'Clone' implementation of 'CryptoHasher' now
properly initializes the context before cloning, and its 'Drop'
implementation now discards the context to prevent leaks.

* bc/rust-hash-cleanups:
  rust: discard hash context when finished
  hash: initialize context before cloning
2026-07-27 09:09:01 -07:00
Junio C Hamano f364885cb3 Merge branch 'pw/rebase-drop-notes-with-commit'
The rebase post-rewrite notes-copying logic has been corrected.  When
a commit is dropped during rebase (e.g., because its changes are
already upstream), it is no longer recorded as rewritten, preventing
its notes from being copied to an unrelated commit.

* pw/rebase-drop-notes-with-commit:
  sequencer: do not record dropped commits as rewritten
  sequencer: use an enum to represent result of picking a commit
  sequencer: simplify pick_one_commit()
  sequencer: remove unnecessary condition in pick_one_commit()
  sequencer: simplify handling of fixup with conflicts
  sequencer: remove unnecessary "or" in pick_one_commit()
  sequencer: never reschedule on failed commit
  sequencer: be more careful with external merge
  t3400: restore coverage for note copying with apply backend
2026-07-27 09:09:01 -07:00
Junio C Hamano bb8bc6995e Merge branch 'ps/copy-wo-the-repository'
The copy_file() and copy_file_with_time() functions have been
refactored to take a repository parameter, allowing the removal of the
implicit dependency on the global 'the_repository' variable in
'copy.c'.

* ps/copy-wo-the-repository:
  copy: drop dependency on `the_repository`
2026-07-27 09:09:00 -07:00
Junio C Hamano 7d64c1e1fc Merge branch 'sc/wt-status-avoid-quadratic-insertion'
The enumeration of untracked and ignored files in 'git status' has
been optimized by avoiding quadratic complexity when inserting into
string lists, reducing the construction cost from O(n^2) to O(n log
n).

* sc/wt-status-avoid-quadratic-insertion:
  wt-status: avoid repeated insertion for untracked paths
2026-07-27 09:09:00 -07:00
Junio C Hamano 4be0a34098 Merge branch 'ps/refspec-wo-the-repository'
The dependency on the global 'the_repository' variable in the
'refspec.c' API has been removed by passing the hash algorithm
explicitly to refspec-parsing functions and storing it in 'struct
refspec'.

* ps/refspec-wo-the-repository:
  refspec: stop depending on `the_repository`
  refspec: let callers pass in hash algorithm when parsing items
  refspec: group related structures and functions
2026-07-27 09:09:00 -07:00
Junio C Hamano f97a43e914 Merge branch 'rs/remote-curl-simplify-push-specs'
The passing of push destination specifications in the 'remote-curl'
helper has been simplified by removing the explicit 'count' parameter
and relying on the NULL-termination of the array.

* rs/remote-curl-simplify-push-specs:
  remote-curl: simplify passing of push specs
2026-07-27 09:08:59 -07:00
Junio C Hamano ebc9fb5873 Merge branch 'td/ref-filter-memoize-contains'
'git branch --contains' and 'git for-each-ref --contains' have been
optimized to use the memoized commit traversal previously used only by
'git tag --contains', significantly speeding up connectivity checks
across many candidate refs with shared history.

* td/ref-filter-memoize-contains:
  commit-reach: die on contains walk errors
  ref-filter: memoize --contains with generations
  commit-reach: reject cycles in contains walk
2026-07-27 09:08:59 -07:00
Junio C Hamano 0c20b08631 Merge branch 'ps/refs-wo-the-repository'
The ref subsystem and the worktree API have been refactored to pass a
repository pointer down the call chain, allowing them to drop
references to the global 'the_repository' variable.  As part of this,
the handling of the 'core.packedRefsTimeout' configuration has been
moved into the per-repository ref store structure.

* ps/refs-wo-the-repository:
  refs: remove remaining uses of `the_repository`
  worktree: pass repository to public functions
  worktree: pass repository to file-local functions
  worktree: refactor code to use available repositories
  refs/files: drop `USE_THE_REPOSITORY_VARIABLE`
  refs/packed: de-globalize handling of "core.packedRefsTimeout"
2026-07-27 09:08:59 -07:00
Junio C Hamano f931547dff Merge branch 'jc/submodule-helper-avoid-zu'
An accidental use of the '%zu' format specifier in 'git
submodule--helper' has been corrected to use 'PRIuMAX' and cast the
value to 'uintmax_t' to avoid portability issues.

* jc/submodule-helper-avoid-zu:
  submodule--helper: avoid use of %zu for now
2026-07-27 09:08:58 -07:00
Junio C Hamano fbf123a028 Merge branch 'ps/shift-root-in-graph'
'git log --graph' has been modified to visually distinguish parentless
'root' commits (and commits that become roots due to history
simplification) by indenting them, preventing them from appearing
falsely related to unrelated commits rendered immediately above them.

* ps/shift-root-in-graph:
  graph: add --[no-]graph-indent and log.graphIndent
  graph: move config reading into graph_read_config()
  graph: wrap cascading commits after 4 columns
  graph: indent visual root in graph
  graph: add a 2 commit buffer for lookahead
  revision: add next_commit_to_show()
  lib-log-graph: move check_graph function
2026-07-27 09:08:58 -07:00
Phillip Wood 4a3a8ee96c rebase: remember fixup -c after skipping fixup/squash
When the final command in a chain of "fixup" and "squash" commands
is skipped, we should prompt the user to edit the commit message
if the chain contains a "fixup -c" command that was not skipped.
Unfortunately, commit_staged_changes() only looks for completed "squash"
commands and so does not prompt the user to edit the message. Fix
this by recording whether a fixup command has the "-c" flag set and
then checking whether we have seen either a "fixup -c" or a "squash"
command. Add regression tests for skipping a command in the middle
of the chain (which currently works but has no test coverage), and
for skipping the final command (which is fixed by this patch).

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-26 14:02:01 -07:00
Phillip Wood 2f5ff2c339 rebase -i: fix counting of fixups after rebase --skip
When the sequencer processes a chain of "fixup" and "squash" commands
it keeps a list of the commands that have been executed. If there are
conflicts, then the list is saved when the rebase stops for the user to
resolve them. When the rebase resumes, the list is loaded and is used
to initialize the count of how many "fixup" and "squash" commands have
been processed; if a command has been skipped with "git rebase --skip",
then the last command needs to be popped off the end of the list.

To count the number of commands, commit_staged_changes() uses the
number of newlines in the file plus one. This is due to the slightly
unusual way the list is constructed - instead of appending a newline
when a command is added, a newline is inserted before the command
if the current count is greater than zero. Therefore, when we pop a
skipped command off the list, we should also remove the newline that
precedes it. Otherwise, when a new command is added, a blank line
will be left before it, which will contribute to the fixup count the
next time the file is read. Unfortunately, the preceding newline is
not removed, leading to an incorrect count. Fix this by removing the
newline that appears before the skipped command.

In addition to fixing the code that removes a skipped command from the
list, the code that reads the list is fixed to skip blank lines. We
have had reports of users starting a rebase with one version of
git and continuing it with another. Often this happens because the
version of git bundled with an IDE or TUI differs from the one used
at the command line. By fixing both the reading and writing ends of
the problem we ensure the count is correct when an older version of
git reads the fixup file written by a newer version and vice versa.

Triggering the incorrect count requires the user to skip two "fixup" or
"squash" commands before the final command in the chain. An existing
test is extended to prevent future regressions. The consequence of
miscounting is not serious: we just print the wrong count in the
header of the commit message template.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-26 14:02:01 -07:00
Jeff King 1a1579c42d ci: bump ubuntu image version for static-analysis job
We recently ran into a case[1] where old versions of coccinelle ran very
slowly, but newer ones are fine. The version we use in GitHub's CI was
the old slow version, leading to timeouts of the static-analysis job.

We get the old version because we ask for the ubuntu-22.04 image. That
has coccinelle 1.1.1, but the "fast" improvement is in coccinelle 1.3.0,
specifically their 58619b8fe (break up envs for e1 & e2, 2024-08-18).

Bumping to ubuntu-25.10 would be enough to get that new version. But I
don't see any need to ask for a specific version at all. We originally
used a specific version because coccinelle wasn't available in ubuntu
20.04, so we pinned to 18.04 in d051ed77ee (.github/workflows/main.yml:
run static-analysis on bionic, 2021-02-08). Later that got bumped in
ef46584831 (ci: update 'static-analysis' to Ubuntu 22.04, 2022-08-23)
when 18.04 support was dropped.

It seems like the absence of coccinelle was a blip in 20.04, and we can
just stick with "latest" going forward.

I tested the result on GitHub's CI. I bumped the matching line in the
GitLab definition, but didn't have a simple means of testing (but it's
such a trivial change nothing could go wrong, right?).

[1] https://lore.kernel.org/git/20260724091152.27794-2-tnyman@openai.com/

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-26 09:36:17 -07:00
Jeff King ae0780def7 bloom: silence CHECK_ASSERTION_SIDE_EFFECTS false positive
Using gcc 15, compiling with CHECK_ASSERTION_SIDE_EFFECTS=1 causes a
complaint about this line in bloom.c having a side effect:

	assert(version == 1 || version == 2);

I think this is pretty clearly a false positive, as those comparisons
should not have side effects. The side-effect checker uses a magic
definition of assert() that relies on the compiler's optimizer to drop a
reference to an otherwise unused variable. And for whatever reason, gcc
chooses not to do so here under -O2 (side note: if you have -O0 in your
CFLAGS, that naturally creates many more false positives!).

This code has been around for a while, but nobody seems to have noticed
because we use an older version of the compiler in our static-analysis
ci job, and it does not complain. Presumably very few people run this
check locally on their more modern compilers.

Let's silence the false positive to avoid confusion for anyone running
locally, and to make it possible to upgrade the image we use for our
static-analysis job.

We could just switch to our custom ASSERT() here, but I think we can
improve the code by integrating the assertion into the if/else cascade.
That avoids repeating the logic about which versions are acceptable.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-26 09:36:17 -07:00
Junio C Hamano 244d577d93 Merge branch 'ps/odb-move-loose-object-writing' into next
The logic to write loose objects has been refactored and moved from
'object-file.c' to the loose backend source file 'odb/source-loose.c',
making the loose backend more self-contained.  This is achieved by
first refactoring force_object_loose() to use generic ODB write
interfaces instead of loose-backend internals.

* ps/odb-move-loose-object-writing:
  object-file: move logic to write loose objects
  object-file: move `force_object_loose()`
  object-file: force objects loose via generic interface
  object-file: fix memory leak in `force_object_loose()`
  odb: support setting mtime when writing objects
  odb: lift object existence check out of the "loose" backend
  odb: compute object hash in `odb_write_object_ext()`
  t/u-odb-inmemory: implement wrapper for writing objects
  odb: compute compat object ID in `odb_write_object_ext()`
2026-07-25 14:37:16 -07:00
Junio C Hamano 5b92e8a325 Merge branch 'tl/gitweb-shorten-hashes-with-modes' into next
The object ID shortening and linking in the 'commitdiff' view of
'gitweb' has been corrected to work even when the index line carries
a trailing file mode.

* tl/gitweb-shorten-hashes-with-modes:
  gitweb: shorten index hashes with trailing file modes
2026-07-25 14:37:16 -07:00
Junio C Hamano 8829f9b348 Merge branch 'jk/diff-relative-cached-unmerged' into next
'git diff --relative' running with '--cached' has been corrected to
avoid a segfault when encountering unmerged paths outside the
prefix.

* jk/diff-relative-cached-unmerged:
  diff: ignore unmerged paths outside prefix with --relative --cached
2026-07-25 14:37:16 -07:00
Junio C Hamano 85755d1d45 Merge branch 'jc/exclude-first-parent-seen' into next
Traversals with '--exclude-first-parent-only' have been corrected to
properly stop after the first parent even when it has already been
marked as SEEN.

* jc/exclude-first-parent-seen:
  revision: honor --exclude-first-parent-only with SEEN first parent
2026-07-25 14:37:15 -07:00
Junio C Hamano 9b776656fc Merge branch 'hn/url-push-tracking' into next
When the push remote is specified as a URL, the fetch refspec of a
uniquely matching configured remote is now used to find and update
the remote-tracking branch (e.g., '@{push}').

* hn/url-push-tracking:
  remote: find tracking branches for URL push destinations
  remote: pass repository to push tracking helper
2026-07-25 14:37:15 -07:00
Harald Nordgren d1d403ccff branch: add --dry-run for --delete-merged
"git branch --dry-run --delete-merged ..." prints one line per ref that
would be deleted without modifying refs or branch configuration.

--dry-run is only meaningful together with --delete-merged and is
rejected otherwise.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-25 10:15:04 -07:00
Harald Nordgren 4ef02bc803 branch: add branch.<name>.deleteMerged opt-out
Setting branch.<name>.deleteMerged=false exempts that branch from
"git branch --delete-merged", which is useful for a topic you want
to keep developing after an early round of it has been merged
upstream. Unless --quiet is given, each skip is reported so the
user knows why their topic was kept.

Explicit deletion with "git branch -d" still uses the normal merge
check and ignores this setting.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-25 10:15:04 -07:00
Harald Nordgren 75403cb3a4 branch: add --delete-merged <branch>
git branch (--delete-merged <branch>)... [<pattern>...]

deletes local branches matching the optional patterns when their
configured upstream matches one of the --delete-merged arguments and
their tip is reachable from that upstream. The work has already landed
on the upstream they track, so the local copy is no longer needed.

The option can be repeated to widen the upstream match. Keeping the
candidate patterns as positional arguments lets users bound the set of
local branches that may be deleted independently of the upstream
selection.

A branch is not deleted when:

  * it is checked out in any worktree
  * its configured upstream ref no longer exists, since a missing
    upstream is not by itself a sign of integration
  * pushing it by name to the remote configured by
    branch.<name>.remote would update its upstream, as determined by
    mapping the branch ref through that remote's fetch refspec. For
    example, a local "main" that tracks "origin/main" is kept even when
    remote.pushDefault names a fork. Right after a pull it merely looks
    fully merged.

A branch whose work is not yet merged into its upstream is silently
skipped, so one unmerged topic does not abort the whole sweep.

A branch that a surviving branch depends on through a chain of local
upstreams is also kept, so no branch is deleted out from under stacked
work. Collect this transitive set without changing the candidate set
during ref iteration: walk upstream chains from surviving branches,
visit each branch at most once, and remove the collected bases only
after the iteration completes. This makes the result independent of
ref iteration order without repeated full scans.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-25 10:15:03 -07:00
Harald Nordgren 01df07c9df branch: prepare delete_branches for a bulk caller
Teach delete_branches() a new mode for the upcoming --delete-merged
caller that checks whether a branch is merged into its upstream without
falling back to HEAD when there is no upstream. Existing callers keep
their current behavior.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-25 10:15:03 -07:00
Harald Nordgren 3b329696f9 branch: let delete_branches skip unmerged branches on bulk refusal
Add a skip-unmerged mode to delete_branches() and check_branch_commit()
so a bulk caller can silently skip branches that are not fully merged
and carry on, rather than erroring with the "use 'git branch -D'"
advice that the plain "git branch -d" path emits. Existing callers are
unaffected.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-25 10:15:03 -07:00
Harald Nordgren 271c047f1b branch: convert delete_branches() to a flags argument
delete_branches() takes separate force and quiet parameters, while
check_branch_commit() takes force. The next commits would grow this
collection further. Replace them with a single unsigned flags argument
and an enum.

Test the FORCE and QUIET bits directly from flags at each use site so
that mutating or forwarding flags cannot leave cached values stale.

No change in behavior.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-25 10:15:03 -07:00