summaryrefslogtreecommitdiff
AgeCommit message (Collapse)AuthorFilesLines
2026-01-09bloom: use repo_parse_tree()René Scharfe1-1/+1
Use the passed in repository instead of the implicit the_repository when parsing the tree. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09add-interactive: use repo_parse_tree_indirect()René Scharfe1-1/+1
1b374ad71f (add-interactive: stop using `the_repository`, 2024-12-17) replaced explicit uses of the_repository. parse_tree_indirect() uses it internally, though, so call repo_parse_tree_indirect() instead and hand it the correct repository. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09tree: add repo_parse_tree*()René Scharfe2-3/+21
Add variants of parse_tree(), parse_tree_gently() and parse_tree_indirect() that allow using an arbitrary repository. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09environment: move access to core.maxTreeDepth into repo settingsRené Scharfe9-34/+36
The config setting core.maxTreeDepth is stored in a global variable and populated by the function git_default_core_config. This won't work if we need to access multiple repositories with different values of that setting in the same process. Store the setting in struct repo_settings instead and track it separately for each repository. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: special-case index entries for symlinks with buggy sizeJohannes Schindelin1-0/+11
In https://github.com/git-for-windows/git/pull/2637, we fixed a bug where symbolic links' target path sizes were recorded incorrectly in the index. The downside of this fix was that every user with tracked symbolic links in their checkouts would see them as modified in `git status`, but not in `git diff`, and only a `git add <path>` (or `git add -u`) would "fix" this. Let's do better than that: we can detect that situation and simply pretend that a symbolic link with a known bad size (or a size that just happens to be that bad size, a _very_ unlikely scenario because it would overflow our buffers due to the trailing NUL byte) means that it needs to be re-checked as if we had just checked it out. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: emulate `stat()` a little more faithfullyJohannes Schindelin1-1/+13
When creating directories via `safe_create_leading_directories()`, we might encounter an already-existing directory which is not readable by the current user. To handle that situation, Git's code calls `stat()` to determine whether we're looking at a directory. In such a case, `CreateFile()` will fail, though, no matter what, and consequently `mingw_stat()` will fail, too. But POSIX semantics seem to still allow `stat()` to go forward. So let's call `mingw_lstat()` to the rescue if we fail to get a file handle due to denied permission in `mingw_stat()`, and fill the stat info that way. We need to be careful to not allow this to go forward in case that we're looking at a symbolic link: to resolve the link, we would still have to create a file handle, and we just found out that we cannot. Therefore, `stat()` still needs to fail with `EACCES` in that case. This fixes https://github.com/git-for-windows/git/issues/2531. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: try to create symlinks without elevated permissionsJohannes Schindelin1-2/+24
As of Windows 10 Build 14972 in Developer Mode, a new flag is supported by `CreateSymbolicLink()` to create symbolic links even when running outside of an elevated session (which was previously required). This new flag is called `SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE` and has the numeric value 0x02. Previous Windows 10 versions will not understand that flag and return an `ERROR_INVALID_PARAMETER`, therefore we have to be careful to try passing that flag only when the build number indicates that it is supported. For more information about the new flag, see this blog post: https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/ Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: add support for symlinks to directoriesKarsten Blees1-0/+164
Symlinks on Windows have a flag that indicates whether the target is a file or a directory. Symlinks of wrong type simply don't work. This even affects core Win32 APIs (e.g. `DeleteFile()` refuses to delete directory symlinks). However, `CreateFile()` with FILE_FLAG_BACKUP_SEMANTICS does work. Check the target type by first creating a tentative file symlink, opening it, and checking the type of the resulting handle. If it is a directory, recreate the symlink with the directory flag set. It is possible to create symlinks before the target exists (or in case of symlinks to symlinks: before the target type is known). If this happens, create a tentative file symlink and postpone the directory decision: keep a list of phantom symlinks to be processed whenever a new directory is created in `mingw_mkdir()`. Limitations: This algorithm may fail if a link target changes from file to directory or vice versa, or if the target directory is created in another process. It's the best Git can do, though. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: implement basic `symlink()` functionality (file symlinks only)Karsten Blees2-2/+29
Implement `symlink()`. This implementation always creates _file_ symlinks (remember: Windows discerns between symlinks pointing to directories and those pointing to files). Support for directory symlinks will be added in a subseqeuent commit. This implementation fails with `ENOSYS` if symlinks are disabled or unsupported. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: implement `readlink()`Karsten Blees2-2/+25
Implement `readlink()` by reading NTFS reparse points via the `read_reparse_point()` function that was introduced earlier to determine the length of symlink targets. Works for symlinks and directory junctions. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: allow `mingw_chdir()` to change to symlink-resolved directoriesKarsten Blees1-1/+19
If symlinks are enabled, resolve all symlinks when changing directories, as required by POSIX. Note: Git's `real_path()` function bases its link resolution algorithm on this property of `chdir()`. Unfortunately, the current directory on Windows is limited to only MAX_PATH (260) characters. Therefore using symlinks and long paths in combination may be problematic. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: support renaming symlinksKarsten Blees1-22/+16
Older MSVCRT's `_wrename()` function cannot rename symlinks over existing files: it returns success without doing anything. Newer MSVCR*.dll versions probably do not share this problem: according to CRT sources, they just call `MoveFileEx()` with the `MOVEFILE_COPY_ALLOWED` flag. Avoid the `_wrename()` call, and go with directly calling `MoveFileEx()`, with proper error handling of course. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: handle symlinks to directories in `mingw_unlink()`Karsten Blees1-1/+8
The `_wunlink()` and `DeleteFileW()` functions refuse to delete symlinks to directories on Windows; The error code would be `ERROR_ACCESS_DENIED` in that case. Take that error code as an indicator that we need to try `_wrmdir()` as well. In the best case, it will remove a symlink. In the worst case, it will fail with the same error code again. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: add symlink-specific error codesKarsten Blees1-0/+5
The Win32 API calls do not set `errno`; Instead, error codes for failed operations must be obtained via the `GetLastError()` function. Git would not know what to do with those error values, though, which is why Git's Windows compatibility layer translates them to `errno` values. Let's handle a couple of symlink-related error codes that will become relevant with the upcoming support for symlinks on Windows. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: change default of `core.symlinks` to falseKarsten Blees1-0/+9
Symlinks on Windows don't work the same way as on Unix systems. For example, there are different types of symlinks for directories and files, and unless using a recent-ish Windows version in Developer Mode, creating symlinks requires administrative privileges. By default, disable symlink support on Windows. That is, users explicitly have to enable it with `git config [--system|--global] core.symlinks true`; For convenience, `git init` (and `git clone`) will perform a test whether the current setup allows creating symlinks and will configure that setting in the repository config. The test suite ignores system / global config files. Allow testing *with* symlink support by checking if native symlinks are enabled in MSYS2 (via setting the special environment variable `MSYS=winsymlinks:nativestrict` to ask the MSYS2 runtime to enable creating symlinks). Note: This assumes that Git's test suite is run in MSYS2's Bash, which is true for the time being (an experiment to switch to BusyBox-w32 failed due to the experimental nature of BusyBox-w32). Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: factor out the retry logicKarsten Blees1-58/+46
In several places, Git's Windows-specific code follows the pattern where it tries to perform an operation, and retries several times when that operation fails, sleeping an increasing amount of time, before finally giving up and asking the user whether to rety (after, say, closing an editor that held a handle to a file, preventing the operation from succeeding). This logic is a bit hard to use, and inconsistent: `mingw_unlink()` and `mingw_rmdir()` duplicate the code to retry, and both of them do so incompletely. They also do not restore `errno` if the user answers 'no'. Introduce a `retry_ask_yes_no()` helper function that handles retry with small delay, asking the user, and restoring `errno`. Note that in `mingw_unlink()`, we include the `_wchmod()` call in the retry loop (which may fail if the file is locked exclusively). In `mingw_rmdir()`, we include special error handling in the retry loop. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: compute the correct size for symlinks in `mingw_lstat()`Bill Zissimopoulos1-10/+104
POSIX specifies that upon successful return from `lstat()`: "the value of the st_size member shall be set to the length of the pathname contained in the symbolic link not including any terminating null byte". Git typically doesn't trust the `stat.st_size` member of symlinks (e.g. see `strbuf_readlink()`). Therefore, it is tempting to save on the extra overhead of opening and reading the reparse point merely to calculate the exact size of the link target. This is, in fact, what Git for Windows did, from May 2015 to May 2020. At least almost: some functions take shortcuts if `st_size` is 0 (e.g. `diff_populate_filespec()`), hence Git for Windows hard-coded the length of all symlinks to MAX_PATH. This did cause problems, though, specifically in Git repositories that were also accessed by Git for Cygwin or Git for WSL. For example, doing `git reset --hard` using Git for Windows would update the size of symlinks in the index to be MAX_PATH; at a later time Git for Cygwin or Git for WSL would find that symlinks have changed size during `git status` and update the index. And then Git for Windows would think that the index needs to be updated. Even if the symlinks did not, in fact, change. To avoid that, the correct size must be determined. Signed-off-by: Bill Zissimopoulos <billziss@navimatics.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: teach dirent about symlinksKarsten Blees3-13/+11
Move the `S_IFLNK` detection to `file_attr_to_st_mode()`. Implement `DT_LNK` detection in dirent.c's `readdir()` function. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: let `mingw_lstat()` error early upon problems with reparse pointsKarsten Blees1-10/+14
When obtaining lstat information for reparse points, we need to call `FindFirstFile()` in addition to `GetFileInformationEx()` to obtain the type of the reparse point (symlink, mount point etc.). However, currently there is no error handling whatsoever if `FindFirstFile()` fails. Call `FindFirstFile()` before modifying the `stat *buf` output parameter and error out if the call fails. Note: The `FindFirstFile()` return value includes all the data that we get from `GetFileAttributesEx()`, so we could replace `GetFileAttributesEx()` with `FindFirstFile()`. We don't do that because `GetFileAttributesEx()` is about twice as fast for single files. I.e. we only pay the extra cost of calling `FindFirstFile()` in the rare case that we encounter a reparse point. Please also note that the indentation the remaining reparse point code changed, and hence the best way to look at this diff is with `--color-moved -w`. That code was _not_ moved because a subsequent commit will move it to an altogether different function, anyway. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: drop the separate `do_lstat()` functionKarsten Blees1-21/+2
With the new `mingw_stat()` implementation, `do_lstat()` is only called from `mingw_lstat()` (with the function parameter `follow == 0`). Remove the extra function and the old `mingw_stat()`-specific (`follow == 1`) logic. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: implement `stat()` with symlink supportKarsten Blees1-1/+18
With respect to symlinks, the current `mingw_stat()` implementation is almost identical to `mingw_lstat()`: except for the file type (`st_mode & S_IFMT`), it returns information about the link rather than the target. Implement `mingw_stat()` by opening the file handle requesting minimal permissions, and then calling `GetFileInformationByHandle()` on it. This way, all links are resolved by the Windows file system layer. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: don't call `GetFileAttributes()` twice in `mingw_lstat()`Karsten Blees1-38/+15
The Win32 API function `GetFileAttributes()` cannot handle paths with trailing dir separators. The current `mingw_stat()`/`mingw_lstat()` implementation calls `GetFileAttributes()` twice if the path has trailing slashes (first with the original path that was passed as function parameter, and and a second time with a path copy with trailing '/' removed). With the conversion to wide Unicode, we get the length of the path for free, and also have a (wide char) buffer that can be modified. This makes it easy to avoid that extraneous Win32 API call. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09Merge branch 'js/prep-symlink-windows' into js/symlink-windowsJunio C Hamano15-41/+78
* js/prep-symlink-windows: trim_last_path_component(): avoid hard-coding the directory separator strbuf_readlink(): support link targets that exceed 2*PATH_MAX strbuf_readlink(): avoid calling `readlink()` twice in corner-cases init: do parse _all_ core.* settings early mingw: do resolve symlinks in `getcwd()` t7800: work around the MSYS path conversion on Windows t6423: introduce Windows-specific handling for symlinking to /dev/null t1305: skip symlink tests that do not apply to Windows t1006: accommodate for symlink support in MSYS2 t0600: fix incomplete prerequisite for a test case t0301: another fix for Windows compatibility t0001: handle `diff --no-index` gracefully mingw: special-case `open(symlink, O_CREAT | O_EXCL)` apply: symbolic links lack a "trustable executable bit" t9700: accommodate for Windows paths
2026-01-09trim_last_path_component(): avoid hard-coding the directory separatorKarsten Blees1-2/+2
Currently, this function hard-codes the directory separator as the forward slash. However, on Windows the backslash character is valid, too. And we want to call this function in the upcoming support for symlinks on Windows with the symlink targets (which naturally use the canonical directory separator on Windows, which is _not_ the forward slash). Prepare that function to be useful also in that context. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09strbuf_readlink(): support link targets that exceed 2*PATH_MAXJohannes Schindelin1-1/+1
The `strbuf_readlink()` function refuses to read link targets that exceed 2*PATH_MAX (even if a sufficient size was specified by the caller). The reason that that limit is 2*PATH_MAX instead of PATH_MAX is that the symlink targets do not need to be normalized. After running `ln -s a/../a/../a/../a/../b c`, the target of the symlink `c` will not be normalized to `b` but instead be much longer. As such, symlink targets' lengths can far exceed PATH_MAX. They are frequently much longer than 2*PATH_MAX on Windows, which actually supports paths up to 32,767 characters, but sets PATH_MAX to 260 for backwards compatibility. For full details, see https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation Let's just hard-code the limit used by `strbuf_readlink()` to 32,767 and make it independent of the current platform's PATH_MAX. Based-on-a-patch-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09strbuf_readlink(): avoid calling `readlink()` twice in corner-casesKarsten Blees1-3/+3
The `strbuf_readlink()` function calls `readlink()`` twice if the hint argument specifies the exact size of the link target (e.g. by passing stat.st_size as returned by `lstat()`). This is necessary because `readlink(..., hint) == hint` could mean that the buffer was too small. Use `hint + 1` as buffer size to prevent this. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09init: do parse _all_ core.* settings earlyJohannes Schindelin3-3/+5
In Git for Windows, `has_symlinks` is set to 0 by default. Therefore, we need to parse the config setting `core.symlinks` to know if it has been set to `true`. In `git init`, we must do that before copying the templates because they might contain symbolic links. Even if the support for symbolic links on Windows has not made it to upstream Git yet, we really should make sure that all the `core.*` settings are parsed before proceeding, as they might very well change the behavior of `git init` in a way the user intended. This fixes https://github.com/git-for-windows/git/issues/3414 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09mingw: do resolve symlinks in `getcwd()`Johannes Schindelin1-11/+7
As pointed out in https://github.com/git-for-windows/git/issues/1676, the `git rev-parse --is-inside-work-tree` command currently fails when the current directory's path contains symbolic links. The underlying reason for this bug is that `getcwd()` is supposed to resolve symbolic links, but our `mingw_getcwd()` implementation did not. We do have all the building blocks for that, though: the `GetFinalPathByHandleW()` function will resolve symbolic links. However, we only called that function if `GetLongPathNameW()` failed, for historical reasons: the latter function was supported for a long time, but the former API function was introduced only with Windows Vista, and we used to support also Windows XP. With that support having been dropped, we are free to call the symbolic link-resolving function right away. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09fsck: snapshot default refs before object walkElijah Newren1-40/+122
Fsck has a race when operating on live repositories; consider the following simple script that writes new commits as fsck runs: #!/bin/bash git fsck & PID=$! while ps -p $PID >/dev/null; do sleep 3 git commit -q --allow-empty -m "Another commit" done Since fsck walks objects for connectivity and then reads the refs at the end to check, this can cause fsck to get confused and think that the new refs refer to missing commits and that new reflog entries are invalid. Running the above script in a clone of git.git results in the following (output ellipsized to remove additional errors of the same type): $ ./fsck-while-writing.sh Checking ref database: 100% (1/1), done. Checking object directories: 100% (256/256), done. warning in tag d6602ec5194c87b0fc87103ca4d67251c76f233a: missingTaggerEntry: invalid format - expected 'tagger' line Checking objects: 100% (835091/835091), done. error: HEAD: invalid reflog entry 2aac9f9286e2164fbf8e4f1d1df53044ace2b310 error: HEAD: invalid reflog entry 2aac9f9286e2164fbf8e4f1d1df53044ace2b310 error: HEAD: invalid reflog entry da0f5b80d61844a6f0ad2ddfd57e4fdfa246ea68 error: HEAD: invalid reflog entry da0f5b80d61844a6f0ad2ddfd57e4fdfa246ea68 [...] error: HEAD: invalid reflog entry 87c8a5c2f6b79d9afa9e941590b9a097b6f7ac09 error: HEAD: invalid reflog entry d80887a48865e6ad165274b152cbbbed29f8a55a error: HEAD: invalid reflog entry d80887a48865e6ad165274b152cbbbed29f8a55a error: HEAD: invalid reflog entry 6724f2dfede88bfa9445a333e06e78536c0c6c0d error: refs/heads/mybranch invalid reflog entry 2aac9f9286e2164fbf8e4f1d1df53044ace2b310 error: refs/heads/mybranch: invalid reflog entry 2aac9f9286e2164fbf8e4f1d1df53044ace2b310 error: refs/heads/mybranch: invalid reflog entry da0f5b80d61844a6f0ad2ddfd57e4fdfa246ea68 error: refs/heads/mybranch: invalid reflog entry da0f5b80d61844a6f0ad2ddfd57e4fdfa246ea68 [...] error: refs/heads/mybranch: invalid reflog entry 87c8a5c2f6b79d9afa9e941590b9a097b6f7ac09 error: refs/heads/mybranch: invalid reflog entry d80887a48865e6ad165274b152cbbbed29f8a55a error: refs/heads/mybranch: invalid reflog entry d80887a48865e6ad165274b152cbbbed29f8a55a error: refs/heads/mybranch: invalid reflog entry 6724f2dfede88bfa9445a333e06e78536c0c6c0d Checking connectivity: 833846, done. missing commit 6724f2dfede88bfa9445a333e06e78536c0c6c0d Verifying commits in commit graph: 100% (242243/242243), done. We can minimize the race opportunities by taking a snapshot of refs at program invocation, doing the connectivity check, and then checking the snapshotted refs afterward. This avoids races with regular refs between fsck and adding objects to the database, though it still leaves a race between a gc and fsck. We are less concerned about folks simultaneously running gc with fsck; though, if it becomes an issue, we could lock fsck during gc. We definitely do not want to lock fsck during operations that may add objects to the object store; that would be problematic for forges. Note that refs aren't the only problem, though; reflog entries and index entries could be problematic as well. For now we punt on index entries just leaving a TODO comment, and for reflogs we use a coarse solution of taking the time at the beginning of the program and ignoring reflog entries newer than that time. That may be imperfect if dealing with a network filesystem, so we leave TODO comment for those that want to improve that handling as well. As a high level overview: * In addition to fsck_handle_ref(), which now is only a few lines long to process a ref, there's also a snapshot_ref() which is called early in the program for each ref and takes all the error checking logic. * The iterating over refs that used to be in get_default_heads() plus a loop over the arguments now appears in shapshot_refs(). * There's a new process_refs() as well that kind of looks like the old get_default_heads() though it is streamlined due to the work done by snapshot_refs(). This combination of changes modifies the output of running the script (from the beginning of this commit message) to: $ ./fsck-while-writing.sh Checking ref database: 100% (1/1), done. Checking object directories: 100% (256/256), done. warning in tag d6602ec5194c87b0fc87103ca4d67251c76f233a: missingTaggerEntry: invalid format - expected 'tagger' line Checking objects: 100% (835091/835091), done. Checking connectivity: 833846, done. Verifying commits in commit graph: 100% (242243/242243), done. While worries about live updates while running fsck is likely of most interest for forge operators, it may also benefit those with automated jobs (such as git maintenance) or even casual users who want to do other work in their clone while fsck is running. Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Jeff King <peff@peff.net> Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09packfile: move MIDX into packfile storePatrick Steinhardt5-25/+19
The multi-pack index still is tracked as a member of the object database source, but ultimately the MIDX is always tied to one specific packfile store. Move the structure into `struct packfile_store` accordingly. This ensures that the packfile store now keeps track of all data related to packfiles. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09packfile: refactor `find_pack_entry()` to work on the packfile storePatrick Steinhardt1-20/+23
The function `find_pack_entry()` doesn't work on a specific packfile store, but instead works on the whole repository. This causes a bit of a conceptual mismatch in its callers: - `packfile_store_freshen_object()` supposedly acts on a store, and its callers know to iterate through all sources already. - `packfile_store_read_object_info()` behaves likewise. The only exception that doesn't know to handle iteration through sources is `has_object_pack()`, but that function is trivial to adapt. Refactor the code so that `find_pack_entry()` works on the packfile store level instead. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09packfile: inline `find_kept_pack_entry()`Patrick Steinhardt2-24/+10
The `find_kept_pack_entry()` function is only used in `has_object_kept_pack()`, which is only a trivial wrapper itself. Inline the latter into the former. Furthermore, reorder the code so that we can drop the declaration of the function in "packfile.h". This allows us to make the function file-local. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09packfile: only prepare owning store in `packfile_store_prepare()`Patrick Steinhardt2-20/+13
When calling `packfile_store_prepare()` we prepare not only the provided packfile store, but also all those of all other sources part of the same object database. This was required when the store was still sitting on the object database level. But now that it sits on the source level it's not anymore. Refactor the code so that we only prepare the single packfile store passed by the caller. Adapt callers accordingly. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09packfile: only prepare owning store in `packfile_store_get_packs()`Patrick Steinhardt1-4/+2
When calling `packfile_store_get_packs()` we prepare not only the provided packfile store, but also all those of all other sources part of the same object database. This was required when the store was still sitting on the object database level. But now that it sits on the source level it's not anymore. Adapt the code so that we only prepare the MIDX of the provided store. All callers only work in the context of a single store or call the function in a loop over all sources, so this change shouldn't have any practical effects. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09packfile: move packfile store into object sourcePatrick Steinhardt11-145/+243
The packfile store is a member of `struct object_database`, which means that we have a single store per database. This doesn't really make much sense though: each source connected to the database has its own set of packfiles, so there is a conceptual mismatch here. This hasn't really caused much of a problem in the past, but with the advent of pluggable object databases this is becoming more of a problem because some of the sources may not even use packfiles in the first place. Move the packfile store down by one level from the object database into the object database source. This ensures that each source now has its own packfile store, and we can eventually start to abstract it away entirely so that the caller doesn't even know what kind of store it uses. Note that we only need to adjust a relatively small number of callers, way less than one might expect. This is because most callers are using `repo_for_each_pack()`, which handles enumeration of all packfiles that exist in the repository. So for now, none of these callers need to be adapted. The remaining callers that iterate through the packfiles directly and that need adjustment are those that are a bit more tangled with packfiles. These will be adjusted over time. Note that this patch only moves the packfile store, and there is still a bunch of functions that seemingly operate on a packfile store but that end up iterating over all sources. These will be adjusted in subsequent commits. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09packfile: refactor misleading code when unusing pack windowsPatrick Steinhardt1-6/+5
The function `unuse_one_window()` is responsible for unmapping one of the packfile windows, which is done when we have exceeded the allowed number of window. The function receives a `struct packed_git` as input, which serves as an additional packfile that should be considered to be closed. If not given, we seemingly skip that and instead go through all of the repository's packfiles. The conditional that checks whether we have a packfile though does not make much sense anymore, as we dereference the packfile regardless of whether or not it is a `NULL` pointer to derive the repository's packfile store. The function was originally introduced via f0e17e86e1 (pack: move release_pack_memory(), 2017-08-18), and here we indeed had a caller that passed a `NULL` pointer. That caller was later removed via 9827d4c185 (packfile: drop release_pack_memory(), 2019-08-12), so starting with that commit we always pass a `struct packed_git`. In 9c5ce06d74 (packfile: use `repository` from `packed_git` directly, 2024-12-03) we then inadvertently started to rely on the fact that the pointer is never `NULL` because we use it now to identify the repository. Arguably, it didn't really make sense in the first place that the caller provides a packfile, as the selected window would have been overridden anyway by the subsequent loop over all packfiles if there was an older window. So the overall logic is quite misleading overall. The only case where it _could_ make a difference is when there were two packfiles with the same `last_used` value, but that case doesn't ever happen because the `pack_used_ctr` is strictly increasing. Refactor the code so that we instead pass in the object database to help make the code less misleading. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09packfile: refactor kept-pack cache to work with packfile storesPatrick Steinhardt5-36/+48
The kept pack cache is a cache of packfiles that are marked as kept either via an accompanying ".kept" file or via an in-memory flag. The cache can be retrieved via `kept_pack_cache()`, where one needs to pass in a repository. Ultimately though the kept-pack cache is a property of the packfile store, and this causes problems in a subsequent commit where we want to move down the packfile store to be a per-object-source entity. Prepare for this and refactor the kept-pack cache to work on top of a packfile store instead. While at it, rename both the function and flags specific to the kept-pack cache so that they can be properly attributed to the respective subsystems. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09packfile: pass source to `prepare_pack()`Patrick Steinhardt1-9/+5
When preparing a packfile we pass various pieces attached to the pack's object database source via the `struct prepare_pack_data`. Refactor this code to instead pass in the source directly. This reduces the number of variables we need to pass and allows for a subsequent refactoring where we start to prepare the pack via the source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09packfile: create store via its owning sourcePatrick Steinhardt3-14/+14
In subsequent patches we're about to move the packfile store from the object database layer into the object database source layer. Once done, we'll have one packfile store per source, where the source is owning the store. Prepare for this future and refactor `packfile_store_new()` to be initialized via an object database source instead of via the object database itself. This refactoring leads to a weird in-between state where the store is owned by the object database but created via the source. But this makes subsequent refactorings easier because we can now start to access the owning source of a given store. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09builtin.h: update documentationDerrick Stolee1-1/+25
The documentation for the builtin API was moved from the technical documentation and into a comment in builtin.h by ec14d4ecb5 (builtin.h: take over documentation from api-builtin.txt, 2017-08-02). This documentation wasn't updated as part of the major overhaul to include a repository struct in 9b1cb5070f (builtin: add a repository parameter for builtin functions, 2024-09-13). There was a brief update regarding the move from *.txt to *.adoc by e8015223c7 (builtin.h: *.txt -> *.adoc fixes, 2025-03-03). I noticed that there was quite a bit missing from the old documentation, which is still visible on git-scm.com [1]. [1] https://github.com/git/git-scm.com/issues/2124 This change updates the documentation in the following ways: 1. Updates the cmd_foo() prototype to include a repository. 2. Adds some newlines to have uniformity in the list of flags. 3. Adds a description of the NO_PARSEOPT flag. 4. Describes the tests that perform checks on all builtins, which may trip up a contributor working on a new builtin. I double-checked these instructions against a toy example in my local branch to be sure that it was complete. Signed-off-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09t7101: modernize test path checksK Jayatheerth1-9/+9
Replace old-style `test -[df]` and `! test -[df]` assertions with the modern `test_path_is_file`, `test_path_is_dir`, and `test_path_is_missing` helpers. These helpers provide more informative error messages in case of failure (e.g., "File 'foo' is missing" instead of just exit code 1). While at it, fix a typo and an incorrect path reference in one of the test descriptions. Signed-off-by: K Jayatheerth <jayatheerthkulkarni2005@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09gitfaq: document using stash import/export to sync working treebrian m. carlson1-11/+28
Git 2.51 learned how to import and export stashes. This is a secure and robust way to transfer working tree states across machines and comes with almost none of the pitfalls of rsync or other tools. Recommend this as an alternative in the FAQ. Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09doc: git-blame: convert to new doc formatMichael Lyons1-35/+37
- Use _<placeholder>_ instead of <placeholder> in the description - Use _underscores_ around math associated with <placeholders> - Use `backticks` for keywords and more complex option descriptions. The new rendering engine will apply synopsis rules to these spans. Signed-off-by: Michael Lyons <git@michael.lyo.nz> Acked-by: Jean-Noël AVILA <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09doc: blame-options: convert to new doc formatMichael Lyons1-60/+60
- Use _<placeholder>_ instead of <placeholder> in the description - Modify some samples to use <placeholders> - Use `backticks` for keywords and more complex option descriptions. The new rendering engine will apply synopsis rules to these spans. Signed-off-by: Michael Lyons <git@michael.lyo.nz> Acked-by: Jean-Noël AVILA <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09add -p: show user's hunk decision when selecting hunksAbraham Samuel Adekunle2-46/+53
When a user is interactively deciding which hunks to use or skip for staging, unstaging, stashing etc, there is no way to know the decision previously chosen for a hunk when navigating through the previous and next hunks using K/J respectively. Improve the UI to explicitly show if a user has previously decided to use a hunk (by pressing 'y') or skip the hunk (by pressing 'n'). This will improve clarity when and aid the navigation process for the user. Reported-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Abraham Samuel Adekunle <abrahamadekunle50@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09doc: patch-id: --verbatim locks in --stableKristoffer Haugsbakk1-1/+1
The default `--unstable` is a legacy format that predates `--stable`. That’s why 2871f4d4 (builtin: patch-id: add --verbatim as a command mode, 2022-10-24) made `--verbatim` lock in[1] `--stable`: Users of --unstable mainly care about compatibility with old git versions, which unstripping the whitespace would break. Thus there isn't a usecase for the combination of --verbatim and --unstable, and we don't expose this so as to not add maintainence burden. † 1: imply `--stable`, disallow `--unstable` Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09doc: patch-id: spell out the git-diff-tree(1) formKristoffer Haugsbakk1-1/+1
You specifically need `--patch` since the default output is a raw diff. Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09doc: patch-id: use definite article for the resultKristoffer Haugsbakk1-1/+1
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09patch-id: use “patch ID” throughoutKristoffer Haugsbakk2-7/+7
The “Description” section decided to introduce and use the term “patch ID” for the ID value itself. Let’s use the same term on the options as well. Also make to sure to use bare “ID” instead of “id”. Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-09doc: patch-id: capitalize Git versionKristoffer Haugsbakk1-3/+3
Git versions are always capitalized. Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>