Skip to content

Migrate the automation of the merging-rebases of the shears/* branches from Azure Pipelines to GitHub workflows#152

Merged
dscho merged 29 commits into
mainfrom
rebase-shears
Feb 22, 2026
Merged

Migrate the automation of the merging-rebases of the shears/* branches from Azure Pipelines to GitHub workflows#152
dscho merged 29 commits into
mainfrom
rebase-shears

Conversation

@dscho

@dscho dscho commented Feb 17, 2026

Copy link
Copy Markdown
Member

The shears/* branches (also known as "ever-green" branches) maintain Git for Windows' downstream patches, continuously rebased, on top of upstream Git's master, next, seen and maint branches. By always reflecting the current state of upstream, we are notified early of any problems between our downstream patches and in-flight upstream changes. This also helps identify upstream regressions that only surface in our downstream testing, and since we rebase continuously, we can react much more quickly than if we waited for upstream releases.

Until now, these rebases were handled by two Azure Pipelines (triggered by Git for Windows' main updates and triggered by upstream branch updates). This was the last piece of Git for Windows' automation still running on Azure Pipelines. After converting the Coverity workflow, I was eager to finish the migration, but it took over a year to get started on the shears automation. AI assistance (in the beginning, Claude Sonnet, recently Claude Opus) made that feasible by doing much of the work in the background — slower than if I would have done it myself, but not on my clock, and much of my feedback was dictated using AI without much in the way of bothering to correct typos (modern LLMs are quite lenient with typos, saving my time).

This PR adds a GitHub Actions workflow, a merge conflict resolving "agent", and a POSIX shell script that together automate the full merging-rebase cycle:

  • Rebase script (rebase-branch.sh): creates a worktree, finds the merging-rebase marker, syncs with origin/main, builds correspondence maps (upstream, sibling, previous) to skip or reuse resolutions for already-upstreamed patches, and runs the interactive rebase with automatic trivial-skip detection.
  • AI conflict resolution: when a non-trivial conflict is encountered, the script invokes GitHub Copilot CLI with a carefully scoped prompt and tool permissions. The AI can inspect patches, run git range-diff and git log -L to understand how upstream modified conflicting lines, and either skip the commit (reporting a corresponding upstream commit's OID, or a reason), surgically resolve the conflict, or report failure. A build verification step catches resolution mistakes and gives the AI a second chance.
  • Robust parsing: the decision parser handles GitHub Copilot's trailing stats output, case-insensitive keywords, bare skips for obsolete patches, and skip -- <reason> for patches made redundant by upstream changes without a directly corresponding upstream version of said patch.
  • Reporting: each run produces a GitHub Actions step summary with linked commit ranges, per-conflict entries (skipped, resolved, failed), a statistics table, and a collapsible range-diff.
  • Running on a schedule (smartly skipping the rebases if appropriate): the workflow runs every six hours, but avoids expensive clones when nothing has changed by recording observed ref tips in the Check Run output via the Checks API. Subsequent runs compare current refs against the previous run's recorded state and skip entirely if nothing moved, with a notice linking back to the original run that performed the last actual rebase.

Note: This is not a straight conversion of the Azure Pipelines, but instead a rewrite from scratch, mostly written by Claude Opus 4.6 (and very closely monitored by me).

Features that were added in the process:

  • Nice reports
  • Resolution via sibling branches or pre-rebase commits
  • Conflict resolution via AI
  • A build check after resolving conflicts

Note: To my great disappointment, I had to move from build checks running on Windows (which would be incredibly helpful) to running this entire workflow on Linux, including the test builds. The reason is that there are just too many rough edges around trying to run GitHub Copilot on Windows. My initial attempt with https://github.com/github/gh-aw caused utter frustration when I realized that it simply falls over its own feet, badly, already very early, when you dare to try this with Windows runners. But even after switching to "real" GitHub workflows (which gh-aw "compiles" to, requiring you to do this locally after every change and committing it, anyway, an improvable flow if you ask me), there were just too many problems with apparently too few engineers having to handle all of them (a pattern I observed previously at that company), with github/copilot-cli#1401 being the nail in the coffin. In the wise words of Representative Binks: My give up. Maybe at some stage I might try again to get this whole thing working on Windows, it should work fine, it Works On My Machine, after all, but for now I need to heal the scars.

In any case, I am quite hopeful that this workflow will help with Git for Windows' maintenance quite a bit, as one of the most tedious things is trying to fix things when upstream patches break Git for Windows functionality that worked for years. My expectation is that this automation will be able to handle quite a bit more of that load than the previous automation could.

@dscho
dscho requested review from mjcheetham and rimrul February 17, 2026 14:02
@dscho dscho self-assigned this Feb 17, 2026
@dscho

dscho commented Feb 17, 2026

Copy link
Copy Markdown
Member Author

For the record: https://github.com/git-for-windows/git-for-windows-automation/actions/runs/22094271117 is a run of an unpolished version of this automation. It did rebase shears/seen and shears/main, but did not require any fancy merge conflict resolution. https://github.com/git-for-windows/git/actions/runs/21928061487 shows an even earlier version (back when, I still thought that it might live in git instead of git-for-windows-automation). The merge-conflict resolution "agent" is something I started developing even earlier, when I encountered some intermediate-level merge conflicts during a manual rebase of shears/seen.

I have no illusions about this automation working as intended for anything but relatively trivial cases. My hope is that I can get it to the point where it handles more and more complex scenarios over time, incrementally, when the occasion arises (read: when merge conflicts cause this workflow to fail).

@mjcheetham

Copy link
Copy Markdown
Member

Could we try running this against prior rebase efforts that did produce more complicated merge conflicts and see how it did? Maybe even provide prior conflict-resolutions as examples for it to learn from? A kind of rerere for the rebasing agent?

Comment thread rebase-branch.sh
# shears-branch - The branch to rebase (e.g., shears/seen)
# upstream-branch - The upstream branch to rebase onto (e.g., upstream/seen)
#
# Preconditions:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another precondition is the presence of the origin remote that we want to operate on ;)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hoped this would be implicitly clear by the precondition that origin/shears/<name> must be fetched already...

Comment thread rebase-branch.sh
You may want to examine these with 'git show <oid>' for hints on how to resolve."
fi

prompt="Resolve merge conflict during rebase of commit REBASE_HEAD.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we know that this invocation is using the conflict-resolver.agent.md AGENTS.md file, and not another in the .github/agents directory (should another one be added in the future)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we know [...]

@mjcheetham we do not. I do not currently see any way to enforce that :-(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to specify agent instructions to preload using the --agent flag. It takes the agent id (the part before the .agent.md in the filename)

So in this case copilot --agent conflict-resolver --prompt "Resolve merge conflict during rebase of commit REBASE_HEAD."should do the trick.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that's nice! @niik is there some sort of search path for the agents, or a way to provide a relative path so as to avoid having a potential future ~/.copilot/agents/conflict-resolver/ interfere?

Comment thread rebase-branch.sh
echo "::endgroup::"
echo "::warning::Build failed after conflict resolution, giving AI another chance"

retry_prompt="Build failed after your conflict resolution. Fix the compilation error.

@mjcheetham mjcheetham Feb 17, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's just me, but I always feel a little weirded out by mixing the prompts within the shell script. Is it possible to extract the prompts to their own 'prompts/retry.txt' and 'prompts/rebase.txt' files?

I realise also that these prompts are being dynamically built with interpolated $var shell variables (which also makes me concerned).

To move the prompts out would require more stringent replacement markers like "Run <<<CHECK_CMD>>> to check something first." or something.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjcheetham my original plan was that, but I encountered a bug and I'm wary to reopen that can of worms again because sometimes I do want to be able to debug this on Windows, even if it doesn't run on Windows in CI.

@mjcheetham mjcheetham left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (conflict resolution) is a very interesting and clever use of AI agents! Just a few comments around keeping prompts separate from the shell scripts.

@dscho

dscho commented Feb 17, 2026

Copy link
Copy Markdown
Member Author

Could we try running this against prior rebase efforts that did produce more complicated merge conflicts and see how it did? Maybe even provide prior conflict-resolutions as examples for it to learn from? A kind of rerere for the rebasing agent?

Good idea! I did exactly that. In particular, I went through my shears/next's reflog and found a moderately complex one, where the range-diff between the pre-/post-rebase state has a couple of non-trivial things in it. Concretely, before the rebase, the tip commit was d643eef37bb745080a1820ad88e803bdcb8e361e, afterwards it was d7bf620e09cf03f27c6e2d014adf4e72eb11eb82. Here is the range-diff between those:

Range-diff between pre-/post-rebase tip commits
  • -: ------------ > 1: b60f7d890dee submodule--helper: use submodule_name_to_gitdir in add_submodule

  • -: ------------ > 2: 05a1cdb5255b submodule: always validate gitdirs inside submodule_name_to_gitdir

  • -: ------------ > 3: 34206caaf7c5 builtin/submodule--helper: add gitdir command

  • -: ------------ > 4: 4173df5187c8 submodule: introduce extensions.submodulePathConfig

  • -: ------------ > 5: c349bad72969 submodule: allow runtime enabling extensions.submodulePathConfig

  • -: ------------ > 6: e14349d58eea submodule--helper: add gitdir migration command

  • -: ------------ > 7: 226694bdf4aa builtin/credential-store: move is_rfc3986_unreserved to url.[ch]

  • -: ------------ > 8: 920fbe4d4ee8 submodule--helper: fix filesystem collisions by encoding gitdir paths

  • -: ------------ > 9: 1685bba838ac submodule: fix case-folding gitdir filesystem collisions

  • -: ------------ > 10: 82c36fa0a987 submodule: hash the submodule name for the gitdir path

  • -: ------------ > 11: e897c9b7f31c submodule: detect conflicts with existing gitdir configs

  • -: ------------ > 12: 1954b943227f t5403: introduce check_post_checkout helper function

  • -: ------------ > 13: 7a747f972d73 t5403: use test_cmp for post-checkout argument checks

  • -: ------------ > 14: 81021871eaa8 doc: MyFirstContribution: fix missing dependencies and clarify build steps

  • -: ------------ > 15: 1454743eb84e builtin/replay: extract core logic to replay revisions

  • -: ------------ > 16: 6aeda3cf5b6d builtin/replay: move core logic into "libgit.a"

  • -: ------------ > 17: 410e3780602a replay: small set of cleanups

  • -: ------------ > 18: 5425771568ee replay: support empty commit ranges

  • -: ------------ > 19: 48a72f61f04c replay: support updating detached HEAD

  • -: ------------ > 20: 475ade1cd6e8 wt-status: provide function to expose status for trees

  • -: ------------ > 21: a675183d483c builtin: add new "history" command

  • -: ------------ > 22: d205234cb05a builtin/history: implement "reword" subcommand

  • -: ------------ > 23: 0ee71f4bd035 replay: drop commits that become empty

  • -: ------------ > 24: ed0f7a62f752 remote-curl: use auth for probe_rpc() requests too

  • -: ------------ > 25: f85b49f3d4af diff: improve scaling of filenames in diffstat to handle UTF-8 chars

  • -: ------------ > 26: 04f5d95ef771 t4073: add test for diffstat paths length when containing UTF-8 chars

  • -: ------------ > 27: b143f0f60816 last-modified: clarify in the docs the command takes a pathspec

  • -: ------------ > 28: 209574de2d2a last-modified: document option '-z'

  • -: ------------ > 29: 9bfaf78cb28b last-modified: document option '--max-depth'

  • -: ------------ > 30: 9dcc09bed13a last-modified: change default max-depth to 0

  • -: ------------ > 31: 49223593fd74 sparse-checkout: optimize string_list construction and add tests to verify deduplication.

  • -: ------------ > 32: a824421d3644 t5500: simplify test implementation and fix git exit code suppression

  • -: ------------ > 33: 83a705b6879c git-gui i18n: Update Bulgarian translation (557t)

  • -: ------------ > 34: ea24e2c55433 A bit more before -rc2

  • -: ------------ > 35: 539e6337b82e git-gui i18n: Update Bulgarian translation (558t)

  • -: ------------ > 36: 1a729ccb930f git-gui: mark *.po files at any directory level as UTF-8

  • -: ------------ > 37: ab689ea7f91a Revert "Merge branch 'cs/rebased-subtree-split'"

  • -: ------------ > 38: c9aea168b5fc t/t5571-prep-push-hook.sh: Add test with writing to stderr

  • -: ------------ > 39: ab380cb80b07 Git 2.53-rc2

  • -: ------------ > 40: ea717645d199 RelNotes: a few spelling fixes

  • 1: e82166f36790 = 41: 72f5de3454f0 sideband: mask control characters

  • 2: 7f6b38b4427e = 42: 69ad7769d91b sideband: introduce an "escape hatch" to allow control characters

  • 3: 0e03c0f1bc94 = 43: ed536c92320e sideband: do allow ANSI color sequences by default

  • 4: 9ff5babbb02e = 44: 2cd5611bd9c6 unix-socket: avoid leak when initialization fails

  • 5: ac934bbfd087 = 45: 154c3b8f2c8e grep: prevent ^$ false match at end of file

  • 6: 320f1b1809e7 = 46: 61ca56afb7ea t9350: point out that refs are not updated correctly

  • 7: ce4d3aea4f4c = 47: ae83d576a7d0 transport-helper: add trailing --

  • 8: 4a6a78d87c70 = 48: baaf71713bf5 remote-helper: check helper status after import/export

  • 10: 45b6b99828e5 = 49: 579319b620a4 clean: do not traverse mount points

  • 9: 3f84ceedc036 = 50: f0d37931c68a Always auto-gc after calling a fast-import transport

  • 12: cee44a5fa415 = 51: f5f40f3b121a mingw: prevent regressions with "drive-less" absolute paths

  • 11: e7a96277b1f2 = 52: 5d6daeebfb78 mingw: include the Python parts in the build

  • 14: 59ea40c352d9 = 53: 2f738e48d2b0 win32/pthread: avoid name clashes with winpthread

  • 15: a52ae9fa938b = 54: 0f84586a4735 git-compat-util: avoid redeclaring _DEFAULT_SOURCE

  • 16: bbc48c5bae6f = 55: e7cdf876a90c Import the source code of mimalloc v2.2.7

  • 17: eecd18f3f895 = 56: 2f285aea9f17 mimalloc: adjust for building inside Git

  • 18: 01efc868b38d = 57: 7e3722cc824f mimalloc: offer a build-time option to enable it

  • 23: 58239f03efbd = 58: 299cf470515c mingw: demonstrate a git add issue with NTFS junctions

  • 19: 58d70415b70a = 59: 9c43955fed47 mingw: use mimalloc

  • 13: 61c878ae4db2 = 60: ee43a66a8439 clean: remove mount points when possible

  • 20: 50bfcfc89558 = 61: 6f6e8f424500 transport: optionally disable side-band-64k

  • 21: ea6543b9744d = 62: 2c41f19e894d mingw: fix fatal error working on mapped network drives on Windows

  • 22: a952c45dd0aa = 63: 963231342a49 clink.pl: fix MSVC compile script to handle libcurl-d.lib

  • 33: 39005b0efd49 = 64: 71dccee64376 vcpkg_install: detect lack of Git

  • 34: a27980d81e75 = 65: ea8a6b7eda22 vcpkg_install: add comment regarding slow network connections

  • 25: 3349f03719dd = 66: ddf039e5ffee strbuf_realpath(): use platform-dependent API if available

  • 24: b919338daba9 = 67: 14dc73634a97 t5505/t5516: allow running without .git/branches/ in the templates

  • 35: b27856b179a3 = 68: e94d8273ab34 vcbuild: install ARM64 dependencies when building ARM64 binaries

  • 26: 34574299ba7c = 69: 69135d81fd7d http: use new "best effort" strategy for Secure Channel revoke checking

  • 28: caaa2fd75491 = 70: 065aaec6464e mingw: implement a platform-specific strbuf_realpath()

  • 27: 3b5160907555 = 71: db0e354abb91 t5505/t5516: fix white-space around redirectors

  • 29: 564b13356342 = 72: 0924bbd7b1af t3701: verify that we can add lots of files interactively

  • 30: 210a84addb11 = 73: d00df93e0aaa commit: accept "scissors" with CR/LF line endings

  • 31: 55ff7164968b = 74: 26563b25dece t0014: fix indentation

  • 32: b446d3b38402 = 75: 63e838f85932 git-gui: accommodate for intent-to-add files

  • 36: 945a99aeb9fc = 76: c3c19beffc96 vcbuild: add an option to install individual 'features'

  • 37: 55953e90a03b = 77: 7262616276ed cmake: allow building for Windows/ARM64

  • 38: f0fa090298d3 = 78: 8bea01b12e71 ci(vs-build) also build Windows/ARM64 artifacts

  • 39: 574692372863 = 79: 18aa03feffe9 Add schannel to curl installation

  • 40: b0d76b0511a5 = 80: 702dc2683b5c cmake(): allow setting HOST_CPU for cross-compilation

  • 44: 242fcca43655 = 81: 88a65bf26e6c CMake: default Visual Studio generator has changed

  • 50: a722b8e77910 = 82: 1c0fe1a018d9 hash-object: demonstrate a >4GB/LLP64 problem

  • 45: 8ba264dcb665 = 83: ee8cdf0cd7eb .gitignore: add Visual Studio CMakeSetting.json file

  • 51: 22559ffbb067 = 84: da87446dceb5 object-file.c: use size_t for header lengths

  • 46: 7a82087f390f = 85: 6057555be577 subtree: update contrib/subtree test target

  • 47: af2766012753 = 86: 24849e3602f7 CMakeLists: add default "x64-windows" arch for Visual Studio

  • 52: 17b6146b068f = 87: d5fab9ba0cda hash algorithms: use size_t for section lengths

  • 41: 1fd3b0e10649 = 88: f154867f2566 mingw: allow for longer paths in parse_interpreter()

  • 42: 22f09f3b42eb = 89: 92b17b15d057 compat/vcbuild: document preferred way to build in Visual Studio

  • 43: 0a5babc2939a = 90: 6d4c9d6bccd4 http: optionally send SSL client certificate

  • 48: 9c0023f0d5c5 = 91: 757a2c9bd586 ci: run contrib/subtree tests in CI builds

  • 49: e77ad2d5e477 = 92: 23246b4397a8 CMake: show Win32 and Generator_platform build-option values

  • 53: a8855bfa8a5b = 93: 28101c6ae052 hash-object --stdin: verify that it works with >4GB/LLP64

  • 54: 994f5cd7bb93 = 94: 770dec44be09 hash-object: add another >4GB/LLP64 test case

  • 55: 0e6d57f91c18 = 95: dbcd7123712a setup: properly use "%(prefix)/" when in WSL

  • 57: 3da0980d6eb8 = 96: 10f0986c6357 Add config option windows.appendAtomically

  • 56: 73db3918ce8b = 97: 9ea106018ca8 hash-object: add a >4GB/LLP64 test case using filtered input

  • 58: 86451598b3f1 = 98: d8f746f542d2 compat/mingw.c: do not warn when failing to get owner

  • 59: 7dc21531041c = 99: cb452cf7584c mingw: $env:TERM="xterm-256color" for newer OSes

  • 60: 69033434f248 = 100: 53be802501cb winansi: check result and Buffer before using Name

  • 61: 98669c76126f = 101: 6a8a5a922131 mingw: change core.fsyncObjectFiles = 1 by default

  • 63: 9ba56a9c13f9 = 102: 4f2721c77896 MinGW: link as terminal server aware

  • 64: 1011d6781048 = 103: 4337ef00a176 Fix Windows version resources

  • 65: 6d3efc8138ef = 104: 906ddd07a915 status: fix for old-style submodules with commondir

  • 62: 4cd1f699d8b7 = 105: 55d10664920c windows: skip linking git-<command> for built-ins

  • 66: cc5f280125b3 = 106: dd3b541d21d7 mingw: stop hard-coding CC = gcc

  • 67: 8390789c84d5 = 107: 6855ef0b350b mingw: drop the -D_USE_32BIT_TIME_T option

  • 68: 0dbf9aff7f19 = 108: fcae0f80ebd9 mingw: only use -Wl,--large-address-aware for 32-bit builds

  • 69: 79134fb060f2 = 109: fb3404d94af5 mingw: avoid over-specifying --pic-executable

  • 70: 27317db12f6c = 110: 3471074861fd mingw: set the prefix and HOST_CPU as per MSYS2's settings

  • 71: 65faf0effe16 = 111: 75886a3bc8e4 mingw: only enable the MSYS2-specific stuff when compiling in MSYS2

  • 72: 2d6ea997dfa3 = 112: 71c1841f0e93 mingw: rely on MSYS2's metadata instead of hard-coding it

  • 73: f4bf61576494 = 113: d1f0a5ba34e0 mingw: always define ETC_* for MSYS2 environments

  • -: ------------ > 114: e23ad91110c1 max_tree_depth: lower it for clang builds in general on Windows

  • 83: 9259206f1326 = 115: 7af555c5b979 http: optionally load libcurl lazily

  • 85: 26c7465b70aa = 116: 5ef2a8d03ff8 http: support lazy-loading libcurl also on Windows

  • 95: 3b45e522514c = 117: 7238dd9bc115 ci: work around a problem with HTTP/2 vs libcurl v8.10.0

  • 87: 4c02603e3ae7 = 118: dd918bd51d5e http: when loading libcurl lazily, allow for multiple SSL backends

  • 96: 1b9ef4d89366 = 119: ad2b49eaab35 revision: create mark_trees_uninteresting_dense()

  • 88: c11f5f22c0c0 = 120: 9e5b795a9a92 mingw: do load libcurl dynamically by default

  • 89: 7e0bea240b0b = 121: 11ef03e8275c Add a GitHub workflow to verify that Git/Scalar work in Nano Server

  • 90: 76af884ab514 = 122: 0a76992d6295 mingw: suggest windows.appendAtomically in more cases

  • 91: 006abaa7ea07 = 123: 650a165ef91a win32: use native ANSI sequence processing, if possible

  • 92: 779dd5cc98a8 = 124: 9da49cabc171 common-main.c: fflush stdout buffer upon exit

  • 93: 877d7df489f3 = 125: 025e9a85c992 t5601/t7406(mingw): do run tests with symlink support

  • 94: 830a4ff43a66 = 126: e4e770cf1053 win32: ensure that localtime_r() is declared even in i686 builds

  • 98: 6eb8fbbd4af0 = 127: 3e9b12fd0d0a Fallback to AppData if XDG_CONFIG_HOME is unset

  • 99: ae45f9a61114 = 128: 70b9ce9fe105 run-command: be helpful with Git LFS fails on Windows 7

  • 97: e3915e8d31cb = 129: c99f45051c05 survey: stub in new experimental 'git-survey' command

  • 100: 7f9f714a2e97 = 130: ac99a8f35263 survey: add command line opts to select references

  • 101: e5147294c9c1 = 131: 949cf92170dd survey: start pretty printing data in table form

  • 102: 424e2adb0505 = 132: 1743754fefa7 survey: add object count summary

  • 103: 088bbc823ce6 = 133: 03f77f8e970c survey: summarize total sizes by object type

  • 104: e3a3bab23d39 = 134: 7879885db360 survey: show progress during object walk

  • 107: 239db126ea7f = 135: c797e5aa3a57 mingw: make sure errno is set correctly when socket operations fail

  • 105: d5c8387da7ad = 136: 3dcc8daf863c survey: add ability to track prioritized lists

  • 108: 556c46856c4c = 137: 6358efd8b247 compat/mingw: handle WSA errors in strerror

  • 106: b77379cf6468 = 138: 338e9b93604a survey: add report of "largest" paths

  • 109: 6d245b6e2d12 = 139: 79c8bcc69544 compat/mingw: drop outdated comment

  • 110: 4bbb97e28ecf = 140: 07beb8f5f5da survey: add --top= option and config

  • 111: 32913f4f57b4 = 141: 3ce045cd6641 t0301: actually test credential-cache on Windows

  • 112: 05b04b72001a = 142: e04c2735ea9d survey: clearly note the experimental nature in the output

  • 113: b1476e121934 = 143: fdc5dd88d808 credential-cache: handle ECONNREFUSED gracefully

  • 114: 919f4a498f99 = 144: 746fc5e0ce09 reftable: do make sure to use custom allocators

  • 115: 4a297e01bdf5 = 145: e1a6400f8db4 check-whitespace: avoid alerts about upstream commits

  • -: ------------ > 146: 541d54bc0121 t/t5571-prep-push-hook.sh: Add test with writing to stderr

  • 74: 36c1d18cf59d = 147: b53fe0e7edda mingw: ensure valid CTYPE

  • 75: 40ec81a8e15d = 148: 8e2d9a909894 mingw: allow git.exe to be used instead of the "Git wrapper"

  • 76: 6e33fa93752a = 149: 9a0a9c1ded8b mingw: ignore HOMEDRIVE/HOMEPATH if it points to Windows' system directory

  • 77: ae995ca7099b = 150: b37402aaf8cc clink.pl: fix libexpatd.lib link error when using MSVC

  • 78: 1f68d8ffe8d9 = 151: eb66711b2cba Makefile: clean up .ilk files when MSVC=1

  • 79: 43fcaeefa21e = 152: 26af83426c14 vcbuild: add support for compiling Windows resource files

  • 80: 4d343b98eaa2 = 153: 4d1cc403e039 config.mak.uname: add git.rc to MSVC builds

  • 81: 09eea6b5068c = 154: 4a2e42ab88bd clink.pl: ignore no-stack-protector arg on MSVC=1 builds

  • 82: f0b0a2fe3125 = 155: a127f482d598 clink.pl: move default linker options for MSVC=1 builds

  • 84: 6cd104ace6b3 = 156: 2910ae0743a6 cmake: install headless-git.

  • 86: 85a13dbb94e1 = 157: 97118b010184 git.rc: include winuser.h

  • 116: 080217231abd = 158: a01bd81ccbb8 git-svn: mark it as unsupported by the Git for Windows project

  • 117: 68640ec465f1 = 159: 76c3bf3653d1 ci(macos): skip the git p4 tests

  • 119: 40424890a756 = 160: 91dcc05d474e Win32: make FILETIME conversion functions public

  • 120: 86d30c59f173 = 161: 73c6e636358c Win32: dirent.c: Move opendir down

  • 122: 1c511ccf162f = 162: c65ad577c841 mingw: make the dirent implementation pluggable

  • 123: d372eefc6a2f = 163: cb36770f6750 Win32: make the lstat implementation pluggable

  • 124: 3ac452d9e5c5 = 164: 5ded7b7efcaa mingw: add infrastructure for read-only file system level caches

  • 125: 99e7ff8f4644 ! 165: bf9f0e72e00b mingw: add a cache below mingw's lstat and dirent implementations

    @@ compat/win32/fscache.c (new)
     +
     +	fse = fsentry_alloc(list, buf, len);
     +
    -+	fse->st_mode = file_attr_to_st_mode(fdata->dwFileAttributes);
    -+	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
    ++	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    ++					    fdata->EaSize);
    ++	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    ++			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
     +	fse->u.s.st_size = (((off64_t) (fdata->nFileSizeHigh)) << 32)
     +			| fdata->nFileSizeLow;
     +	filetime_to_timespec(&(fdata->ftLastAccessTime), &(fse->u.s.st_atim));
    @@ compat/win32/fscache.c (new)
     +		return -1;
     +	}
     +
    ++	/*
    ++	 * Special case symbolic links: FindFirstFile()/FindNextFile() did not
    ++	 * provide us with the length of the target path.
    ++	 */
    ++	if (fse->u.s.st_size == MAX_PATH && S_ISLNK(fse->st_mode)) {
    ++		char buf[MAX_LONG_PATH];
    ++		int len = readlink(filename, buf, sizeof(buf) - 1);
    ++
    ++		if (len > 0)
    ++			fse->u.s.st_size = len;
    ++	}
    ++
     +	/* copy stat data */
     +	st->st_ino = 0;
     +	st->st_gid = 0;
  • 126: e2d54fff9b8b = 166: fb4c6a513570 fscache: load directories only once

  • 127: 6a8105c8c410 = 167: d3105b49d9c6 fscache: add key for GIT_TRACE_FSCACHE

  • 128: 078d422d2ae2 = 168: 4650e1300875 fscache: remember not-found directories

  • 129: 498046ba2cfa = 169: bba3243fb6eb fscache: add a test for the dir-not-found optimization

  • 130: 85413abff11d = 170: 89545a17e87e add: use preload-index and fscache for performance

  • 131: 990803e0952a = 171: 89d3cb27fd42 dir.c: make add_excludes aware of fscache during status

  • 132: c5c6a4346388 = 172: dd81725b2cce fscache: make fscache_enabled() public

  • 118: ed05727037c7 = 173: 0bad704151f5 git-gui--askyesno: fix funny text wrapping

  • 133: f11f5dcd939a = 174: d74b0b0038ae dir.c: regression fix for add_excludes with fscache

  • 121: 40e5bc7a4525 = 175: 31ddaed68fe7 git-gui--askyesno (mingw): use Git for Windows' icon, if available

  • 134: 2894d5fa003d = 176: 147e66371c58 fetch-pack.c: enable fscache for stats under .git/objects

  • 135: 571f22536cad = 177: 38e3b0b60fbf checkout.c: enable fscache for checkout again

  • 136: f7cd479c9d47 = 178: 9c26251e35a0 Enable the filesystem cache (fscache) in refresh_index().

  • 137: e63e75e91b0f = 179: 10641751c34a fscache: use FindFirstFileExW to avoid retrieving the short name

  • 138: 830815646ee8 = 180: 8f3d874d8b74 fscache: add GIT_TEST_FSCACHE support

  • 139: 410859ed96d6 = 181: ed9e62ed1a30 fscache: add fscache hit statistics

  • 140: 63387087886b = 182: 16a1f1870ab6 unpack-trees: enable fscache for sparse-checkout

  • 141: 874d05d67728 = 183: a919310c6d1e status: disable and free fscache at the end of the status command

  • 142: 7b1618250a54 = 184: 3dd272a340d4 mem_pool: add GIT_TRACE_MEMPOOL support

  • 143: d3e6bdd1c752 = 185: 86df8be1dee4 fscache: fscache takes an initial size

  • 144: 72ccb306c67a = 186: 7ba4643052c6 fscache: update fscache to be thread specific instead of global

  • 145: 182e2ac7de91 ! 187: 7588ad517b99 fscache: teach fscache to use mempool

    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsen
     -	fse = fsentry_alloc(list, buf, len);
     +	fse = fsentry_alloc(cache, list, buf, len);
      
    - 	fse->st_mode = file_attr_to_st_mode(fdata->dwFileAttributes);
    - 	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
    + 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    + 					    fdata->EaSize);
     @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsentry *list,
       * Dir should not contain trailing '/'. Use an empty string for the current
       * directory (not "."!).
  • 146: e0261d891266 = 188: 260041ff5028 fscache: make fscache_enable() thread safe

  • 147: 76cfa4aff56e ! 189: 4aa340bad70a fscache: teach fscache to use NtQueryDirectoryFile

    @@ compat/win32/fscache.c: static void fsentry_release(struct fsentry *fse)
      
      	fse = fsentry_alloc(cache, list, buf, len);
      
    --	fse->st_mode = file_attr_to_st_mode(fdata->dwFileAttributes);
    -+	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes);
    - 	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
    +@@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fscache *cache,
    + 					    fdata->EaSize);
    + 	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    + 			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
     -	fse->u.s.st_size = (((off64_t) (fdata->nFileSizeHigh)) << 32)
     -			| fdata->nFileSizeLow;
     -	filetime_to_timespec(&(fdata->ftLastAccessTime), &(fse->u.s.st_atim));
     -	filetime_to_timespec(&(fdata->ftLastWriteTime), &(fse->u.s.st_mtim));
     -	filetime_to_timespec(&(fdata->ftCreationTime), &(fse->u.s.st_ctim));
    -+	fse->u.s.st_size = fdata->EndOfFile.LowPart |
    -+		(((off_t)fdata->EndOfFile.HighPart) << 32);
    ++	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_PATH :
    ++			fdata->EndOfFile.LowPart |
    ++			(((off_t)fdata->EndOfFile.HighPart) << 32);
     +	filetime_to_timespec((FILETIME *)&(fdata->LastAccessTime),
     +			     &(fse->u.s.st_atim));
     +	filetime_to_timespec((FILETIME *)&(fdata->LastWriteTime),
  • 148: d4d68c29e2dd ! 190: 91ce9a8e3c28 fscache: remember the reparse tag for each entry

    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsca
     +		fdata->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ?
     +		fdata->EaSize : 0;
     +
    - 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes);
    - 	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
    - 	fse->u.s.st_size = fdata->EndOfFile.LowPart |
    + 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    + 					    fdata->EaSize);
    + 	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
  • 149: 368c3dc36a06 = 191: a08c6ea4096c fscache: implement an FSCache-aware is_mount_point()

  • 150: 8a3c85cd47cc = 192: 691bc35e47a0 clean: make use of FSCache

  • 151: 0415b14ccbda = 193: 519b619b8505 pack-objects (mingw): demonstrate a segmentation fault with large deltas

  • 152: b8cf0206ccc3 ! 194: 0b0dc0f8082d mingw: support long paths

    @@ compat/mingw.c: static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOT
      int mingw_core_config(const char *var, const char *value,
      		      const struct config_context *ctx UNUSED,
      		      void *cb UNUSED)
    +@@ compat/mingw.c: process_phantom_symlink(const wchar_t *wtarget, const wchar_t *wlink)
    + {
    + 	HANDLE hnd;
    + 	BY_HANDLE_FILE_INFORMATION fdata;
    +-	wchar_t relative[MAX_PATH];
    ++	wchar_t relative[MAX_LONG_PATH];
    + 	const wchar_t *rel;
    + 
    + 	/* check that wlink is still a file symlink */
     @@ compat/mingw.c: static wchar_t *normalize_ntpath(wchar_t *wbuf)
      int mingw_unlink(const char *pathname, int handle_in_use_error)
      {
      	int tries = 0;
     -	wchar_t wpathname[MAX_PATH];
    +-	if (xutftowcs_path(wpathname, pathname) < 0)
     +	wchar_t wpathname[MAX_LONG_PATH];
    - 	if (xutftowcs_path(wpathname, pathname) < 0)
    ++	if (xutftowcs_long_path(wpathname, pathname) < 0)
      		return -1;
      
    + 	if (DeleteFileW(wpathname))
     @@ compat/mingw.c: static int is_dir_empty(const wchar_t *wpath)
      {
      	WIN32_FIND_DATAW findbuf;
    @@ compat/mingw.c: ssize_t mingw_write(int fd, const void *buf, size_t len)
      int mingw_chdir(const char *dirname)
      {
     -	wchar_t wdirname[MAX_PATH];
    +-
    +-	if (xutftowcs_path(wdirname, dirname) < 0)
    ++	int result;
     +	wchar_t wdirname[MAX_LONG_PATH];
    - 
    - 	if (xutftowcs_path(wdirname, dirname) < 0)
    ++	if (xutftowcs_long_path(wdirname, dirname) < 0)
      		return -1;
    + 
    + 	if (has_symlinks) {
     @@ compat/mingw.c: int mingw_chdir(const char *dirname)
    + 		CloseHandle(hnd);
    + 	}
    + 
    +-	return _wchdir(normalize_ntpath(wdirname));
    ++	result = _wchdir(normalize_ntpath(wdirname));
    ++	current_directory_len = GetCurrentDirectoryW(0, NULL);
    ++	return result;
    + }
      
      int mingw_chmod(const char *filename, int mode)
      {
    @@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
      	DWORD reparse_tag = 0;
      	int link_len = 0;
     -	wchar_t wfilename[MAX_PATH];
    +-	int wlen = xutftowcs_path(wfilename, file_name);
     +	wchar_t wfilename[MAX_LONG_PATH];
    - 	int wlen = xutftowcs_path(wfilename, file_name);
    ++	int wlen = xutftowcs_long_path(wfilename, file_name);
      	if (wlen < 0)
      		return -1;
    + 
    +@@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
    + 	if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
    + 		/* for reparse points, get the link tag and length */
    + 		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
    +-			char tmpbuf[MAX_PATH];
    ++			char tmpbuf[MAX_LONG_PATH];
    + 
    + 			if (read_reparse_point(wfilename, FALSE, tmpbuf,
    + 					       &link_len, &reparse_tag) < 0)
    +@@ compat/mingw.c: static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
    + 
    + int mingw_stat(const char *file_name, struct stat *buf)
    + {
    +-	wchar_t wfile_name[MAX_PATH];
    ++	wchar_t wfile_name[MAX_LONG_PATH];
    + 	HANDLE hnd;
    + 	int result;
    + 
    + 	/* open the file and let Windows resolve the links */
    +-	if (xutftowcs_path(wfile_name, file_name) < 0)
    ++	if (xutftowcs_long_path(wfile_name, file_name) < 0)
    + 		return -1;
    + 	hnd = CreateFileW(wfile_name, 0,
    + 			FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
     @@ compat/mingw.c: int mingw_utime (const char *file_name, const struct utimbuf *times)
      	FILETIME mft, aft;
      	int rc;
    @@ compat/mingw.c: int mingw_raise(int sig)
      		return -1;
      
      	if (!CreateHardLinkW(wnewpath, woldpath, NULL)) {
    +@@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
    + 
    + int symlink(const char *target, const char *link)
    + {
    +-	wchar_t wtarget[MAX_PATH], wlink[MAX_PATH];
    ++	wchar_t wtarget[MAX_LONG_PATH], wlink[MAX_LONG_PATH];
    + 	int len;
    + 
    + 	/* fail if symlinks are disabled or API is not supported (WinXP) */
    +@@ compat/mingw.c: int symlink(const char *target, const char *link)
    + 		return -1;
    + 	}
    + 
    +-	if ((len = xutftowcs_path(wtarget, target)) < 0
    +-			|| xutftowcs_path(wlink, link) < 0)
    ++	if ((len = xutftowcs_long_path(wtarget, target)) < 0
    ++			|| xutftowcs_long_path(wlink, link) < 0)
    + 		return -1;
    + 
    + 	/* convert target dir separators to backslashes */
    +@@ compat/mingw.c: int symlink(const char *target, const char *link)
    + 
    + int readlink(const char *path, char *buf, size_t bufsiz)
    + {
    +-	WCHAR wpath[MAX_PATH];
    +-	char tmpbuf[MAX_PATH];
    ++	WCHAR wpath[MAX_LONG_PATH];
    ++	char tmpbuf[MAX_LONG_PATH];
    + 	int len;
    + 	DWORD tag;
    + 
    +-	if (xutftowcs_path(wpath, path) < 0)
    ++	if (xutftowcs_long_path(wpath, path) < 0)
    + 		return -1;
    + 
    + 	if (read_reparse_point(wpath, TRUE, tmpbuf, &len, &tag) < 0)
     @@ compat/mingw.c: int mingw_is_mount_point(struct strbuf *path)
      {
      	WIN32_FIND_DATAW findbuf = { 0 };
    @@ compat/win32/fscache.c: static void fsentry_init(struct fsentry *fse, struct fse
      		BUG("Trying to allocate fsentry for long path '%.*s'",
      		    (int)len, name);
      	memcpy(fse->dirent.d_name, name, len);
    +@@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fscache *cache,
    + 		fdata->EaSize : 0;
    + 
    + 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    +-					    fdata->EaSize);
    ++					    fdata->EaSize, buf);
    + 	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    + 			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
    +-	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_PATH :
    ++	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
    + 			fdata->EndOfFile.LowPart |
    + 			(((off_t)fdata->EndOfFile.HighPart) << 32);
    + 	filetime_to_timespec((FILETIME *)&(fdata->LastAccessTime),
     @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fscache *cache,
      static struct fsentry *fsentry_create_list(struct fscache *cache, const struct fsentry *dir,
      					   int *dir_not_found)
    @@ compat/win32/fscache.c: static struct fsentry *fsentry_create_list(struct fscach
      
      	/* handle CWD */
      	if (!wlen) {
    +@@ compat/win32/fscache.c: int fscache_lstat(const char *filename, struct stat *st)
    + 	 * Special case symbolic links: FindFirstFile()/FindNextFile() did not
    + 	 * provide us with the length of the target path.
    + 	 */
    +-	if (fse->u.s.st_size == MAX_PATH && S_ISLNK(fse->st_mode)) {
    ++	if (fse->u.s.st_size == MAX_LONG_PATH && S_ISLNK(fse->st_mode)) {
    + 		char buf[MAX_LONG_PATH];
    + 		int len = readlink(filename, buf, sizeof(buf) - 1);
    + 
     
      ## t/meson.build ##
     @@ t/meson.build: integration_tests = [
  • 153: 9390fb2f6d41 ! 195: 2b15f836a87d win32(long path support): leave drive-less absolute paths intact

    @@ Commit message
         Signed-off-by: Johannes Schindelin <[email protected]>
     
      ## compat/mingw.c ##
    -@@ compat/mingw.c: int mingw_core_config(const char *var, const char *value,
    - 	return 0;
    - }
    - 
    --static inline int is_wdir_sep(wchar_t wchar)
    --{
    --	return wchar == L'/' || wchar == L'\\';
    --}
    --
    - static const wchar_t *make_relative_to(const wchar_t *path,
    - 				       const wchar_t *relative_to, wchar_t *out,
    - 				       size_t size)
    -@@ compat/mingw.c: static void process_phantom_symlinks(void)
    - 	LeaveCriticalSection(&phantom_symlinks_cs);
    - }
    - 
    -+static inline int is_wdir_sep(wchar_t wchar)
    -+{
    -+	return wchar == L'/' || wchar == L'\\';
    -+}
    -+
    - /* Normalizes NT paths as returned by some low-level APIs. */
    - static wchar_t *normalize_ntpath(wchar_t *wbuf)
    - {
     @@ compat/mingw.c: int handle_long_path(wchar_t *path, int len, int max_path, int expand)
      	 * "cwd + path" doesn't due to '..' components)
      	 */
  • 154: 788aac7d9f27 = 196: c25c3abc6ffa compat/fsmonitor/fsm-*-win32: support long paths

  • 155: 2bd5cc4d408a = 197: 6d5896bd3538 clean: suggest using core.longPaths if paths are too long to remove

  • 156: a4d5b0e6c216 = 198: 7bb8f5a48c89 mingw: Support git_terminal_prompt with more terminals

  • 157: c60deaf25315 = 199: 4b6c17c67963 compat/terminal.c: only use the Windows console if bash 'read -r' fails

  • 158: 2c45d1144946 = 200: 408161624fcd mingw (git_terminal_prompt): do fall back to CONIN$/CONOUT$ method

  • 159: 912315060d5e < -: ------------ mingw: teach fscache and dirent about symlinks

  • 160: fb72f51367ff = 201: f24db036a81f mingw: introduce code to detect whether we're inside a Windows container

  • 161: 871df8c9d5e1 = 202: 379ec9de1ca7 mingw: when running in a Windows container, try to rename() harder

  • 165: 3986146abfb1 ! 203: 1a6bf775968d Win32: symlink: move phantom symlink creation to a separate function

    @@ compat/mingw.c: static void process_phantom_symlinks(void)
     +	return 0;
     +}
     +
    - static inline int is_wdir_sep(wchar_t wchar)
    + /* Normalizes NT paths as returned by some low-level APIs. */
    + static wchar_t *normalize_ntpath(wchar_t *wbuf)
      {
    - 	return wchar == L'/' || wchar == L'\\';
     @@ compat/mingw.c: int symlink(const char *target, const char *link)
      		if (wtarget[len] == '/')
      			wtarget[len] = '\\';
  • 162: 15ac5dd3c767 = 204: 5dcce1ae7832 mingw: move the file_attr_to_st_mode() function definition

  • 166: aad99be35412 ! 205: 0960f328624f Introduce helper to create symlinks that knows about index_state

    @@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
     -int symlink(const char *target, const char *link)
     +int mingw_create_symlink(struct index_state *index UNUSED, const char *target, const char *link)
      {
    - 	wchar_t wtarget[MAX_PATH], wlink[MAX_PATH];
    + 	wchar_t wtarget[MAX_LONG_PATH], wlink[MAX_LONG_PATH];
      	int len;
     
      ## entry.c ##
  • 163: a83fccbf2112 ! 206: baea4cfe36cf mingw: Windows Docker volumes are not symbolic links

    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsca
     +	}
     +
      	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    --					    fdata->EaSize);
    -+					    fdata->EaSize, buf);
    + 					    fdata->EaSize, buf);
      	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    - 			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
    - 	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
  • 167: 8df7f9a02f70 ! 207: 12ea88bdfbdf mingw: allow to specify the symlink type in .gitattributes

    @@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
     +
     +int mingw_create_symlink(struct index_state *index, const char *target, const char *link)
      {
    - 	wchar_t wtarget[MAX_PATH], wlink[MAX_PATH];
    + 	wchar_t wtarget[MAX_LONG_PATH], wlink[MAX_LONG_PATH];
      	int len;
     @@ compat/mingw.c: int mingw_create_symlink(struct index_state *index UNUSED, const char *target, c
      		if (wtarget[len] == '/')
  • 164: fcb43d2d75ff = 208: cee92a642d24 mingw: work around rename() failing on a read-only file

  • 168: c10e2df34520 = 209: c7ac5703c5ea Win32: symlink: add test for symlink attribute

  • 169: 55e6f6dab308 = 210: dd6f4f6e151e mingw: explicitly specify with which cmd to prefix the cmdline

  • 170: 6f69eedd23bc = 211: 119a7bd28ad6 mingw: when path_lookup() failed, try BusyBox

  • 171: c22d1eec6284 = 212: 5fff04da152e test-tool: learn to act as a drop-in replacement for iconv

  • 172: 3e93f080f612 = 213: 713de6120df7 tests(mingw): if iconv is unavailable, use test-helper --iconv

  • 173: ac93c6ffb55a = 214: 83ce344e4af8 gitattributes: mark .png files as binary

  • 174: 9810f873c296 = 215: 7a10947b3d2b tests: move test PNGs into t/lib-diff/

  • 175: f4b831301669 = 216: 50e21b09548b tests: only override sort & find if there are usable ones in /usr/bin/

  • 176: 03b24980c827 = 217: ab77ebf8c4a6 tests: use the correct path separator with BusyBox

  • 177: 749a3f171c7d = 218: 6cd81171a36d mingw: only use Bash-ism builtin pwd -W when available

  • 178: f96fa24a8fbf = 219: 495bf8e20136 tests (mingw): remove Bash-specific pwd option

  • 179: f1302400f226 = 220: 61b0aea2ca3c test-lib: add BUSYBOX prerequisite

  • 180: 6b9702ada4b0 = 221: b29f110fedd9 t5003: use binary file from t/lib-diff/

  • 181: f8d96d5c4472 = 222: bffa16ecdbff t5532: workaround for BusyBox on Windows

  • 182: f0d24db6f5a2 = 223: f1c208d52f19 t5605: special-case hardlink test for BusyBox-w32

  • 183: c243d75bbb3c = 224: 19fc9e77af1e t5813: allow for $PWD to be a Windows path

  • 184: 824ac5d0e9a4 = 225: 4161847c826c t9200: skip tests when $PWD contains a colon

  • 185: 6e9437e4eb9a = 226: 948a2857fa5f mingw: kill child processes in a gentler way

  • 186: f8a7613f4c4e = 227: 49c3f4b19b36 mingw: optionally enable wsl compability file mode bits

  • 187: 9db5f1d366ee = 228: 5c2a5a354922 mingw: really handle SIGINT

  • 188: cb8a2ee599c5 = 229: cb06418701c8 Partially un-revert "editor: save and reset terminal after calling EDITOR"

  • 189: e607f57e33a9 = 230: 4ac142d37230 reset: reinstate support for the deprecated --stdin option

  • 191: a11d922da599 = 231: 2b232771ceef Describe Git for Windows' architecture [no ci]

  • 192: 7aa62caa9841 = 232: 8721a8b3c0c7 Modify the Code of Conduct for Git for Windows

  • 193: bed628ca251e = 233: 0a7316bf461d CONTRIBUTING.md: add guide for first-time contributors

  • 194: 7fc0cfb3a2fc = 234: 992862351f1b README.md: Add a Windows-specific preamble

  • 195: 3bb5687192b6 = 235: fa4ba97ec50e Add an issue template

  • 196: de6c1288d127 = 236: 58c26230ebaa Add a GitHub workflow to monitor component updates

  • 197: 7b3d784cf8b6 = 237: 14a252e67809 Modify the GitHub Pull Request template (to reflect Git for Windows)

  • 190: c5120ad6b6c6 = 238: aadd41abdf3b fsmonitor: reintroduce core.useBuiltinFSMonitor

  • 198: dd9f84e39f44 = 239: 9e508167ec80 dependabot: help keeping GitHub Actions versions up to date

  • 199: 2e459b5780e0 = 240: 6fda5302109b SECURITY.md: document Git for Windows' policies

  • 200: 04e96b05f00c < -: ------------ fixup??? win32(long path support): leave drive-less absolute paths intact

  • 201: d643eef37bb7 < -: ------------ fixup??? mingw: support long paths

  • -: ------------ > 241: 2c109f0653c0 sideband: mask control characters

  • -: ------------ > 242: a122a21b2246 sideband: introduce an "escape hatch" to allow control characters

  • -: ------------ > 243: 2bbfb9429d7a sideband: do allow ANSI color sequences by default

  • -: ------------ > 244: edbd371ac138 unix-socket: avoid leak when initialization fails

  • -: ------------ > 245: c3a966c0c0d0 grep: prevent ^$ false match at end of file

  • -: ------------ > 246: 07ad15c49cc1 t9350: point out that refs are not updated correctly

  • -: ------------ > 247: a521ce884bbe transport-helper: add trailing --

  • -: ------------ > 248: d3fb36a9b0e6 remote-helper: check helper status after import/export

  • -: ------------ > 249: 1755d8a9f2b0 Always auto-gc after calling a fast-import transport

  • -: ------------ > 250: 8b0c34b37d0a mingw: prevent regressions with "drive-less" absolute paths

  • -: ------------ > 251: 316b86eef5c1 mingw: include the Python parts in the build

  • -: ------------ > 252: 46144573d55e win32/pthread: avoid name clashes with winpthread

  • -: ------------ > 253: 51e6fec1585e clean: do not traverse mount points

  • -: ------------ > 254: fc889a75b0b8 git-compat-util: avoid redeclaring _DEFAULT_SOURCE

  • -: ------------ > 255: 0f04fcf12e55 clean: remove mount points when possible

  • -: ------------ > 256: 5117884210fc Import the source code of mimalloc v2.2.7

  • -: ------------ > 257: 3d9e140fc5e8 mimalloc: adjust for building inside Git

  • -: ------------ > 258: dbc2b9e2b042 mimalloc: offer a build-time option to enable it

  • -: ------------ > 259: 7629cb87c652 mingw: use mimalloc

  • -: ------------ > 260: d32213ddbeb7 transport: optionally disable side-band-64k

  • -: ------------ > 261: 82c5b1a6aebb mingw: demonstrate a git add issue with NTFS junctions

  • -: ------------ > 262: d85f2a867d1e strbuf_realpath(): use platform-dependent API if available

  • -: ------------ > 263: 6ddbe3fb4963 mingw: fix fatal error working on mapped network drives on Windows

  • -: ------------ > 264: b91f9db7daf6 clink.pl: fix MSVC compile script to handle libcurl-d.lib

  • -: ------------ > 265: 80707ce83ba6 mingw: implement a platform-specific strbuf_realpath()

  • -: ------------ > 266: b04d0beae0af t5505/t5516: allow running without .git/branches/ in the templates

  • -: ------------ > 267: 16db2439a1d8 http: use new "best effort" strategy for Secure Channel revoke checking

  • -: ------------ > 268: 24646ea1ced1 t5505/t5516: fix white-space around redirectors

  • -: ------------ > 269: 5813c3297da3 t3701: verify that we can add lots of files interactively

  • -: ------------ > 270: a3b89ccf3aa3 commit: accept "scissors" with CR/LF line endings

  • -: ------------ > 271: a864b7adf5d4 t0014: fix indentation

  • -: ------------ > 272: f8fb7b631193 git-gui: accommodate for intent-to-add files

  • -: ------------ > 273: 9415a3955ca4 vcpkg_install: detect lack of Git

  • -: ------------ > 274: d9a4d9fa5a30 vcpkg_install: add comment regarding slow network connections

  • -: ------------ > 275: b5c104db493c vcbuild: install ARM64 dependencies when building ARM64 binaries

  • -: ------------ > 276: 948ee5682daa vcbuild: add an option to install individual 'features'

  • -: ------------ > 277: 8a2c6103871f cmake: allow building for Windows/ARM64

  • -: ------------ > 278: 83d71f22c9d1 ci(vs-build) also build Windows/ARM64 artifacts

  • -: ------------ > 279: 6d62db2a08b4 Add schannel to curl installation

  • -: ------------ > 280: f9757680ecf0 cmake(): allow setting HOST_CPU for cross-compilation

  • -: ------------ > 281: fc5c74c06045 mingw: allow for longer paths in parse_interpreter()

  • -: ------------ > 282: 5796b20eaccc compat/vcbuild: document preferred way to build in Visual Studio

  • -: ------------ > 283: 6b7abfcd4e7e http: optionally send SSL client certificate

  • -: ------------ > 284: 0a1ba9570c0f CMake: default Visual Studio generator has changed

  • -: ------------ > 285: 5f33d0e6b761 .gitignore: add Visual Studio CMakeSetting.json file

  • -: ------------ > 286: ed0580cdb256 subtree: update contrib/subtree test target

  • -: ------------ > 287: aeae47e03d17 CMakeLists: add default "x64-windows" arch for Visual Studio

  • -: ------------ > 288: ef2cb96f9d7d ci: run contrib/subtree tests in CI builds

  • -: ------------ > 289: 934b9d2207a1 CMake: show Win32 and Generator_platform build-option values

  • -: ------------ > 290: f6d0105e8ad1 hash-object: demonstrate a >4GB/LLP64 problem

  • -: ------------ > 291: 172785e58cf3 object-file.c: use size_t for header lengths

  • -: ------------ > 292: e0b508f5ad7f hash algorithms: use size_t for section lengths

  • -: ------------ > 293: 7edf3be9daa4 hash-object --stdin: verify that it works with >4GB/LLP64

  • -: ------------ > 294: 300a40dec85c hash-object: add another >4GB/LLP64 test case

  • -: ------------ > 295: 38a762fd7cbe setup: properly use "%(prefix)/" when in WSL

  • -: ------------ > 296: 94cbc8b5b616 hash-object: add a >4GB/LLP64 test case using filtered input

  • -: ------------ > 297: 798ec83ca37f Add config option windows.appendAtomically

  • -: ------------ > 298: 17aedc394ab8 compat/mingw.c: do not warn when failing to get owner

  • -: ------------ > 299: 5f7dd089da45 mingw: $env:TERM="xterm-256color" for newer OSes

  • -: ------------ > 300: 93246f46c9d7 winansi: check result and Buffer before using Name

  • -: ------------ > 301: f1492d07d983 mingw: change core.fsyncObjectFiles = 1 by default

  • -: ------------ > 302: b7e44f7f27b6 MinGW: link as terminal server aware

  • -: ------------ > 303: 17bc91ef97cc Fix Windows version resources

  • -: ------------ > 304: 7af04a575e15 status: fix for old-style submodules with commondir

  • -: ------------ > 305: 3bfa5ab1bae0 windows: skip linking git-<command> for built-ins

  • -: ------------ > 306: 160abdae7641 mingw: stop hard-coding CC = gcc

  • -: ------------ > 307: fc0d7e348ad4 mingw: drop the -D_USE_32BIT_TIME_T option

  • -: ------------ > 308: 7275397b7b50 mingw: only use -Wl,--large-address-aware for 32-bit builds

  • -: ------------ > 309: bd6ae62be2c3 mingw: avoid over-specifying --pic-executable

  • -: ------------ > 310: 207cf9c4e8e8 mingw: set the prefix and HOST_CPU as per MSYS2's settings

  • -: ------------ > 311: 8585178f5855 mingw: only enable the MSYS2-specific stuff when compiling in MSYS2

  • -: ------------ > 312: d8382ed26253 mingw: rely on MSYS2's metadata instead of hard-coding it

  • -: ------------ > 313: 5ab8fd9c46f4 mingw: always define ETC_* for MSYS2 environments

  • -: ------------ > 314: 4ae9f862ffc5 max_tree_depth: lower it for clang builds in general on Windows

  • -: ------------ > 315: 0287b4a7237d mingw: ensure valid CTYPE

  • -: ------------ > 316: eeb297075fef mingw: allow git.exe to be used instead of the "Git wrapper"

  • -: ------------ > 317: e021ac168307 mingw: ignore HOMEDRIVE/HOMEPATH if it points to Windows' system directory

  • -: ------------ > 318: 8fb48570746e clink.pl: fix libexpatd.lib link error when using MSVC

  • -: ------------ > 319: decc53ae0a4e Makefile: clean up .ilk files when MSVC=1

  • -: ------------ > 320: e1e190e76d82 vcbuild: add support for compiling Windows resource files

  • -: ------------ > 321: 45c72f19fd31 config.mak.uname: add git.rc to MSVC builds

  • -: ------------ > 322: dfc75b86184e clink.pl: ignore no-stack-protector arg on MSVC=1 builds

  • -: ------------ > 323: 85a87ece994d clink.pl: move default linker options for MSVC=1 builds

  • -: ------------ > 324: 2ece4cee33dd cmake: install headless-git.

  • -: ------------ > 325: ea060ab958bd http: optionally load libcurl lazily

  • -: ------------ > 326: 6f37322d0d4f http: support lazy-loading libcurl also on Windows

  • -: ------------ > 327: 7d444ad4512c http: when loading libcurl lazily, allow for multiple SSL backends

  • -: ------------ > 328: 9d008e8471e2 git.rc: include winuser.h

  • -: ------------ > 329: 0edcf73b6986 mingw: do load libcurl dynamically by default

  • -: ------------ > 330: 693599f62dbe Add a GitHub workflow to verify that Git/Scalar work in Nano Server

  • -: ------------ > 331: 339575303a65 mingw: suggest windows.appendAtomically in more cases

  • -: ------------ > 332: ee7fb0600ed4 win32: use native ANSI sequence processing, if possible

  • -: ------------ > 333: 8bbb07055158 common-main.c: fflush stdout buffer upon exit

  • -: ------------ > 334: 27467b1805aa t5601/t7406(mingw): do run tests with symlink support

  • -: ------------ > 335: 4f0f35f624c0 win32: ensure that localtime_r() is declared even in i686 builds

  • -: ------------ > 336: 26c2afafe5d7 Fallback to AppData if XDG_CONFIG_HOME is unset

  • -: ------------ > 337: 0a25b7694ea4 run-command: be helpful with Git LFS fails on Windows 7

  • -: ------------ > 338: 5f28bbd1a5ba ci: work around a problem with HTTP/2 vs libcurl v8.10.0

  • -: ------------ > 339: afef0c0f9e8f revision: create mark_trees_uninteresting_dense()

  • -: ------------ > 340: c00546418d98 survey: stub in new experimental 'git-survey' command

  • -: ------------ > 341: 5bbfb07e9716 survey: add command line opts to select references

  • -: ------------ > 342: eed6270c9e0d survey: start pretty printing data in table form

  • -: ------------ > 343: d8ef8a4fa7d0 survey: add object count summary

  • -: ------------ > 344: f47f23e6bfff survey: summarize total sizes by object type

  • -: ------------ > 345: 66fa2e731d9f survey: show progress during object walk

  • -: ------------ > 346: a54d10bf7b23 survey: add ability to track prioritized lists

  • -: ------------ > 347: 93f43f51ddf1 mingw: make sure errno is set correctly when socket operations fail

  • -: ------------ > 348: 6092529f878e survey: add report of "largest" paths

  • -: ------------ > 349: 0a45e4674590 compat/mingw: handle WSA errors in strerror

  • -: ------------ > 350: dd055dcb7e06 survey: add --top= option and config

  • -: ------------ > 351: 1f3ca1f515b5 compat/mingw: drop outdated comment

  • -: ------------ > 352: 3ea8ed45a41d survey: clearly note the experimental nature in the output

  • -: ------------ > 353: ad43de1eac6e t0301: actually test credential-cache on Windows

  • -: ------------ > 354: 793c95d606be credential-cache: handle ECONNREFUSED gracefully

  • -: ------------ > 355: de91a2bc0982 reftable: do make sure to use custom allocators

  • -: ------------ > 356: 086f484f6eaa check-whitespace: avoid alerts about upstream commits

  • -: ------------ > 357: 3039ca3a8809 t/t5571-prep-push-hook.sh: Add test with writing to stderr

  • -: ------------ > 358: 5aa362abb5e0 git-svn: mark it as unsupported by the Git for Windows project

  • -: ------------ > 359: 493d720c3c29 ci(macos): skip the git p4 tests

  • -: ------------ > 360: 76b8c19eb4ad git-gui--askyesno: fix funny text wrapping

  • -: ------------ > 361: 6c02e0ddd94b Win32: make FILETIME conversion functions public

  • -: ------------ > 362: c637077145d1 git-gui--askyesno (mingw): use Git for Windows' icon, if available

  • -: ------------ > 363: 7f831cf9fe3d Win32: dirent.c: Move opendir down

  • -: ------------ > 364: 1f8cacef89cf mingw: make the dirent implementation pluggable

  • -: ------------ > 365: d10cc1c8817a Win32: make the lstat implementation pluggable

  • -: ------------ > 366: 318f35a03d41 mingw: add infrastructure for read-only file system level caches

  • -: ------------ > 367: d21074aa7c4d mingw: add a cache below mingw's lstat and dirent implementations

  • -: ------------ > 368: 090b1d55017b fscache: load directories only once

  • -: ------------ > 369: 8dacfca8e807 fscache: add key for GIT_TRACE_FSCACHE

  • -: ------------ > 370: e4a06fd8fb04 fscache: remember not-found directories

  • -: ------------ > 371: ec8a5c2a7e90 fscache: add a test for the dir-not-found optimization

  • -: ------------ > 372: 89ac088ddf37 add: use preload-index and fscache for performance

  • -: ------------ > 373: 314860c36ec7 dir.c: make add_excludes aware of fscache during status

  • -: ------------ > 374: 80977c82bed7 fscache: make fscache_enabled() public

  • -: ------------ > 375: e6e206872df7 dir.c: regression fix for add_excludes with fscache

  • -: ------------ > 376: 903e383bf8ff fetch-pack.c: enable fscache for stats under .git/objects

  • -: ------------ > 377: 44cd3957b88f checkout.c: enable fscache for checkout again

  • -: ------------ > 378: 1340412f61cc Enable the filesystem cache (fscache) in refresh_index().

  • -: ------------ > 379: 3aaa230def55 fscache: use FindFirstFileExW to avoid retrieving the short name

  • -: ------------ > 380: 77168a36fd75 fscache: add GIT_TEST_FSCACHE support

  • -: ------------ > 381: d5ce36ac9509 fscache: add fscache hit statistics

  • -: ------------ > 382: 234b4cf85144 unpack-trees: enable fscache for sparse-checkout

  • -: ------------ > 383: 225675f79aa1 status: disable and free fscache at the end of the status command

  • -: ------------ > 384: 750d093cc226 mem_pool: add GIT_TRACE_MEMPOOL support

  • -: ------------ > 385: a4e29092582b fscache: fscache takes an initial size

  • -: ------------ > 386: 4a3239d1988f fscache: update fscache to be thread specific instead of global

  • -: ------------ > 387: e19ab54fb249 fscache: teach fscache to use mempool

  • -: ------------ > 388: 0a67e3c9334b fscache: make fscache_enable() thread safe

  • -: ------------ > 389: e198df135cf8 fscache: teach fscache to use NtQueryDirectoryFile

  • -: ------------ > 390: 019962b49513 fscache: remember the reparse tag for each entry

  • -: ------------ > 391: 7d86876d8f9e fscache: implement an FSCache-aware is_mount_point()

  • -: ------------ > 392: ed8ab892d3d0 clean: make use of FSCache

  • -: ------------ > 393: c6ca7b78dacd pack-objects (mingw): demonstrate a segmentation fault with large deltas

  • -: ------------ > 394: 7d336512deb5 mingw: support long paths

  • -: ------------ > 395: 94c695274e47 win32(long path support): leave drive-less absolute paths intact

  • -: ------------ > 396: 445480c0bb48 compat/fsmonitor/fsm-*-win32: support long paths

  • -: ------------ > 397: 5c7c5b4c5834 clean: suggest using core.longPaths if paths are too long to remove

  • -: ------------ > 398: 7097cb149a37 mingw: Support git_terminal_prompt with more terminals

  • -: ------------ > 399: af665751ca52 compat/terminal.c: only use the Windows console if bash 'read -r' fails

  • -: ------------ > 400: 6b5ddd5f2933 mingw (git_terminal_prompt): do fall back to CONIN$/CONOUT$ method

  • -: ------------ > 401: 34844eae97e2 mingw: introduce code to detect whether we're inside a Windows container

  • -: ------------ > 402: fd36a90a6fdb mingw: when running in a Windows container, try to rename() harder

  • -: ------------ > 403: 7ad2aacffacf mingw: move the file_attr_to_st_mode() function definition

  • -: ------------ > 404: 3b679d9b0cd8 mingw: Windows Docker volumes are not symbolic links

  • -: ------------ > 405: a730a86cdb1a mingw: work around rename() failing on a read-only file

  • -: ------------ > 406: c744a09eaf81 Win32: symlink: move phantom symlink creation to a separate function

  • -: ------------ > 407: 25fb14389136 Introduce helper to create symlinks that knows about index_state

  • -: ------------ > 408: cf32cfea1c8f mingw: allow to specify the symlink type in .gitattributes

  • -: ------------ > 409: e60aa0b8b048 Win32: symlink: add test for symlink attribute

  • -: ------------ > 410: 13a334a52fc2 mingw: explicitly specify with which cmd to prefix the cmdline

  • -: ------------ > 411: adfc87cec578 mingw: when path_lookup() failed, try BusyBox

  • -: ------------ > 412: cbba0d3aa5d1 test-tool: learn to act as a drop-in replacement for iconv

  • -: ------------ > 413: e6e112ee41aa tests(mingw): if iconv is unavailable, use test-helper --iconv

  • -: ------------ > 414: df88a47db503 gitattributes: mark .png files as binary

  • -: ------------ > 415: ffe90db6d7d8 tests: move test PNGs into t/lib-diff/

  • -: ------------ > 416: f9249afe3651 tests: only override sort & find if there are usable ones in /usr/bin/

  • -: ------------ > 417: 800fc2eb8708 tests: use the correct path separator with BusyBox

  • -: ------------ > 418: fcf6be04168c mingw: only use Bash-ism builtin pwd -W when available

  • -: ------------ > 419: a65621494f04 tests (mingw): remove Bash-specific pwd option

  • -: ------------ > 420: b344cc497ef8 test-lib: add BUSYBOX prerequisite

  • -: ------------ > 421: aab4afa636b0 t5003: use binary file from t/lib-diff/

  • -: ------------ > 422: c06d0ed48383 t5532: workaround for BusyBox on Windows

  • -: ------------ > 423: e7c79f941a6a t5605: special-case hardlink test for BusyBox-w32

  • -: ------------ > 424: ebecb17a7ef4 t5813: allow for $PWD to be a Windows path

  • -: ------------ > 425: 8c468565752c t9200: skip tests when $PWD contains a colon

  • -: ------------ > 426: 8460cbc3e12a mingw: kill child processes in a gentler way

  • -: ------------ > 427: 6345708468a3 mingw: optionally enable wsl compability file mode bits

  • -: ------------ > 428: fe2637b25cf5 mingw: really handle SIGINT

  • -: ------------ > 429: 02b6f87daf12 Partially un-revert "editor: save and reset terminal after calling EDITOR"

  • -: ------------ > 430: fdfe0b8f7c16 reset: reinstate support for the deprecated --stdin option

  • -: ------------ > 431: 3a346af8bed8 Describe Git for Windows' architecture [no ci]

  • -: ------------ > 432: bc85b710647d Modify the Code of Conduct for Git for Windows

  • -: ------------ > 433: 60ba9185996b CONTRIBUTING.md: add guide for first-time contributors

  • -: ------------ > 434: a5486aa05e70 Add a GitHub workflow to monitor component updates

  • -: ------------ > 435: 32054c64df16 README.md: Add a Windows-specific preamble

  • -: ------------ > 436: e76d7dd81519 fsmonitor: reintroduce core.useBuiltinFSMonitor

  • -: ------------ > 437: fd7a24cf7b00 dependabot: help keeping GitHub Actions versions up to date

  • -: ------------ > 438: 3c2b4967dd3e Add an issue template

  • -: ------------ > 439: 8744e7447e02 Modify the GitHub Pull Request template (to reflect Git for Windows)

  • -: ------------ > 440: 070a407d7c4d SECURITY.md: document Git for Windows' policies

I let Claude Opus write the throw-away commit f9d870a that temporarily overrides the refs so as to pretend that the remote branches are in the same state as before I performed above-mentioned rebase. When I pushed that, it triggered this run: https://github.com/git-for-windows/git-for-windows-automation/actions/runs/22109791261. The result is in the attached rebase-result artifact, a Git bundle that has the tip commit of the shears/next branch after rebasing: 14aa1e62559b7b3f20a43b20a95973083cdf151f.

To my great surprise, both post-rebase tip commits are tree-same! And not only that, the range-diff shows identical patches (modulo commit OIDs), which I found really surprising:

Range-diff between my rebase and the automated one
  • 1: 2c109f0653c0 = 1: 7a9f5a4029cd sideband: mask control characters
  • 2: a122a21b2246 = 2: 850004e1390d sideband: introduce an "escape hatch" to allow control characters
  • 3: 2bbfb9429d7a = 3: 8cb133481fba sideband: do allow ANSI color sequences by default
  • 4: edbd371ac138 = 4: 5e7a8923bb11 unix-socket: avoid leak when initialization fails
  • 5: c3a966c0c0d0 = 5: e7b1815e8009 grep: prevent ^$ false match at end of file
  • 6: 07ad15c49cc1 = 6: d3eb656f6fc6 t9350: point out that refs are not updated correctly
  • 33: 9415a3955ca4 = 7: 8b0c4dce8617 vcpkg_install: detect lack of Git
  • 34: d9a4d9fa5a30 = 8: 3704694195c6 vcpkg_install: add comment regarding slow network connections
  • 35: b5c104db493c = 9: 49f2eacf7e7b vcbuild: install ARM64 dependencies when building ARM64 binaries
  • 36: 948ee5682daa = 10: 8553d591b623 vcbuild: add an option to install individual 'features'
  • 37: 8a2c6103871f = 11: 261c5be7b61b cmake: allow building for Windows/ARM64
  • 11: 316b86eef5c1 = 12: 84a4e75b3f6d mingw: include the Python parts in the build
  • 38: 83d71f22c9d1 = 13: 775925c5139c ci(vs-build) also build Windows/ARM64 artifacts
  • 12: 46144573d55e = 14: 06d7365cd1be win32/pthread: avoid name clashes with winpthread
  • 39: 6d62db2a08b4 = 15: 543b5cf2e46c Add schannel to curl installation
  • 50: f6d0105e8ad1 = 16: 443b393ce0d2 hash-object: demonstrate a >4GB/LLP64 problem
  • 14: fc889a75b0b8 = 17: 425a38266d66 git-compat-util: avoid redeclaring _DEFAULT_SOURCE
  • 40: f9757680ecf0 = 18: 1c5c186af5c4 cmake(): allow setting HOST_CPU for cross-compilation
  • 51: 172785e58cf3 = 19: c996dc9d9a8e object-file.c: use size_t for header lengths
  • 16: 5117884210fc = 20: 13bac67d1434 Import the source code of mimalloc v2.2.7
  • 44: 0a1ba9570c0f = 21: 5c808fb0ab50 CMake: default Visual Studio generator has changed
  • 52: e0b508f5ad7f = 22: 9111ed18856a hash algorithms: use size_t for section lengths
  • 17: 3d9e140fc5e8 = 23: da37d37872e4 mimalloc: adjust for building inside Git
  • 7: a521ce884bbe = 24: e9a45780d409 transport-helper: add trailing --
  • 21: 82c5b1a6aebb = 25: 01f799c02109 mingw: demonstrate a git add issue with NTFS junctions
  • 45: 5f33d0e6b761 = 26: 4f0c9135e461 .gitignore: add Visual Studio CMakeSetting.json file
  • 53: 7edf3be9daa4 = 27: 9acd8533cd29 hash-object --stdin: verify that it works with >4GB/LLP64
  • 18: dbc2b9e2b042 = 28: aa04988ced5b mimalloc: offer a build-time option to enable it
  • 26: b04d0beae0af = 29: 4355671d784b t5505/t5516: allow running without .git/branches/ in the templates
  • 8: d3fb36a9b0e6 = 30: 8101ee3e7271 remote-helper: check helper status after import/export
  • 13: 51e6fec1585e = 31: 297b5c6a8335 clean: do not traverse mount points
  • 22: d85f2a867d1e = 32: 96c89a549a00 strbuf_realpath(): use platform-dependent API if available
  • 27: 16db2439a1d8 = 33: efc1831344ed http: use new "best effort" strategy for Secure Channel revoke checking
  • 46: ed0580cdb256 = 34: 91c6f5d6446a subtree: update contrib/subtree test target
  • 47: aeae47e03d17 = 35: f49d4799da5d CMakeLists: add default "x64-windows" arch for Visual Studio
  • 54: 300a40dec85c = 36: 21fdbf0487c4 hash-object: add another >4GB/LLP64 test case
  • 55: 38a762fd7cbe = 37: 4499e63d93d0 setup: properly use "%(prefix)/" when in WSL
  • 19: 7629cb87c652 = 38: 6a25680ba07e mingw: use mimalloc
  • 28: 24646ea1ced1 = 39: 3119caff2b45 t5505/t5516: fix white-space around redirectors
  • 9: 1755d8a9f2b0 = 40: fd0b5077fb2d Always auto-gc after calling a fast-import transport
  • 10: 8b0c34b37d0a = 41: 8ee593576ead mingw: prevent regressions with "drive-less" absolute paths
  • 15: 0f04fcf12e55 = 42: 3c2ad0403bf8 clean: remove mount points when possible
  • 20: d32213ddbeb7 = 43: f1b64112ba06 transport: optionally disable side-band-64k
  • 23: 6ddbe3fb4963 = 44: 549d2b435c1a mingw: fix fatal error working on mapped network drives on Windows
  • 24: b91f9db7daf6 = 45: 39f5b298890e clink.pl: fix MSVC compile script to handle libcurl-d.lib
  • 25: 80707ce83ba6 = 46: d5dc3e57ee96 mingw: implement a platform-specific strbuf_realpath()
  • 29: 5813c3297da3 = 47: b1a92b2c8a28 t3701: verify that we can add lots of files interactively
  • 30: a3b89ccf3aa3 = 48: 42b86c42e867 commit: accept "scissors" with CR/LF line endings
  • 31: a864b7adf5d4 = 49: e5a3eb3c47f7 t0014: fix indentation
  • 32: f8fb7b631193 = 50: 8130a31ee23c git-gui: accommodate for intent-to-add files
  • 41: fc5c74c06045 = 51: 56797a69e1f8 mingw: allow for longer paths in parse_interpreter()
  • 42: 5796b20eaccc = 52: 6f0670b0038c compat/vcbuild: document preferred way to build in Visual Studio
  • 43: 6b7abfcd4e7e = 53: 57fcc09224fb http: optionally send SSL client certificate
  • 48: ef2cb96f9d7d = 54: b4e9970bd799 ci: run contrib/subtree tests in CI builds
  • 49: 934b9d2207a1 = 55: 53b6fe3a4729 CMake: show Win32 and Generator_platform build-option values
  • 56: 94cbc8b5b616 = 56: 866ab10c6525 hash-object: add a >4GB/LLP64 test case using filtered input
  • 58: 17aedc394ab8 = 57: 998bf3fb06b1 compat/mingw.c: do not warn when failing to get owner
  • 65: 3bfa5ab1bae0 = 58: 6bf370edef7c windows: skip linking git-<command> for built-ins
  • 66: 160abdae7641 = 59: 093aa718da78 mingw: stop hard-coding CC = gcc
  • 67: fc0d7e348ad4 = 60: 2e21fd56d467 mingw: drop the -D_USE_32BIT_TIME_T option
  • 68: 7275397b7b50 = 61: 0b4312e6fe6f mingw: only use -Wl,--large-address-aware for 32-bit builds
  • 69: bd6ae62be2c3 = 62: 98c80963d7f3 mingw: avoid over-specifying --pic-executable
  • 70: 207cf9c4e8e8 = 63: 69960728c864 mingw: set the prefix and HOST_CPU as per MSYS2's settings
  • 71: 8585178f5855 = 64: c5ac1ef364cd mingw: only enable the MSYS2-specific stuff when compiling in MSYS2
  • 72: d8382ed26253 = 65: 9dae847dde60 mingw: rely on MSYS2's metadata instead of hard-coding it
  • 73: 5ab8fd9c46f4 = 66: 76a71ab8f109 mingw: always define ETC_* for MSYS2 environments
  • 74: 4ae9f862ffc5 = 67: 168fdfa574b6 max_tree_depth: lower it for clang builds in general on Windows
  • 75: 0287b4a7237d = 68: cb7eade340f9 mingw: ensure valid CTYPE
  • 98: 5f28bbd1a5ba = 69: 5443d0f82bf2 ci: work around a problem with HTTP/2 vs libcurl v8.10.0
  • 76: eeb297075fef = 70: e2ab98222202 mingw: allow git.exe to be used instead of the "Git wrapper"
  • 99: afef0c0f9e8f = 71: 94c69252ea0b revision: create mark_trees_uninteresting_dense()
  • 77: e021ac168307 = 72: 5eda487409f7 mingw: ignore HOMEDRIVE/HOMEPATH if it points to Windows' system directory
  • 100: c00546418d98 = 73: 4b7140588881 survey: stub in new experimental 'git-survey' command
  • 101: 5bbfb07e9716 = 74: 757443a81b2a survey: add command line opts to select references
  • 78: 8fb48570746e = 75: 1aecde5b27e6 clink.pl: fix libexpatd.lib link error when using MSVC
  • 102: eed6270c9e0d = 76: dc855aee9143 survey: start pretty printing data in table form
  • 79: decc53ae0a4e = 77: cf4c41374bc5 Makefile: clean up .ilk files when MSVC=1
  • 103: d8ef8a4fa7d0 = 78: c41c41860439 survey: add object count summary
  • 80: e1e190e76d82 = 79: 1dfaa34bcd1e vcbuild: add support for compiling Windows resource files
  • 104: f47f23e6bfff = 80: df5a34f3506f survey: summarize total sizes by object type
  • 81: 45c72f19fd31 = 81: e529303397a9 config.mak.uname: add git.rc to MSVC builds
  • 62: b7e44f7f27b6 = 82: 8cadb9327836 MinGW: link as terminal server aware
  • 105: 66fa2e731d9f = 83: 67cabd0655fb survey: show progress during object walk
  • 82: dfc75b86184e = 84: e510ab9d738a clink.pl: ignore no-stack-protector arg on MSVC=1 builds
  • 85: ea060ab958bd = 85: 856da3a25af6 http: optionally load libcurl lazily
  • 106: a54d10bf7b23 = 86: 3ca4adde71c8 survey: add ability to track prioritized lists
  • 83: 85a87ece994d = 87: cb1b4ec01dda clink.pl: move default linker options for MSVC=1 builds
  • 86: 6f37322d0d4f = 88: d89e9104b942 http: support lazy-loading libcurl also on Windows
  • 108: 6092529f878e = 89: 3c85d319bd3b survey: add report of "largest" paths
  • 57: 798ec83ca37f = 90: 51665eea2c70 Add config option windows.appendAtomically
  • 84: 2ece4cee33dd = 91: 31b518395e30 cmake: install headless-git.
  • 87: 7d444ad4512c = 92: c3bd03933288 http: when loading libcurl lazily, allow for multiple SSL backends
  • 110: dd055dcb7e06 = 93: f8a87c5c4f6c survey: add --top= option and config
  • 59: 5f7dd089da45 = 94: 87cd56b9ca93 mingw: $env:TERM="xterm-256color" for newer OSes
  • 60: 93246f46c9d7 = 95: 4b2afe0ca309 winansi: check result and Buffer before using Name
  • 61: f1492d07d983 = 96: 0e947d083d36 mingw: change core.fsyncObjectFiles = 1 by default
  • 63: 17bc91ef97cc = 97: 4c56ed93350f Fix Windows version resources
  • 64: 7af04a575e15 = 98: c7ba31217d6c status: fix for old-style submodules with commondir
  • 88: 9d008e8471e2 = 99: 673789c17bfa git.rc: include winuser.h
  • 89: 0edcf73b6986 = 100: 1de31624b031 mingw: do load libcurl dynamically by default
  • 90: 693599f62dbe = 101: 4955db044c4b Add a GitHub workflow to verify that Git/Scalar work in Nano Server
  • 91: 339575303a65 = 102: 4a3dd5136577 mingw: suggest windows.appendAtomically in more cases
  • 92: ee7fb0600ed4 = 103: f06a3f47e3a3 win32: use native ANSI sequence processing, if possible
  • 93: 8bbb07055158 = 104: fe1efc6436a1 common-main.c: fflush stdout buffer upon exit
  • 94: 27467b1805aa = 105: 47cab21b1581 t5601/t7406(mingw): do run tests with symlink support
  • 95: 4f0f35f624c0 = 106: be960876a5af win32: ensure that localtime_r() is declared even in i686 builds
  • 96: 26c2afafe5d7 = 107: ac2bdcab59c9 Fallback to AppData if XDG_CONFIG_HOME is unset
  • 97: 0a25b7694ea4 = 108: e99661b5b648 run-command: be helpful with Git LFS fails on Windows 7
  • 112: 3ea8ed45a41d = 109: 7ab8873b7c91 survey: clearly note the experimental nature in the output
  • 107: 93f43f51ddf1 = 110: 74db68078d8d mingw: make sure errno is set correctly when socket operations fail
  • 109: 0a45e4674590 = 111: 3537d1525359 compat/mingw: handle WSA errors in strerror
  • 111: 1f3ca1f515b5 = 112: 83a96134dfa3 compat/mingw: drop outdated comment
  • 113: ad43de1eac6e = 113: f2c63c3788aa t0301: actually test credential-cache on Windows
  • 114: 793c95d606be = 114: 03d0bfc6d97c credential-cache: handle ECONNREFUSED gracefully
  • 115: de91a2bc0982 = 115: a2c8dca2be91 reftable: do make sure to use custom allocators
  • 116: 086f484f6eaa = 116: 095fd540c88d check-whitespace: avoid alerts about upstream commits
  • 117: 3039ca3a8809 = 117: 63ef58733f50 t/t5571-prep-push-hook.sh: Add test with writing to stderr
  • 118: 5aa362abb5e0 = 118: 63d511134713 git-svn: mark it as unsupported by the Git for Windows project
  • 119: 493d720c3c29 = 119: d96fa5145e60 ci(macos): skip the git p4 tests
  • 120: 76b8c19eb4ad = 120: 60b6a2231fed git-gui--askyesno: fix funny text wrapping
  • 121: 6c02e0ddd94b = 121: 24cc88122207 Win32: make FILETIME conversion functions public
  • 123: 7f831cf9fe3d = 122: cdb8e14f608f Win32: dirent.c: Move opendir down
  • 124: 1f8cacef89cf = 123: e6727912fee2 mingw: make the dirent implementation pluggable
  • 125: d10cc1c8817a = 124: 2db04e71cc91 Win32: make the lstat implementation pluggable
  • 126: 318f35a03d41 = 125: d972e43beb42 mingw: add infrastructure for read-only file system level caches
  • 127: d21074aa7c4d = 126: c17bd8df21ff mingw: add a cache below mingw's lstat and dirent implementations
  • 128: 090b1d55017b = 127: 4ee94256bac3 fscache: load directories only once
  • 129: 8dacfca8e807 = 128: 1dd331a7eb8b fscache: add key for GIT_TRACE_FSCACHE
  • 130: e4a06fd8fb04 = 129: 35e24fd1ae8c fscache: remember not-found directories
  • 131: ec8a5c2a7e90 = 130: 6e4e2074ab2c fscache: add a test for the dir-not-found optimization
  • 132: 89ac088ddf37 = 131: 7cbc6953a9c7 add: use preload-index and fscache for performance
  • 133: 314860c36ec7 = 132: 689c98eddde2 dir.c: make add_excludes aware of fscache during status
  • 134: 80977c82bed7 = 133: 45bb34dcea9c fscache: make fscache_enabled() public
  • 135: e6e206872df7 = 134: 5cfaecd877f9 dir.c: regression fix for add_excludes with fscache
  • 136: 903e383bf8ff = 135: 4f107ff688e3 fetch-pack.c: enable fscache for stats under .git/objects
  • 137: 44cd3957b88f = 136: 11ebb3e84464 checkout.c: enable fscache for checkout again
  • 138: 1340412f61cc = 137: 0b883ccde310 Enable the filesystem cache (fscache) in refresh_index().
  • 139: 3aaa230def55 = 138: 12d40b290f96 fscache: use FindFirstFileExW to avoid retrieving the short name
  • 140: 77168a36fd75 = 139: 195a34f9c1ae fscache: add GIT_TEST_FSCACHE support
  • 141: d5ce36ac9509 = 140: 259eabb9927c fscache: add fscache hit statistics
  • 142: 234b4cf85144 = 141: 1e7b988854be unpack-trees: enable fscache for sparse-checkout
  • 143: 225675f79aa1 = 142: 136cea43eab2 status: disable and free fscache at the end of the status command
  • 144: 750d093cc226 = 143: 26d96e6767d2 mem_pool: add GIT_TRACE_MEMPOOL support
  • 145: a4e29092582b = 144: 71e600bbf9c9 fscache: fscache takes an initial size
  • 146: 4a3239d1988f = 145: 24e008a827aa fscache: update fscache to be thread specific instead of global
  • 147: e19ab54fb249 = 146: 06a79a14520f fscache: teach fscache to use mempool
  • 148: 0a67e3c9334b = 147: de914f587944 fscache: make fscache_enable() thread safe
  • 149: e198df135cf8 = 148: 3947827dc68e fscache: teach fscache to use NtQueryDirectoryFile
  • 150: 019962b49513 = 149: 0260526e4fa6 fscache: remember the reparse tag for each entry
  • 151: 7d86876d8f9e = 150: fc70303d5ba4 fscache: implement an FSCache-aware is_mount_point()
  • 152: ed8ab892d3d0 = 151: 3d8b2520b88f clean: make use of FSCache
  • 122: c637077145d1 = 152: 27d6844cb115 git-gui--askyesno (mingw): use Git for Windows' icon, if available
  • 153: c6ca7b78dacd = 153: 82d0a473f4d1 pack-objects (mingw): demonstrate a segmentation fault with large deltas
  • 154: 7d336512deb5 = 154: ef4790f2565c mingw: support long paths
  • 155: 94c695274e47 = 155: c08cb93033bf win32(long path support): leave drive-less absolute paths intact
  • 156: 445480c0bb48 = 156: 7db7139a0983 compat/fsmonitor/fsm-*-win32: support long paths
  • 157: 5c7c5b4c5834 = 157: 6f084d142f27 clean: suggest using core.longPaths if paths are too long to remove
  • 158: 7097cb149a37 = 158: 05d1f3708854 mingw: Support git_terminal_prompt with more terminals
  • 159: af665751ca52 = 159: 35806e2f1a00 compat/terminal.c: only use the Windows console if bash 'read -r' fails
  • 160: 6b5ddd5f2933 = 160: c44959f71ccd mingw (git_terminal_prompt): do fall back to CONIN$/CONOUT$ method
  • 166: c744a09eaf81 = 161: 3f4046f51949 Win32: symlink: move phantom symlink creation to a separate function
  • 167: 25fb14389136 = 162: 4f6a3f37f524 Introduce helper to create symlinks that knows about index_state
  • 168: cf32cfea1c8f = 163: 409171c57994 mingw: allow to specify the symlink type in .gitattributes
  • 169: e60aa0b8b048 = 164: 7eff838a0237 Win32: symlink: add test for symlink attribute
  • 170: 13a334a52fc2 = 165: 4e49dd2daccb mingw: explicitly specify with which cmd to prefix the cmdline
  • 171: adfc87cec578 = 166: bb105ea9a723 mingw: when path_lookup() failed, try BusyBox
  • 172: cbba0d3aa5d1 = 167: 28b60447be5b test-tool: learn to act as a drop-in replacement for iconv
  • 173: e6e112ee41aa = 168: 5447aa8c3e6a tests(mingw): if iconv is unavailable, use test-helper --iconv
  • 161: 34844eae97e2 = 169: c35ab37c20fb mingw: introduce code to detect whether we're inside a Windows container
  • 174: df88a47db503 = 170: 7b71006502f7 gitattributes: mark .png files as binary
  • 162: fd36a90a6fdb = 171: 6b8d5d4b6d59 mingw: when running in a Windows container, try to rename() harder
  • 175: ffe90db6d7d8 = 172: 0b4459caa563 tests: move test PNGs into t/lib-diff/
  • 163: 7ad2aacffacf = 173: c223a1ce20e9 mingw: move the file_attr_to_st_mode() function definition
  • 176: f9249afe3651 = 174: f948ff93b1a0 tests: only override sort & find if there are usable ones in /usr/bin/
  • 164: 3b679d9b0cd8 = 175: e5b9c42169b0 mingw: Windows Docker volumes are not symbolic links
  • 177: 800fc2eb8708 = 176: f1a637df7394 tests: use the correct path separator with BusyBox
  • 165: a730a86cdb1a = 177: 87f9cf902a90 mingw: work around rename() failing on a read-only file
  • 178: fcf6be04168c = 178: 989b2ced0998 mingw: only use Bash-ism builtin pwd -W when available
  • 179: a65621494f04 = 179: d1c041a3f887 tests (mingw): remove Bash-specific pwd option
  • 180: b344cc497ef8 = 180: af2562e2b0ce test-lib: add BUSYBOX prerequisite
  • 181: aab4afa636b0 = 181: 495f989abd1b t5003: use binary file from t/lib-diff/
  • 182: c06d0ed48383 = 182: 0108c9d5f948 t5532: workaround for BusyBox on Windows
  • 183: e7c79f941a6a = 183: f31104e89b91 t5605: special-case hardlink test for BusyBox-w32
  • 184: ebecb17a7ef4 = 184: b88172bc2307 t5813: allow for $PWD to be a Windows path
  • 185: 8c468565752c = 185: d2c974ba0cae t9200: skip tests when $PWD contains a colon
  • 191: 3a346af8bed8 = 186: ec9ae946e314 Describe Git for Windows' architecture [no ci]
  • 186: 8460cbc3e12a = 187: b71c699908b7 mingw: kill child processes in a gentler way
  • 187: 6345708468a3 = 188: 8663e416140e mingw: optionally enable wsl compability file mode bits
  • 192: bc85b710647d = 189: c3583ec37bd9 Modify the Code of Conduct for Git for Windows
  • 188: fe2637b25cf5 = 190: 7ca6eb3e5644 mingw: really handle SIGINT
  • 193: 60ba9185996b = 191: 2364fc9765ca CONTRIBUTING.md: add guide for first-time contributors
  • 189: 02b6f87daf12 = 192: 4bd3b0ad8ba4 Partially un-revert "editor: save and reset terminal after calling EDITOR"
  • 195: 32054c64df16 = 193: 4e34dba679be README.md: Add a Windows-specific preamble
  • 190: fdfe0b8f7c16 = 194: 43a1e9754ab7 reset: reinstate support for the deprecated --stdin option
  • 198: 3c2b4967dd3e = 195: 7550dc6f5a97 Add an issue template
  • 194: a5486aa05e70 = 196: 0dfc6ac40b44 Add a GitHub workflow to monitor component updates
  • 196: e76d7dd81519 = 197: 3361315e962a fsmonitor: reintroduce core.useBuiltinFSMonitor
  • 199: 8744e7447e02 = 198: 31249ab3d5d9 Modify the GitHub Pull Request template (to reflect Git for Windows)
  • 197: fd7a24cf7b00 = 199: 9f60bff4badb dependabot: help keeping GitHub Actions versions up to date
  • 200: 070a407d7c4d = 200: 3508f09804d2 SECURITY.md: document Git for Windows' policies

So this looks a bit suspicious, doesn't it? Therefore, I had a bit of a deeper look at the logs:

The commit 519b619b85 (pack-objects (mingw): demonstrate a segmentation fault with large deltas) required AI to resolve:

AI output
● Show REBASE_HEAD patch stats
  $ cd
  /home/runner/work/git-for-windows-automation/git-for-windows-automation/git/rebase-worktree-next
  && git show --stat REBASE_HEAD | head -20
  └ 21 lines...

● Show REBASE_HEAD commit message
  $ cd
  /home/runner/work/git-for-windows-automation/git-for-windows-automation/git/rebase-worktree-next
  && git log --oneline -1 REBASE_HEAD
  └ 2 lines...

● Find conflict markers
  $ cd
  /home/runner/work/git-for-windows-automation/git-for-windows-automation/git/rebase-worktree-next
  && grep -n '<<<<<<<\|=======\|>>>>>>>' t/meson.build | head -30
  └ 4 lines...

● Read t/meson.build lines 888-905
  └ 18 lines read

● Show patch for t/meson.build
  $ cd
  /home/runner/work/git-for-windows-automation/git-for-windows-automation/git/rebase-worktree-next
  && git show REBASE_HEAD -- t/meson.build
  └ 31 lines...

● Check if upstreamed
  $ cd
  /home/runner/work/git-for-windows-automation/git-for-windows-automation/git/rebase-worktree-next
  && git range-diff REBASE_HEAD^! REBASE_HEAD.. 2>&1 | head -40
  └ 41 lines...

● Edit t/meson.build (-3)

● Stage resolved file and verify no markers
  $ cd
  /home/runner/work/git-for-windows-automation/git-for-windows-automation/git/rebase-worktree-next
  && git add t/meson.build && grep -c '<<<<<<<\|=======\|>>>>>>>' t/meson.build; echo "exit: $?"
  └ 3 lines...

No conflict markers remain. Both entries kept in sorted order (t7425 before t7429).

continue


Total usage est:        3 Premium requests
API time spent:         27s
Total session time:     32s
Total code changes:     +0 -3
Breakdown by AI model:
 claude-opus-4.6         122.1k in, 1.3k out, 95.4k cached (Est. 3 Premium requests)

The reasoning is sound!

However, this was the only commit that was resolved using AI, which is a bit suspicious because the trickier merge conflicts were actually in the FSCache vs symlink patches, e.g. a83fccbf2112 (mingw: Windows Docker volumes are not symbolic links). The reason for that was a merging-rebase of main between that pre-/post-rebase; I failed to realize that this would include my own merge conflict resolutions from the main branch itself!

So while this was an okay test run, it still did not require any hard answers to merge conflicts. I will perform another test that is a lot harder: re-do that merging-rebase that was performed between above-mentioned pre-/post-rebase tip commits of shears/next.

@dscho

dscho commented Feb 17, 2026

Copy link
Copy Markdown
Member Author

I will perform another test that is a lot harder: re-do that merging-rebase that was performed between above-mentioned pre-/post-rebase tip commits of shears/next.

Okay, this is between 5f32b8db3cac437d96ce181fdd70aa3d1ac92f30 and 3832dd2481ce7e25d18511420fca0e5567c6ac54:

Range-diff
  • 1: b7572cc797c4 = 1: 72f5de3454f0 sideband: mask control characters

  • 2: c9101ecf7921 = 2: 69ad7769d91b sideband: introduce an "escape hatch" to allow control characters

  • 3: 201a9e8cb773 = 3: ed536c92320e sideband: do allow ANSI color sequences by default

  • 4: e2b8e90103df = 4: 2cd5611bd9c6 unix-socket: avoid leak when initialization fails

  • 5: 0db877b1c178 = 5: 154c3b8f2c8e grep: prevent ^$ false match at end of file

  • 6: e5598f48d0cf = 6: 61ca56afb7ea t9350: point out that refs are not updated correctly

  • 7: 3d87e0a9f8e6 = 7: ae83d576a7d0 transport-helper: add trailing --

  • 9: ddd5b65816eb = 8: baaf71713bf5 remote-helper: check helper status after import/export

  • 10: 50eaec49b1bd = 9: 579319b620a4 clean: do not traverse mount points

  • 12: 2fc76a5efa83 = 10: f0d37931c68a Always auto-gc after calling a fast-import transport

  • 13: e428fcd9f41a = 11: f5f40f3b121a mingw: prevent regressions with "drive-less" absolute paths

  • 8: c132a44d8a90 = 12: 5d6daeebfb78 mingw: include the Python parts in the build

  • 11: 77e482b30545 = 13: 2f738e48d2b0 win32/pthread: avoid name clashes with winpthread

  • 15: 6af4208c72b6 = 14: 0f84586a4735 git-compat-util: avoid redeclaring _DEFAULT_SOURCE

  • 92: 6ea9c7be1472 = 15: e7cdf876a90c Import the source code of mimalloc v2.2.7

  • 93: 9e36739c1277 = 16: 2f285aea9f17 mimalloc: adjust for building inside Git

  • 94: 8fec3de90aa5 = 17: 7e3722cc824f mimalloc: offer a build-time option to enable it

  • 16: b6ed1cc1042b = 18: 299cf470515c mingw: demonstrate a git add issue with NTFS junctions

  • 95: e12539bab89d = 19: 9c43955fed47 mingw: use mimalloc

  • 14: 64cc5c080950 = 20: ee43a66a8439 clean: remove mount points when possible

  • 18: 8d3faa143e48 = 21: 6f6e8f424500 transport: optionally disable side-band-64k

  • 19: 7b837f59c7f4 = 22: 2c41f19e894d mingw: fix fatal error working on mapped network drives on Windows

  • 20: dafc08f6b476 = 23: 963231342a49 clink.pl: fix MSVC compile script to handle libcurl-d.lib

  • 22: ba8efe4645f5 = 24: 71dccee64376 vcpkg_install: detect lack of Git

  • 24: 1a4656368d76 = 25: ea8a6b7eda22 vcpkg_install: add comment regarding slow network connections

  • 17: 8efcfa2c86c9 = 26: ddf039e5ffee strbuf_realpath(): use platform-dependent API if available

  • 23: 52fa2ffb2c02 = 27: 14dc73634a97 t5505/t5516: allow running without .git/branches/ in the templates

  • 31: 8d6eca973f46 = 28: e94d8273ab34 vcbuild: install ARM64 dependencies when building ARM64 binaries

  • 25: 993102277a66 = 29: 69135d81fd7d http: use new "best effort" strategy for Secure Channel revoke checking

  • 21: a69a07602361 = 30: 065aaec6464e mingw: implement a platform-specific strbuf_realpath()

  • 26: d941d761e9ff = 31: db0e354abb91 t5505/t5516: fix white-space around redirectors

  • 27: feb30ece07f8 = 32: 0924bbd7b1af t3701: verify that we can add lots of files interactively

  • 28: 49bb910a775c = 33: d00df93e0aaa commit: accept "scissors" with CR/LF line endings

  • 29: 9a3cde11b6a2 = 34: 26563b25dece t0014: fix indentation

  • 30: 977e14f630f4 = 35: 63e838f85932 git-gui: accommodate for intent-to-add files

  • 32: b9a643e905eb = 36: c3c19beffc96 vcbuild: add an option to install individual 'features'

  • 33: b74f300ff7ed = 37: 7262616276ed cmake: allow building for Windows/ARM64

  • 34: deee242c9048 = 38: 8bea01b12e71 ci(vs-build) also build Windows/ARM64 artifacts

  • 35: 15d349650d5d = 39: 18aa03feffe9 Add schannel to curl installation

  • 36: 981b75244671 = 40: 702dc2683b5c cmake(): allow setting HOST_CPU for cross-compilation

  • 41: 1c43a63a0d1c = 41: 88a65bf26e6c CMake: default Visual Studio generator has changed

  • 40: 26d62a914424 = 42: 1c0fe1a018d9 hash-object: demonstrate a >4GB/LLP64 problem

  • 43: 12cda9758597 = 43: ee8cdf0cd7eb .gitignore: add Visual Studio CMakeSetting.json file

  • 42: 6a4b7be2722e = 44: da87446dceb5 object-file.c: use size_t for header lengths

  • 45: ea7b2b444afb = 45: 6057555be577 subtree: update contrib/subtree test target

  • 46: d7f4f1ec494b = 46: 24849e3602f7 CMakeLists: add default "x64-windows" arch for Visual Studio

  • 44: bf99aaf19925 = 47: d5fab9ba0cda hash algorithms: use size_t for section lengths

  • 37: e618f991815f = 48: f154867f2566 mingw: allow for longer paths in parse_interpreter()

  • 38: 2d236821598b = 49: 92b17b15d057 compat/vcbuild: document preferred way to build in Visual Studio

  • 39: 378c4b16f26d = 50: 6d4c9d6bccd4 http: optionally send SSL client certificate

  • 48: 24dcc3df8bdf = 51: 757a2c9bd586 ci: run contrib/subtree tests in CI builds

  • 49: c19744fc3ebd = 52: 23246b4397a8 CMake: show Win32 and Generator_platform build-option values

  • 47: 59957d333f5a = 53: 28101c6ae052 hash-object --stdin: verify that it works with >4GB/LLP64

  • 50: 35f6b163c0b8 = 54: 770dec44be09 hash-object: add another >4GB/LLP64 test case

  • 51: a0c7290dbfa3 = 55: dbcd7123712a setup: properly use "%(prefix)/" when in WSL

  • 52: 3f5f17764e9c = 56: 10f0986c6357 Add config option windows.appendAtomically

  • 53: 536a9c2a52f3 = 57: 9ea106018ca8 hash-object: add a >4GB/LLP64 test case using filtered input

  • 54: cd35bf4b339d = 58: d8f746f542d2 compat/mingw.c: do not warn when failing to get owner

  • 55: 3b7160359213 = 59: cb452cf7584c mingw: $env:TERM="xterm-256color" for newer OSes

  • 56: 51a43eed501e = 60: 53be802501cb winansi: check result and Buffer before using Name

  • 58: 8b99bae1ec8f ! 61: 6a8a5a922131 mingw: change core.fsyncObjectFiles = 1 by default

    @@ compat/mingw.c
      #include <conio.h>
      #include <sddl.h>
     @@ compat/mingw.c: int wmain(int argc, const wchar_t **wargv)
    - #endif
      
      	maybe_redirect_std_handles();
    + 	adjust_symlink_flags();
     +	fsync_object_files = 1;
      
      	/* determine size of argv and environ conversion buffer */
  • 57: 9a55aac24df6 = 62: 4f2721c77896 MinGW: link as terminal server aware

  • 59: 26f1d08e22f7 = 63: 4337ef00a176 Fix Windows version resources

  • 60: e30b81fd3fbe = 64: 906ddd07a915 status: fix for old-style submodules with commondir

  • 96: f474630f6aac = 65: 55d10664920c windows: skip linking git-<command> for built-ins

  • 97: c1ef2a6c1627 = 66: dd3b541d21d7 mingw: stop hard-coding CC = gcc

  • 98: 05de8af920f0 = 67: 6855ef0b350b mingw: drop the -D_USE_32BIT_TIME_T option

  • 99: 918ba0775c4e = 68: fcae0f80ebd9 mingw: only use -Wl,--large-address-aware for 32-bit builds

  • 100: 1aa530107ef7 = 69: fb3404d94af5 mingw: avoid over-specifying --pic-executable

  • 101: 72e167c2ad07 = 70: 3471074861fd mingw: set the prefix and HOST_CPU as per MSYS2's settings

  • 102: 0b2e0b1ea5ea = 71: 75886a3bc8e4 mingw: only enable the MSYS2-specific stuff when compiling in MSYS2

  • 103: d9d089e9fd2e = 72: 71c1841f0e93 mingw: rely on MSYS2's metadata instead of hard-coding it

  • 104: 296bcb14c07c = 73: d1f0a5ba34e0 mingw: always define ETC_* for MSYS2 environments

  • 105: 04aa4bc48e2d ! 74: e23ad91110c1 max_tree_depth: lower it for clang builds in general on Windows

    @@ Commit message
     
         Signed-off-by: Johannes Schindelin <[email protected]>
     
    - ## environment.c ##
    -@@ environment.c: int max_allowed_tree_depth =
    - 	 * the stack overflow can occur.
    - 	 */
    - 	512;
    + ## git-compat-util.h ##
    +@@ git-compat-util.h: static inline bool strip_suffix(const char *str, const char *suffix,
    +    * the stack overflow can occur.
    +    */
    + #define DEFAULT_MAX_ALLOWED_TREE_DEPTH 512
     -#elif defined(GIT_WINDOWS_NATIVE) && defined(__clang__) && defined(__aarch64__)
     +#elif defined(GIT_WINDOWS_NATIVE) && defined(__clang__)
    - 	/*
    --	 * Similar to Visual C, it seems that on Windows/ARM64 the clang-based
    --	 * builds have a smaller stack space available. When running out of
    --	 * that stack space, a `STATUS_STACK_OVERFLOW` is produced. When the
    -+	 * Similar to Visual C, it seems that clang-based builds on Windows
    -+	 * have a smaller stack space available. When running out of that
    -+	 * stack space, a `STATUS_STACK_OVERFLOW` is produced. When the
    - 	 * Git command was run from an MSYS2 Bash, this unfortunately results
    - 	 * in an exit code 127. Let's prevent that by lowering the maximal
    --	 * tree depth; This value seems to be low enough.
    -+	 * tree depth; Unfortunately, it seems that the exact limit differs
    -+	 * for aarch64 vs x86_64, and the difference is too large to simply
    -+	 * use a single limit.
    - 	 */
    +   /*
    +-   * Similar to Visual C, it seems that on Windows/ARM64 the clang-based
    +-   * builds have a smaller stack space available. When running out of
    +-   * that stack space, a `STATUS_STACK_OVERFLOW` is produced. When the
    ++   * Similar to Visual C, it seems that clang-based builds on Windows
    ++   * have a smaller stack space available. When running out of that
    ++   * stack space, a `STATUS_STACK_OVERFLOW` is produced. When the
    +    * Git command was run from an MSYS2 Bash, this unfortunately results
    +    * in an exit code 127. Let's prevent that by lowering the maximal
    +-   * tree depth; This value seems to be low enough.
    ++   * tree depth; Unfortunately, it seems that the exact limit differs
    ++   * for aarch64 vs x86_64, and the difference is too large to simply
    ++   * use a single limit.
    +    */
     +#if defined(__aarch64__)
    - 	1280;
    -+#else
    -+	1152;
    -+#endif
    + #define DEFAULT_MAX_ALLOWED_TREE_DEPTH 1280
      #else
    - 	2048;
    ++#define DEFAULT_MAX_ALLOWED_TREE_DEPTH 1152
    ++#endif
    ++#else
    + #define DEFAULT_MAX_ALLOWED_TREE_DEPTH 2048
      #endif
    + 
  • 61: 5e3de953b2d7 = 75: 7af555c5b979 http: optionally load libcurl lazily

  • 62: 34ee3680a742 = 76: 5ef2a8d03ff8 http: support lazy-loading libcurl also on Windows

  • 67: d9992b0a497c = 77: 7238dd9bc115 ci: work around a problem with HTTP/2 vs libcurl v8.10.0

  • 63: 6fa7c1f4707e = 78: dd918bd51d5e http: when loading libcurl lazily, allow for multiple SSL backends

  • 68: 0ca7e765ad0c = 79: ad2b49eaab35 revision: create mark_trees_uninteresting_dense()

  • 64: b7e20647d737 = 80: 9e5b795a9a92 mingw: do load libcurl dynamically by default

  • 65: 61df8a70fb97 = 81: 11ef03e8275c Add a GitHub workflow to verify that Git/Scalar work in Nano Server

  • 66: 3ee2cd67cea0 = 82: 0a76992d6295 mingw: suggest windows.appendAtomically in more cases

  • 69: 8af6f168498a = 83: 650a165ef91a win32: use native ANSI sequence processing, if possible

  • 70: 503d48a90714 = 84: 9da49cabc171 common-main.c: fflush stdout buffer upon exit

  • 71: 232239bfaa9c = 85: 025e9a85c992 t5601/t7406(mingw): do run tests with symlink support

  • 72: 5c8e9d608300 = 86: e4e770cf1053 win32: ensure that localtime_r() is declared even in i686 builds

  • 73: 95025b74073a = 87: 3e9b12fd0d0a Fallback to AppData if XDG_CONFIG_HOME is unset

  • 74: 4b35363ba0c0 = 88: 70b9ce9fe105 run-command: be helpful with Git LFS fails on Windows 7

  • 75: 1e0732538512 = 89: c99f45051c05 survey: stub in new experimental 'git-survey' command

  • 76: 64e2fe8dff77 = 90: ac99a8f35263 survey: add command line opts to select references

  • 77: 9ffe2fa8ee12 = 91: 949cf92170dd survey: start pretty printing data in table form

  • 78: 07d54398bdb9 = 92: 1743754fefa7 survey: add object count summary

  • 79: cb3a6493c5bd = 93: 03f77f8e970c survey: summarize total sizes by object type

  • 80: aff3c05c578a = 94: 7879885db360 survey: show progress during object walk

  • 82: f68c8747a29f = 95: c797e5aa3a57 mingw: make sure errno is set correctly when socket operations fail

  • 81: 825465c6f702 = 96: 3dcc8daf863c survey: add ability to track prioritized lists

  • 84: 59cf350f675f = 97: 6358efd8b247 compat/mingw: handle WSA errors in strerror

  • 83: 9236c2948db2 = 98: 338e9b93604a survey: add report of "largest" paths

  • 86: ab302c5b8207 = 99: 79c8bcc69544 compat/mingw: drop outdated comment

  • 85: 72e6d68a7428 = 100: 07beb8f5f5da survey: add --top= option and config

  • 88: 3c8010b6c36b = 101: 3ce045cd6641 t0301: actually test credential-cache on Windows

  • 87: a7462d2cc15f = 102: e04c2735ea9d survey: clearly note the experimental nature in the output

  • 89: 2058ce7f717e = 103: fdc5dd88d808 credential-cache: handle ECONNREFUSED gracefully

  • 90: 7cd468e8f686 = 104: 746fc5e0ce09 reftable: do make sure to use custom allocators

  • 91: 8d4233e162cb = 105: e1a6400f8db4 check-whitespace: avoid alerts about upstream commits

  • 177: 797f4d27d1e2 ! 106: 541d54bc0121 mingw: try to create symlinks without elevated permissions

    @@
      ## Metadata ##
    -Author: Johannes Schindelin <[email protected]>
    +Author: Thomas Braun <[email protected]>
     
      ## Commit message ##
    -    mingw: try to create symlinks without elevated permissions
    +    t/t5571-prep-push-hook.sh: Add test with writing to stderr
     
    -    With 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).
    +    The 2.53.0.rc0.windows release candidate had a regression where
    +    writing to stderr from a pre-push hook would error out.
     
    -    This new flag is called SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE and
    -    has the numeric value 0x02.
    +    The regression was fixed in 2.53.0.rc1.windows and the test here ensures
    +    that this stays fixed.
     
    -    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.
    +    Signed-off-by: Thomas Braun <[email protected]>
     
    -    For more information about the new flag, see this blog post:
    -    https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/
    -
    -    This patch is loosely based on the patch submitted by Samuel D. Leslie
    -    as https://github.com/git-for-windows/git/pull/1184.
    -
    -    Signed-off-by: Johannes Schindelin <[email protected]>
    -
    - ## compat/mingw.c ##
    -@@ compat/mingw.c: static const wchar_t *make_relative_to(const wchar_t *path,
    - 	return out;
    - }
    - 
    -+static DWORD symlink_file_flags = 0, symlink_directory_flags = 1;
    -+
    - enum phantom_symlink_result {
    - 	PHANTOM_SYMLINK_RETRY,
    - 	PHANTOM_SYMLINK_DONE,
    -@@ compat/mingw.c: process_phantom_symlink(const wchar_t *wtarget, const wchar_t *wlink)
    - 		return PHANTOM_SYMLINK_DONE;
    + ## t/t5571-pre-push-hook.sh ##
    +@@ t/t5571-pre-push-hook.sh: test_expect_success 'sigpipe does not cause pre-push hook failure' '
    + 	git push parent1 "refs/heads/b/*:refs/heads/b/*"
    + '
      
    - 	/* otherwise recreate the symlink with directory flag */
    --	if (DeleteFileW(wlink) && CreateSymbolicLinkW(wlink, wtarget, 1))
    -+	if (DeleteFileW(wlink) &&
    -+	    CreateSymbolicLinkW(wlink, wtarget, symlink_directory_flags))
    - 		return PHANTOM_SYMLINK_DIRECTORY;
    - 
    - 	errno = err_win_to_posix(GetLastError());
    -@@ compat/mingw.c: int symlink(const char *target, const char *link)
    - 			wtarget[len] = '\\';
    - 
    - 	/* create file symlink */
    --	if (!CreateSymbolicLinkW(wlink, wtarget, 0)) {
    -+	if (!CreateSymbolicLinkW(wlink, wtarget, symlink_file_flags)) {
    - 		errno = err_win_to_posix(GetLastError());
    - 		return -1;
    - 	}
    -@@ compat/mingw.c: static void maybe_redirect_std_handles(void)
    - 				  GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
    - }
    - 
    -+static void adjust_symlink_flags(void)
    -+{
    -+	/*
    -+	 * Starting with Windows 10 Build 14972, symbolic links can be created
    -+	 * using CreateSymbolicLink() without elevation by passing the flag
    -+	 * SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x02) as last
    -+	 * parameter, provided the Developer Mode has been enabled. Some
    -+	 * earlier Windows versions complain about this flag with an
    -+	 * ERROR_INVALID_PARAMETER, hence we have to test the build number
    -+	 * specifically.
    -+	 */
    -+	if (GetVersion() >= 14972 << 16) {
    -+		symlink_file_flags |= 2;
    -+		symlink_directory_flags |= 2;
    -+	}
    ++test_expect_success 'can write to stderr' '
    ++	test_hook --clobber pre-push <<-\EOF &&
    ++	echo foo >/dev/stderr &&
    ++	exit 0
    ++	EOF
     +
    -+}
    ++	test_commit third &&
    ++	echo foo >expect &&
    ++	git push --quiet parent1 HEAD 2>actual &&
    ++	test_cmp expect actual
    ++'
     +
    - #ifdef _MSC_VER
    - #ifdef _DEBUG
    - #include <crtdbg.h>
    -@@ compat/mingw.c: int wmain(int argc, const wchar_t **wargv)
    - #endif
    - 
    - 	maybe_redirect_std_handles();
    -+	adjust_symlink_flags();
    - 	fsync_object_files = 1;
    - 
    - 	/* determine size of argv and environ conversion buffer */
    + test_done
  • 106: 28dd1b6f635f ! 107: b53fe0e7edda mingw: ensure valid CTYPE

    @@ Commit message
     
      ## compat/mingw.c ##
     @@ compat/mingw.c: static void setup_windows_environment(void)
    - 		if (!tmp && (tmp = getenv("USERPROFILE")))
      			setenv("HOME", tmp, 1);
      	}
    -+
    + 
     +	if (!getenv("LC_ALL") && !getenv("LC_CTYPE") && !getenv("LANG"))
     +		setenv("LC_CTYPE", "C.UTF-8", 1);
    - }
    - 
    - static void get_current_user_sid(PSID *sid, HANDLE *linked_token)
    ++
    + 	/*
    + 	 * Change 'core.symlinks' default to false, unless native symlinks are
    + 	 * enabled in MSys2 (via 'MSYS=winsymlinks:nativestrict'). Thus we can
  • 107: 32b25ffea7f8 ! 108: 8e2d9a909894 mingw: allow git.exe to be used instead of the "Git wrapper"

    @@ compat/mingw.c: static void setup_windows_environment(void)
     +
      	if (!getenv("LC_ALL") && !getenv("LC_CTYPE") && !getenv("LANG"))
      		setenv("LC_CTYPE", "C.UTF-8", 1);
    - }
    + 
     
      ## config.mak.uname ##
     @@ config.mak.uname: endif
  • 108: 82251529c3be = 109: 9a0a9c1ded8b mingw: ignore HOMEDRIVE/HOMEPATH if it points to Windows' system directory

  • 109: 50fbcd2bcd65 = 110: b37402aaf8cc clink.pl: fix libexpatd.lib link error when using MSVC

  • 110: 725cb5896e9d = 111: eb66711b2cba Makefile: clean up .ilk files when MSVC=1

  • 111: f19d985da8ec = 112: 26af83426c14 vcbuild: add support for compiling Windows resource files

  • 112: c6db63d68909 = 113: 4d1cc403e039 config.mak.uname: add git.rc to MSVC builds

  • 113: edbffbbcda30 = 114: 4a2e42ab88bd clink.pl: ignore no-stack-protector arg on MSVC=1 builds

  • 114: 2f0100201860 = 115: a127f482d598 clink.pl: move default linker options for MSVC=1 builds

  • 115: 0f05ae184a6a = 116: 2910ae0743a6 cmake: install headless-git.

  • 116: 5f107688b5cf = 117: 97118b010184 git.rc: include winuser.h

  • 117: f5ec36ebc962 = 118: a01bd81ccbb8 git-svn: mark it as unsupported by the Git for Windows project

  • 118: e9815f74d0a1 = 119: 76c3bf3653d1 ci(macos): skip the git p4 tests

  • 121: 7f7c7928b826 = 120: 91dcc05d474e Win32: make FILETIME conversion functions public

  • 122: 91f7f63284a4 = 121: 73c6e636358c Win32: dirent.c: Move opendir down

  • 123: 9f7087c4de1a ! 122: c65ad577c841 mingw: make the dirent implementation pluggable

    @@ compat/win32/dirent.c
     +	xwcstoutf(ent->d_name, fdata->cFileName, MAX_PATH * 3);
      
      	/* Set file type, based on WIN32_FIND_DATA */
    - 	if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    + 	if ((fdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
     @@ compat/win32/dirent.c: static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
      		ent->d_type = DT_REG;
      }
  • 124: b7965dae55a6 ! 123: cb36770f6750 Win32: make the lstat implementation pluggable

    @@ compat/mingw-posix.h: int mingw_fstat(int fd, struct stat *buf);
      int mingw_utime(const char *file_name, const struct utimbuf *times);
     
      ## compat/mingw.c ##
    -@@ compat/mingw.c: static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
    - 	return do_lstat(follow, alt_name, buf);
    +@@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
    + 	return -1;
      }
      
     +int (*lstat)(const char *file_name, struct stat *buf) = mingw_lstat;
  • 125: 84d356004c0f = 124: 5ded7b7efcaa mingw: add infrastructure for read-only file system level caches

  • 126: 7c775c7edacf ! 125: bf9f0e72e00b mingw: add a cache below mingw's lstat and dirent implementations

    @@ compat/win32/fscache.c (new)
     +
     +	fse = fsentry_alloc(list, buf, len);
     +
    -+	fse->st_mode = file_attr_to_st_mode(fdata->dwFileAttributes);
    -+	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
    ++	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    ++					    fdata->EaSize);
    ++	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    ++			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
     +	fse->u.s.st_size = (((off64_t) (fdata->nFileSizeHigh)) << 32)
     +			| fdata->nFileSizeLow;
     +	filetime_to_timespec(&(fdata->ftLastAccessTime), &(fse->u.s.st_atim));
    @@ compat/win32/fscache.c (new)
     +		return -1;
     +	}
     +
    ++	/*
    ++	 * Special case symbolic links: FindFirstFile()/FindNextFile() did not
    ++	 * provide us with the length of the target path.
    ++	 */
    ++	if (fse->u.s.st_size == MAX_PATH && S_ISLNK(fse->st_mode)) {
    ++		char buf[MAX_LONG_PATH];
    ++		int len = readlink(filename, buf, sizeof(buf) - 1);
    ++
    ++		if (len > 0)
    ++			fse->u.s.st_size = len;
    ++	}
    ++
     +	/* copy stat data */
     +	st->st_ino = 0;
     +	st->st_gid = 0;
  • 127: 45711437f12c = 126: fb4c6a513570 fscache: load directories only once

  • 128: e51b4ed4189a = 127: d3105b49d9c6 fscache: add key for GIT_TRACE_FSCACHE

  • 129: 7db33ae6b7f7 = 128: 4650e1300875 fscache: remember not-found directories

  • 130: 3c8662454d62 = 129: bba3243fb6eb fscache: add a test for the dir-not-found optimization

  • 131: cc29af6d367e = 130: 89545a17e87e add: use preload-index and fscache for performance

  • 132: bebad966d009 = 131: 89d3cb27fd42 dir.c: make add_excludes aware of fscache during status

  • 133: b922002c7c2b = 132: dd81725b2cce fscache: make fscache_enabled() public

  • 119: e644b3c39752 = 133: 0bad704151f5 git-gui--askyesno: fix funny text wrapping

  • 134: 14874a115852 = 134: d74b0b0038ae dir.c: regression fix for add_excludes with fscache

  • 120: 1459c281ebdb = 135: 31ddaed68fe7 git-gui--askyesno (mingw): use Git for Windows' icon, if available

  • 135: 92c2cee2bc80 = 136: 147e66371c58 fetch-pack.c: enable fscache for stats under .git/objects

  • 136: a583117f1b0b = 137: 38e3b0b60fbf checkout.c: enable fscache for checkout again

  • 137: 183a9fd2fe7b = 138: 9c26251e35a0 Enable the filesystem cache (fscache) in refresh_index().

  • 138: 57a05d9dbcb6 = 139: 10641751c34a fscache: use FindFirstFileExW to avoid retrieving the short name

  • 139: 1623b4bf9af1 = 140: 8f3d874d8b74 fscache: add GIT_TEST_FSCACHE support

  • 140: 80ad05d4e61b = 141: ed9e62ed1a30 fscache: add fscache hit statistics

  • 141: 9087916a3fc7 = 142: 16a1f1870ab6 unpack-trees: enable fscache for sparse-checkout

  • 142: f80f1228b616 = 143: a919310c6d1e status: disable and free fscache at the end of the status command

  • 143: d677459ec280 = 144: 3dd272a340d4 mem_pool: add GIT_TRACE_MEMPOOL support

  • 144: 5a6185162e9c = 145: 86df8be1dee4 fscache: fscache takes an initial size

  • 145: d3f50e973832 = 146: 7ba4643052c6 fscache: update fscache to be thread specific instead of global

  • 146: 06d8c7439793 ! 147: 7588ad517b99 fscache: teach fscache to use mempool

    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsen
     -	fse = fsentry_alloc(list, buf, len);
     +	fse = fsentry_alloc(cache, list, buf, len);
      
    - 	fse->st_mode = file_attr_to_st_mode(fdata->dwFileAttributes);
    - 	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
    + 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    + 					    fdata->EaSize);
     @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsentry *list,
       * Dir should not contain trailing '/'. Use an empty string for the current
       * directory (not "."!).
  • 147: afa186da6383 ! 148: 260041ff5028 fscache: make fscache_enable() thread safe

    @@ compat/mingw.c
      #include "wrapper.h"
      #include "write-or-die.h"
     @@ compat/mingw.c: int wmain(int argc, const wchar_t **wargv)
    - 	/* initialize critical section for waitpid pinfo_t list */
      	InitializeCriticalSection(&pinfo_cs);
    + 	InitializeCriticalSection(&phantom_symlinks_cs);
      
     +	/* initialize critical section for fscache */
     +	InitializeCriticalSection(&fscache_cs);
  • 148: a0173342a690 ! 149: 4aa340bad70a fscache: teach fscache to use NtQueryDirectoryFile

    @@ compat/win32/fscache.c: static void fsentry_release(struct fsentry *fse)
      
      	fse = fsentry_alloc(cache, list, buf, len);
      
    --	fse->st_mode = file_attr_to_st_mode(fdata->dwFileAttributes);
    -+	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes);
    - 	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
    +@@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fscache *cache,
    + 					    fdata->EaSize);
    + 	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    + 			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
     -	fse->u.s.st_size = (((off64_t) (fdata->nFileSizeHigh)) << 32)
     -			| fdata->nFileSizeLow;
     -	filetime_to_timespec(&(fdata->ftLastAccessTime), &(fse->u.s.st_atim));
     -	filetime_to_timespec(&(fdata->ftLastWriteTime), &(fse->u.s.st_mtim));
     -	filetime_to_timespec(&(fdata->ftCreationTime), &(fse->u.s.st_ctim));
    -+	fse->u.s.st_size = fdata->EndOfFile.LowPart |
    -+		(((off_t)fdata->EndOfFile.HighPart) << 32);
    ++	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_PATH :
    ++			fdata->EndOfFile.LowPart |
    ++			(((off_t)fdata->EndOfFile.HighPart) << 32);
     +	filetime_to_timespec((FILETIME *)&(fdata->LastAccessTime),
     +			     &(fse->u.s.st_atim));
     +	filetime_to_timespec((FILETIME *)&(fdata->LastWriteTime),
  • 149: 664e5ab39de0 ! 150: 91ce9a8e3c28 fscache: remember the reparse tag for each entry

    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsca
     +		fdata->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ?
     +		fdata->EaSize : 0;
     +
    - 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes);
    - 	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
    - 	fse->u.s.st_size = fdata->EndOfFile.LowPart |
    + 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    + 					    fdata->EaSize);
    + 	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
  • 150: 463483aeb0ee = 151: a08c6ea4096c fscache: implement an FSCache-aware is_mount_point()

  • 151: ed191e3f8228 = 152: 691bc35e47a0 clean: make use of FSCache

  • 152: 2183eb6221d4 = 153: 519b619b8505 pack-objects (mingw): demonstrate a segmentation fault with large deltas

  • 153: 0d86933e615c ! 154: 0b0dc0f8082d mingw: support long paths

    @@ compat/mingw.c: static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOT
      int mingw_core_config(const char *var, const char *value,
      		      const struct config_context *ctx UNUSED,
      		      void *cb UNUSED)
    +@@ compat/mingw.c: process_phantom_symlink(const wchar_t *wtarget, const wchar_t *wlink)
    + {
    + 	HANDLE hnd;
    + 	BY_HANDLE_FILE_INFORMATION fdata;
    +-	wchar_t relative[MAX_PATH];
    ++	wchar_t relative[MAX_LONG_PATH];
    + 	const wchar_t *rel;
    + 
    + 	/* check that wlink is still a file symlink */
     @@ compat/mingw.c: static wchar_t *normalize_ntpath(wchar_t *wbuf)
      int mingw_unlink(const char *pathname, int handle_in_use_error)
      {
    - 	int ret, tries = 0;
    + 	int tries = 0;
     -	wchar_t wpathname[MAX_PATH];
     -	if (xutftowcs_path(wpathname, pathname) < 0)
     +	wchar_t wpathname[MAX_LONG_PATH];
    @@ compat/mingw.c: static int is_dir_empty(const wchar_t *wpath)
     @@ compat/mingw.c: static int is_dir_empty(const wchar_t *wpath)
      int mingw_rmdir(const char *pathname)
      {
    - 	int ret, tries = 0;
    + 	int tries = 0;
     -	wchar_t wpathname[MAX_PATH];
     +	wchar_t wpathname[MAX_LONG_PATH];
      	struct stat st;
    @@ compat/mingw.c: int mingw_rmdir(const char *pathname)
     +	if (xutftowcs_long_path(wpathname, pathname) < 0)
      		return -1;
      
    - 	while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
    + 	do {
     @@ compat/mingw.c: static int set_hidden_flag(const wchar_t *path, int set)
      int mingw_mkdir(const char *path, int mode UNUSED)
      {
    @@ compat/mingw.c: static int set_hidden_flag(const wchar_t *path, int set)
      		return -1;
     +
      	ret = _wmkdir(wpath);
    - 	if (!ret && needs_hiding(path))
    - 		return set_hidden_flag(wpath, 1);
    + 	if (!ret)
    + 		process_phantom_symlinks();
     @@ compat/mingw.c: int mingw_open (const char *filename, int oflags, ...)
      	va_list args;
      	unsigned mode;
    @@ compat/mingw.c: ssize_t mingw_write(int fd, const void *buf, size_t len)
      int mingw_chdir(const char *dirname)
      {
     -	wchar_t wdirname[MAX_PATH];
    +-
     -	if (xutftowcs_path(wdirname, dirname) < 0)
     +	int result;
     +	wchar_t wdirname[MAX_LONG_PATH];
     +	if (xutftowcs_long_path(wdirname, dirname) < 0)
      		return -1;
    --	return _wchdir(wdirname);
    -+	result = _wchdir(wdirname);
    + 
    + 	if (has_symlinks) {
    +@@ compat/mingw.c: int mingw_chdir(const char *dirname)
    + 		CloseHandle(hnd);
    + 	}
    + 
    +-	return _wchdir(normalize_ntpath(wdirname));
    ++	result = _wchdir(normalize_ntpath(wdirname));
     +	current_directory_len = GetCurrentDirectoryW(0, NULL);
     +	return result;
      }
    @@ compat/mingw.c: ssize_t mingw_write(int fd, const void *buf, size_t len)
      		return -1;
      	return _wchmod(wfilename, mode);
      }
    -@@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
    - static int do_lstat(int follow, const char *file_name, struct stat *buf)
    - {
    +@@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
      	WIN32_FILE_ATTRIBUTE_DATA fdata;
    + 	DWORD reparse_tag = 0;
    + 	int link_len = 0;
     -	wchar_t wfilename[MAX_PATH];
    --	if (xutftowcs_path(wfilename, file_name) < 0)
    +-	int wlen = xutftowcs_path(wfilename, file_name);
     +	wchar_t wfilename[MAX_LONG_PATH];
    -+	if (xutftowcs_long_path(wfilename, file_name) < 0)
    ++	int wlen = xutftowcs_long_path(wfilename, file_name);
    + 	if (wlen < 0)
      		return -1;
      
    +@@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
      	if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
    + 		/* for reparse points, get the link tag and length */
    + 		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
    +-			char tmpbuf[MAX_PATH];
    ++			char tmpbuf[MAX_LONG_PATH];
    + 
    + 			if (read_reparse_point(wfilename, FALSE, tmpbuf,
    + 					       &link_len, &reparse_tag) < 0)
    +@@ compat/mingw.c: static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
    + 
    + int mingw_stat(const char *file_name, struct stat *buf)
    + {
    +-	wchar_t wfile_name[MAX_PATH];
    ++	wchar_t wfile_name[MAX_LONG_PATH];
    + 	HANDLE hnd;
    + 	int result;
    + 
    + 	/* open the file and let Windows resolve the links */
    +-	if (xutftowcs_path(wfile_name, file_name) < 0)
    ++	if (xutftowcs_long_path(wfile_name, file_name) < 0)
    + 		return -1;
    + 	hnd = CreateFileW(wfile_name, 0,
    + 			FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
     @@ compat/mingw.c: int mingw_utime (const char *file_name, const struct utimbuf *times)
      	FILETIME mft, aft;
      	int rc;
    @@ compat/mingw.c: static pid_t mingw_spawnve_fd(const char *cmd, const char **argv
      	if (dir && xutftowcs_path(wdir, dir) < 0)
     @@ compat/mingw.c: int mingw_rename(const char *pold, const char *pnew)
      	static int supports_file_rename_info_ex = 1;
    - 	DWORD attrs, gle;
    + 	DWORD attrs = INVALID_FILE_ATTRIBUTES, gle;
      	int tries = 0;
     -	wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
     +	wchar_t wpold[MAX_LONG_PATH], wpnew[MAX_LONG_PATH];
    @@ compat/mingw.c: int mingw_raise(int sig)
      		return -1;
      
      	if (!CreateHardLinkW(wnewpath, woldpath, NULL)) {
    +@@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
    + 
    + int symlink(const char *target, const char *link)
    + {
    +-	wchar_t wtarget[MAX_PATH], wlink[MAX_PATH];
    ++	wchar_t wtarget[MAX_LONG_PATH], wlink[MAX_LONG_PATH];
    + 	int len;
    + 
    + 	/* fail if symlinks are disabled or API is not supported (WinXP) */
    +@@ compat/mingw.c: int symlink(const char *target, const char *link)
    + 		return -1;
    + 	}
    + 
    +-	if ((len = xutftowcs_path(wtarget, target)) < 0
    +-			|| xutftowcs_path(wlink, link) < 0)
    ++	if ((len = xutftowcs_long_path(wtarget, target)) < 0
    ++			|| xutftowcs_long_path(wlink, link) < 0)
    + 		return -1;
    + 
    + 	/* convert target dir separators to backslashes */
    +@@ compat/mingw.c: int symlink(const char *target, const char *link)
    + 
    + int readlink(const char *path, char *buf, size_t bufsiz)
    + {
    +-	WCHAR wpath[MAX_PATH];
    +-	char tmpbuf[MAX_PATH];
    ++	WCHAR wpath[MAX_LONG_PATH];
    ++	char tmpbuf[MAX_LONG_PATH];
    + 	int len;
    + 	DWORD tag;
    + 
    +-	if (xutftowcs_path(wpath, path) < 0)
    ++	if (xutftowcs_long_path(wpath, path) < 0)
    + 		return -1;
    + 
    + 	if (read_reparse_point(wpath, TRUE, tmpbuf, &len, &tag) < 0)
     @@ compat/mingw.c: int mingw_is_mount_point(struct strbuf *path)
      {
      	WIN32_FIND_DATAW findbuf = { 0 };
    @@ compat/win32/fscache.c: static void fsentry_init(struct fsentry *fse, struct fse
      		BUG("Trying to allocate fsentry for long path '%.*s'",
      		    (int)len, name);
      	memcpy(fse->dirent.d_name, name, len);
    +@@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fscache *cache,
    + 		fdata->EaSize : 0;
    + 
    + 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    +-					    fdata->EaSize);
    ++					    fdata->EaSize, buf);
    + 	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    + 			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
    +-	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_PATH :
    ++	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
    + 			fdata->EndOfFile.LowPart |
    + 			(((off_t)fdata->EndOfFile.HighPart) << 32);
    + 	filetime_to_timespec((FILETIME *)&(fdata->LastAccessTime),
     @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fscache *cache,
      static struct fsentry *fsentry_create_list(struct fscache *cache, const struct fsentry *dir,
      					   int *dir_not_found)
    @@ compat/win32/fscache.c: static struct fsentry *fsentry_create_list(struct fscach
      
      	/* handle CWD */
      	if (!wlen) {
    +@@ compat/win32/fscache.c: int fscache_lstat(const char *filename, struct stat *st)
    + 	 * Special case symbolic links: FindFirstFile()/FindNextFile() did not
    + 	 * provide us with the length of the target path.
    + 	 */
    +-	if (fse->u.s.st_size == MAX_PATH && S_ISLNK(fse->st_mode)) {
    ++	if (fse->u.s.st_size == MAX_LONG_PATH && S_ISLNK(fse->st_mode)) {
    + 		char buf[MAX_LONG_PATH];
    + 		int len = readlink(filename, buf, sizeof(buf) - 1);
    + 
     
      ## t/meson.build ##
     @@ t/meson.build: integration_tests = [
  • 154: 694f810e828d < -: ------------ Win32: fix 'lstat("dir/")' with long paths

  • 155: bc025c8d6371 ! 155: 2b15f836a87d win32(long path support): leave drive-less absolute paths intact

    @@ Commit message
         Signed-off-by: Johannes Schindelin <[email protected]>
     
      ## compat/mingw.c ##
    -@@ compat/mingw.c: int mingw_core_config(const char *var, const char *value,
    - 	return 0;
    - }
    - 
    -+static inline int is_wdir_sep(wchar_t wchar)
    -+{
    -+	return wchar == L'/' || wchar == L'\\';
    -+}
    -+
    - /* Normalizes NT paths as returned by some low-level APIs. */
    - static wchar_t *normalize_ntpath(wchar_t *wbuf)
    - {
     @@ compat/mingw.c: int handle_long_path(wchar_t *path, int len, int max_path, int expand)
      	 * "cwd + path" doesn't due to '..' components)
      	 */
  • 156: f87de62e94d0 = 156: c25c3abc6ffa compat/fsmonitor/fsm-*-win32: support long paths

  • 157: 9943bd66d9ee = 157: 6d5896bd3538 clean: suggest using core.longPaths if paths are too long to remove

  • 158: 88524ea3236f = 158: 7bb8f5a48c89 mingw: Support git_terminal_prompt with more terminals

  • 159: 6b65235ccce5 = 159: 4b6c17c67963 compat/terminal.c: only use the Windows console if bash 'read -r' fails

  • 160: cd6db5487650 = 160: 408161624fcd mingw (git_terminal_prompt): do fall back to CONIN$/CONOUT$ method

  • 161: 86581d67dad0 < -: ------------ Win32: don't call GetFileAttributes twice in mingw_lstat()

  • 162: efb8e66996c0 < -: ------------ Win32: implement stat() with symlink support

  • 163: f30c79a02a79 < -: ------------ Win32: remove separate do_lstat() function

  • 164: e5004ccec75e < -: ------------ Win32: let mingw_lstat() error early upon problems with reparse points

  • 165: 8b0e6c192db5 < -: ------------ mingw: teach fscache and dirent about symlinks

  • 166: ee52791ddc0c < -: ------------ Win32: lstat(): return adequate stat.st_size for symlinks

  • 167: 6ceb5ef587c6 < -: ------------ Win32: factor out retry logic

  • 168: ae6ec2e61589 < -: ------------ Win32: change default of 'core.symlinks' to false

  • 169: bd618741bd8e < -: ------------ Win32: add symlink-specific error codes

  • 170: 830ffaca99bd < -: ------------ Win32: mingw_unlink: support symlinks to directories

  • 171: 365d9ef0d5c3 < -: ------------ Win32: mingw_rename: support renaming symlinks

  • 172: ef76ad774a5d < -: ------------ Win32: mingw_chdir: change to symlink-resolved directory

  • 173: 38cb381da6d2 < -: ------------ Win32: implement readlink()

  • 174: 55408628ef80 < -: ------------ mingw: lstat: compute correct size for symlinks

  • 175: c92295de6bbb < -: ------------ Win32: implement basic symlink() functionality (file symlinks only)

  • 176: 6c58ba208c4c < -: ------------ Win32: symlink: add support for symlinks to directories

  • 178: daac5811a869 < -: ------------ mingw: emulate stat() a little more faithfully

  • 179: 01d54a428b34 < -: ------------ mingw: special-case index entries for symlinks with buggy size

  • 180: 4958bab6ad74 = 161: f24db036a81f mingw: introduce code to detect whether we're inside a Windows container

  • 181: 1cb648f4da62 = 162: 379ec9de1ca7 mingw: when running in a Windows container, try to rename() harder

  • 184: 8fddb92a79c1 ! 163: 1a6bf775968d Win32: symlink: move phantom symlink creation to a separate function

    @@ compat/mingw.c: int symlink(const char *target, const char *link)
     -	switch (process_phantom_symlink(wtarget, wlink)) {
     -	case PHANTOM_SYMLINK_RETRY:	{
     -		/* if target doesn't exist, add to phantom symlinks list */
    --		wchar_t wfullpath[MAX_LONG_PATH];
    +-		wchar_t wfullpath[MAX_PATH];
     -		struct phantom_symlink_info *psi;
     -
     -		/* convert to absolute path to be independent of cwd */
    --		len = GetFullPathNameW(wlink, MAX_LONG_PATH, wfullpath, NULL);
    --		if (!len || len >= MAX_LONG_PATH) {
    +-		len = GetFullPathNameW(wlink, MAX_PATH, wfullpath, NULL);
    +-		if (!len || len >= MAX_PATH) {
     -			errno = err_win_to_posix(GetLastError());
     -			return -1;
     -		}
    @@ compat/mingw.c: int symlink(const char *target, const char *link)
     +	return create_phantom_symlink(wtarget, wlink);
      }
      
    - #ifndef _WINNT_H
    + int readlink(const char *path, char *buf, size_t bufsiz)
  • 182: a86a9ba5e1e5 = 164: 5dcce1ae7832 mingw: move the file_attr_to_st_mode() function definition

  • 186: ceb439c192fe = 165: 0960f328624f Introduce helper to create symlinks that knows about index_state

  • 183: eb053981d6fb ! 166: baea4cfe36cf mingw: Windows Docker volumes are not symbolic links

    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsca
     +	}
     +
      	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    --					    fdata->EaSize);
    -+					    fdata->EaSize, buf);
    + 					    fdata->EaSize, buf);
      	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    - 			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
    - 	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
  • 187: 2b8f87faea8c ! 167: 12ea88bdfbdf mingw: allow to specify the symlink type in .gitattributes

    @@ compat/mingw.c: int mingw_create_symlink(struct index_state *index UNUSED, const
     +	return -1;
      }
      
    - #ifndef _WINNT_H
    + int readlink(const char *path, char *buf, size_t bufsiz)
  • 185: 82b409f9713c = 168: cee92a642d24 mingw: work around rename() failing on a read-only file

  • 188: 746dcb655217 = 169: c7ac5703c5ea Win32: symlink: add test for symlink attribute

  • 189: 1d994c000ce4 = 170: dd6f4f6e151e mingw: explicitly specify with which cmd to prefix the cmdline

  • 190: f69430461b55 = 171: 119a7bd28ad6 mingw: when path_lookup() failed, try BusyBox

  • 191: c39fd215fe6b = 172: 5fff04da152e test-tool: learn to act as a drop-in replacement for iconv

  • 192: a3df806c9231 = 173: 713de6120df7 tests(mingw): if iconv is unavailable, use test-helper --iconv

  • 193: 06e2aff66d84 = 174: 83ce344e4af8 gitattributes: mark .png files as binary

  • 194: 1173e4bb9cd7 = 175: 7a10947b3d2b tests: move test PNGs into t/lib-diff/

  • 195: 5e360db826d5 = 176: 50e21b09548b tests: only override sort & find if there are usable ones in /usr/bin/

  • 196: 4ff2d8e14e8d = 177: ab77ebf8c4a6 tests: use the correct path separator with BusyBox

  • 197: 1e820ff0f184 = 178: 6cd81171a36d mingw: only use Bash-ism builtin pwd -W when available

  • 198: 8a93857eacc6 = 179: 495bf8e20136 tests (mingw): remove Bash-specific pwd option

  • 199: f77554da92ec = 180: 61b0aea2ca3c test-lib: add BUSYBOX prerequisite

  • 200: da2dc16cc773 = 181: b29f110fedd9 t5003: use binary file from t/lib-diff/

  • 201: cf3461705508 = 182: bffa16ecdbff t5532: workaround for BusyBox on Windows

  • 202: 11fdc4109406 = 183: f1c208d52f19 t5605: special-case hardlink test for BusyBox-w32

  • 203: e52cd4afce25 = 184: 19fc9e77af1e t5813: allow for $PWD to be a Windows path

  • 204: 25b6a2d38adf = 185: 4161847c826c t9200: skip tests when $PWD contains a colon

  • 211: 4292bc3ca421 = 186: 948a2857fa5f mingw: kill child processes in a gentler way

  • 205: 997c3b5ffe9f = 187: 49c3f4b19b36 mingw: optionally enable wsl compability file mode bits

  • 214: 5ab983f58927 = 188: 5c2a5a354922 mingw: really handle SIGINT

  • 215: 902e343c3d7b = 189: cb06418701c8 Partially un-revert "editor: save and reset terminal after calling EDITOR"

  • 216: 51e13606298e = 190: 4ac142d37230 reset: reinstate support for the deprecated --stdin option

  • 206: 07fca2f81a20 = 191: 2b232771ceef Describe Git for Windows' architecture [no ci]

  • 207: e488bbd3cdf5 = 192: 8721a8b3c0c7 Modify the Code of Conduct for Git for Windows

  • 208: 6fdb617bd4e7 = 193: 0a7316bf461d CONTRIBUTING.md: add guide for first-time contributors

  • 209: ae446853c50e = 194: 992862351f1b README.md: Add a Windows-specific preamble

  • 210: e54f74d98dac = 195: fa4ba97ec50e Add an issue template

  • 212: 70d123f805c6 = 196: 58c26230ebaa Add a GitHub workflow to monitor component updates

  • 213: 67eb5e48ad25 = 197: 14a252e67809 Modify the GitHub Pull Request template (to reflect Git for Windows)

  • 217: 5ea59fd7f31a = 198: aadd41abdf3b fsmonitor: reintroduce core.useBuiltinFSMonitor

  • 218: 95d9d1ce1e4a = 199: 9e508167ec80 dependabot: help keeping GitHub Actions versions up to date

  • 219: b810f3da87a6 = 200: 6fda5302109b SECURITY.md: document Git for Windows' policies

Here is the workflow run: https://github.com/git-for-windows/git-for-windows-automation/actions/runs/22113557351; It took almost half an hour to run.

And this is a much more interesting case, indeed, as it did not do the exact identical thing as I did in my corresponding rebase:

git diff 3832dd2481ce7e25d18511420fca0e5567c6ac54 ee62a84b1d0a0a1482c0ef7791babc579228f7b0 (my rebase vs this run's)
diff --git a/compat/mingw.c b/compat/mingw.c
index 5a9807e7a42b..f6173c4b1d55 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -382,7 +382,7 @@ process_phantom_symlink(const wchar_t *wtarget, const wchar_t *wlink)
 {
 	HANDLE hnd;
 	BY_HANDLE_FILE_INFORMATION fdata;
-	wchar_t relative[MAX_LONG_PATH];
+	wchar_t relative[MAX_PATH];
 	const wchar_t *rel;
 
 	/* check that wlink is still a file symlink */
@@ -1119,6 +1119,7 @@ int mingw_chdir(const char *dirname)
 {
 	int result;
 	wchar_t wdirname[MAX_LONG_PATH];
+
 	if (xutftowcs_long_path(wdirname, dirname) < 0)
 		return -1;
 
@@ -1296,7 +1297,7 @@ int mingw_lstat(const char *file_name, struct stat *buf)
 	if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
 		/* for reparse points, get the link tag and length */
 		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
-			char tmpbuf[MAX_LONG_PATH];
+			char tmpbuf[MAX_PATH];
 
 			if (read_reparse_point(wfilename, FALSE, tmpbuf,
 					       &link_len, &reparse_tag) < 0)
@@ -1377,12 +1378,12 @@ static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
 
 int mingw_stat(const char *file_name, struct stat *buf)
 {
-	wchar_t wfile_name[MAX_LONG_PATH];
+	wchar_t wfile_name[MAX_PATH];
 	HANDLE hnd;
 	int result;
 
 	/* open the file and let Windows resolve the links */
-	if (xutftowcs_long_path(wfile_name, file_name) < 0)
+	if (xutftowcs_path(wfile_name, file_name) < 0)
 		return -1;
 	hnd = CreateFileW(wfile_name, 0,
 			FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
@@ -3381,7 +3382,7 @@ static enum symlink_type check_symlink_attr(struct index_state *index, const cha
 
 int mingw_create_symlink(struct index_state *index, const char *target, const char *link)
 {
-	wchar_t wtarget[MAX_LONG_PATH], wlink[MAX_LONG_PATH];
+	wchar_t wtarget[MAX_PATH], wlink[MAX_PATH];
 	int len;
 
 	/* fail if symlinks are disabled or API is not supported (WinXP) */
@@ -3390,8 +3391,8 @@ int mingw_create_symlink(struct index_state *index, const char *target, const ch
 		return -1;
 	}
 
-	if ((len = xutftowcs_long_path(wtarget, target)) < 0
-			|| xutftowcs_long_path(wlink, link) < 0)
+	if ((len = xutftowcs_path(wtarget, target)) < 0
+			|| xutftowcs_path(wlink, link) < 0)
 		return -1;
 
 	/* convert target dir separators to backslashes */
@@ -3428,12 +3429,12 @@ int mingw_create_symlink(struct index_state *index, const char *target, const ch
 
 int readlink(const char *path, char *buf, size_t bufsiz)
 {
-	WCHAR wpath[MAX_LONG_PATH];
-	char tmpbuf[MAX_LONG_PATH];
+	WCHAR wpath[MAX_PATH];
+	char tmpbuf[MAX_PATH];
 	int len;
 	DWORD tag;
 
-	if (xutftowcs_long_path(wpath, path) < 0)
+	if (xutftowcs_path(wpath, path) < 0)
 		return -1;
 
 	if (read_reparse_point(wpath, TRUE, tmpbuf, &len, &tag) < 0)
@@ -3726,6 +3727,15 @@ static void setup_windows_environment(void)
 			setenv("HOME", tmp, 1);
 	}
 
+	/*
+	 * Change 'core.symlinks' default to false, unless native symlinks are
+	 * enabled in MSys2 (via 'MSYS=winsymlinks:nativestrict'). Thus we can
+	 * run the test suite (which doesn't obey config files) with or without
+	 * symlink support.
+	 */
+	if (!(tmp = getenv("MSYS")) || !strstr(tmp, "winsymlinks:nativestrict"))
+		has_symlinks = 0;
+
 	if (!getenv("PLINK_PROTOCOL"))
 		setenv("PLINK_PROTOCOL", "ssh", 0);
 
diff --git a/compat/win32/fscache.c b/compat/win32/fscache.c
index cbd90ececf6b..cb97bddfecad 100644
--- a/compat/win32/fscache.c
+++ b/compat/win32/fscache.c
@@ -632,18 +632,6 @@ int fscache_lstat(const char *filename, struct stat *st)
 		return -1;
 	}
 
-	/*
-	 * Special case symbolic links: FindFirstFile()/FindNextFile() did not
-	 * provide us with the length of the target path.
-	 */
-	if (fse->u.s.st_size == MAX_LONG_PATH && S_ISLNK(fse->st_mode)) {
-		char buf[MAX_LONG_PATH];
-		int len = readlink(filename, buf, sizeof(buf) - 1);
-
-		if (len > 0)
-			fse->u.s.st_size = len;
-	}
-
 	/* copy stat data */
 	st->st_ino = 0;
 	st->st_gid = 0;
diff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh
index 25b8d50c9428..a11b20e37822 100755
--- a/t/t5571-pre-push-hook.sh
+++ b/t/t5571-pre-push-hook.sh
@@ -138,16 +138,4 @@ test_expect_success 'sigpipe does not cause pre-push hook failure' '
 	git push parent1 "refs/heads/b/*:refs/heads/b/*"
 '
 
-test_expect_success 'can write to stderr' '
-	test_hook --clobber pre-push <<-\EOF &&
-	echo foo >/dev/stderr &&
-	exit 0
-	EOF
-
-	test_commit third &&
-	echo foo >expect &&
-	git push --quiet parent1 HEAD 2>actual &&
-	test_cmp expect actual
-'
-
 test_done

The differences are mostly missed core.longPaths changes, which is understandable because Git for Windows' main branch originally put the symlink patches on top of the longPaths patches, but I upstreamed the symlink patches first (which meant that a couple of MAX_LONG_PATH instances in the symlink patches had to be upstreamed as MAX_PATH instead, and the automation is not (yet?) smart enough to understand that the change from MAX_PATH to MAX_LONG_PATH now needs to be moved into the longPaths patches.

Similarly, an FSCache/symlink special-case that uses MAX_LONG_PATH now needs to be assimilated into the longPaths patches.

Another difference is the "Win32: change default of 'core.symlinks' to false" patch, which the automated run contains twice, once from upstream, once from downstream, because it applied without conflicts.

A red herring is the "dropped" 541d54bc0121 (t/t5571-prep-push-hook.sh: Add test with writing to stderr, 2026-01-26): I simply used a fake origin/main in that workflow run that did not contain this change, by mistake.

With that in mind, here is the range-diff between my manual rebase and the rebase performed by that workflow run:

Range-diff manual vs automated rebase
  • 1: 72f5de3454f0 = 1: 9dd95041af58 sideband: mask control characters

  • 2: 69ad7769d91b = 2: 085308dec88c sideband: introduce an "escape hatch" to allow control characters

  • 3: ed536c92320e = 3: 74d30094bf33 sideband: do allow ANSI color sequences by default

  • 4: 2cd5611bd9c6 = 4: 7399a1890392 unix-socket: avoid leak when initialization fails

  • 5: 154c3b8f2c8e = 5: a64e5c841fe7 grep: prevent ^$ false match at end of file

  • 6: 61ca56afb7ea = 6: 970aa0d9b30d t9350: point out that refs are not updated correctly

  • 7: ae83d576a7d0 = 7: 23b2457744be transport-helper: add trailing --

  • 8: baaf71713bf5 = 8: de138a2821cc remote-helper: check helper status after import/export

  • 9: 579319b620a4 = 9: 208b32466593 clean: do not traverse mount points

  • 10: f0d37931c68a = 10: ecb16b1eabb9 Always auto-gc after calling a fast-import transport

  • 11: f5f40f3b121a = 11: 6c01bb5c71e8 mingw: prevent regressions with "drive-less" absolute paths

  • 20: ee43a66a8439 = 12: 801f26bc09cd clean: remove mount points when possible

  • 24: 71dccee64376 = 13: d77e2f64d22a vcpkg_install: detect lack of Git

  • 25: ea8a6b7eda22 = 14: cf66d02957c2 vcpkg_install: add comment regarding slow network connections

  • 28: e94d8273ab34 = 15: 928219497238 vcbuild: install ARM64 dependencies when building ARM64 binaries

  • 36: c3c19beffc96 = 16: fa7c95733cff vcbuild: add an option to install individual 'features'

  • 37: 7262616276ed = 17: ef4c024a7c25 cmake: allow building for Windows/ARM64

  • 12: 5d6daeebfb78 = 18: 11907042cd4f mingw: include the Python parts in the build

  • 38: 8bea01b12e71 = 19: 29f5358a5062 ci(vs-build) also build Windows/ARM64 artifacts

  • 13: 2f738e48d2b0 = 20: f00cfdb5904c win32/pthread: avoid name clashes with winpthread

  • 39: 18aa03feffe9 = 21: ecca847756cd Add schannel to curl installation

  • 42: 1c0fe1a018d9 = 22: 361624f6aa13 hash-object: demonstrate a >4GB/LLP64 problem

  • 14: 0f84586a4735 = 23: 1bccbf3b894d git-compat-util: avoid redeclaring _DEFAULT_SOURCE

  • 40: 702dc2683b5c = 24: 73bfe8d60c67 cmake(): allow setting HOST_CPU for cross-compilation

  • 44: da87446dceb5 = 25: beb05b1f8eef object-file.c: use size_t for header lengths

  • 15: e7cdf876a90c = 26: 891103a95d67 Import the source code of mimalloc v2.2.7

  • 41: 88a65bf26e6c = 27: 3ca88e921f13 CMake: default Visual Studio generator has changed

  • 47: d5fab9ba0cda = 28: ddee9a0d88ba hash algorithms: use size_t for section lengths

  • 16: 2f285aea9f17 = 29: d7d7ea39bc32 mimalloc: adjust for building inside Git

  • 18: 299cf470515c = 30: de383fbbd7c1 mingw: demonstrate a git add issue with NTFS junctions

  • 43: ee8cdf0cd7eb = 31: 57c312c47165 .gitignore: add Visual Studio CMakeSetting.json file

  • 53: 28101c6ae052 = 32: 53f1e5c18cf1 hash-object --stdin: verify that it works with >4GB/LLP64

  • 17: 7e3722cc824f = 33: 2664c7924bda mimalloc: offer a build-time option to enable it

  • 27: 14dc73634a97 = 34: ea230d47ec36 t5505/t5516: allow running without .git/branches/ in the templates

  • 26: ddf039e5ffee = 35: 80f0e00b4dac strbuf_realpath(): use platform-dependent API if available

  • 29: 69135d81fd7d = 36: 9e4e8a51e37a http: use new "best effort" strategy for Secure Channel revoke checking

  • 45: 6057555be577 = 37: 3ebbf69a5610 subtree: update contrib/subtree test target

  • 46: 24849e3602f7 = 38: bc553a8df070 CMakeLists: add default "x64-windows" arch for Visual Studio

  • 54: 770dec44be09 = 39: a1fb06848380 hash-object: add another >4GB/LLP64 test case

  • 55: dbcd7123712a = 40: f348bfc3d59e setup: properly use "%(prefix)/" when in WSL

  • 19: 9c43955fed47 = 41: 91a9f14c43b2 mingw: use mimalloc

  • 56: 10f0986c6357 = 42: 31befe24774f Add config option windows.appendAtomically

  • 31: db0e354abb91 = 43: c64dd235a72f t5505/t5516: fix white-space around redirectors

  • 21: 6f6e8f424500 = 44: 292be1f04fbe transport: optionally disable side-band-64k

  • 22: 2c41f19e894d = 45: 91c8f85c3183 mingw: fix fatal error working on mapped network drives on Windows

  • 23: 963231342a49 = 46: 6a7a79e459c4 clink.pl: fix MSVC compile script to handle libcurl-d.lib

  • 30: 065aaec6464e = 47: 0111bdbaca1a mingw: implement a platform-specific strbuf_realpath()

  • 32: 0924bbd7b1af = 48: 3f7ab56b30a1 t3701: verify that we can add lots of files interactively

  • 33: d00df93e0aaa = 49: 9ee20f240eed commit: accept "scissors" with CR/LF line endings

  • 34: 26563b25dece = 50: c11713f74d6a t0014: fix indentation

  • 35: 63e838f85932 = 51: b2868c46a95b git-gui: accommodate for intent-to-add files

  • 48: f154867f2566 = 52: 9f84c2100c43 mingw: allow for longer paths in parse_interpreter()

  • 49: 92b17b15d057 = 53: 3e8836c74f4b compat/vcbuild: document preferred way to build in Visual Studio

  • 50: 6d4c9d6bccd4 = 54: b782a751c70f http: optionally send SSL client certificate

  • 51: 757a2c9bd586 = 55: acaa97417879 ci: run contrib/subtree tests in CI builds

  • 52: 23246b4397a8 = 56: e1a2cb45e8f1 CMake: show Win32 and Generator_platform build-option values

  • 57: 9ea106018ca8 = 57: b7e8fde69f68 hash-object: add a >4GB/LLP64 test case using filtered input

  • 58: d8f746f542d2 = 58: 92f4389050e3 compat/mingw.c: do not warn when failing to get owner

  • 59: cb452cf7584c = 59: 894f02296f4f mingw: $env:TERM="xterm-256color" for newer OSes

  • 60: 53be802501cb = 60: 62261160301f winansi: check result and Buffer before using Name

  • 65: 55d10664920c = 61: 6e1134570594 windows: skip linking git-<command> for built-ins

  • 66: dd3b541d21d7 = 62: b6822f7f99b9 mingw: stop hard-coding CC = gcc

  • 67: 6855ef0b350b = 63: 0f179516bca6 mingw: drop the -D_USE_32BIT_TIME_T option

  • 68: fcae0f80ebd9 = 64: 7bd9abe978a6 mingw: only use -Wl,--large-address-aware for 32-bit builds

  • 69: fb3404d94af5 = 65: 09cfd29901a4 mingw: avoid over-specifying --pic-executable

  • 70: 3471074861fd = 66: 351961b38d21 mingw: set the prefix and HOST_CPU as per MSYS2's settings

  • 71: 75886a3bc8e4 = 67: 169fa2d509cf mingw: only enable the MSYS2-specific stuff when compiling in MSYS2

  • 72: 71c1841f0e93 = 68: 7dfa024b2fbc mingw: rely on MSYS2's metadata instead of hard-coding it

  • 62: 4f2721c77896 = 69: b21302cbde64 MinGW: link as terminal server aware

  • 61: 6a8a5a922131 = 70: a3bbeba35e2f mingw: change core.fsyncObjectFiles = 1 by default

  • 63: 4337ef00a176 = 71: 238ace9b31c8 Fix Windows version resources

  • 64: 906ddd07a915 = 72: 27b439df1807 status: fix for old-style submodules with commondir

  • 73: d1f0a5ba34e0 = 73: 6b826867d5a0 mingw: always define ETC_* for MSYS2 environments

  • 74: e23ad91110c1 = 74: 270e3dd15f6f max_tree_depth: lower it for clang builds in general on Windows

  • 107: b53fe0e7edda ! 75: 85d86e262a2f mingw: ensure valid CTYPE

    @@ Commit message
     
      ## compat/mingw.c ##
     @@ compat/mingw.c: static void setup_windows_environment(void)
    - 			setenv("HOME", tmp, 1);
    - 	}
    - 
    + 	 */
    + 	if (!(tmp = getenv("MSYS")) || !strstr(tmp, "winsymlinks:nativestrict"))
    + 		has_symlinks = 0;
    ++
     +	if (!getenv("LC_ALL") && !getenv("LC_CTYPE") && !getenv("LANG"))
     +		setenv("LC_CTYPE", "C.UTF-8", 1);
    -+
    - 	/*
    - 	 * Change 'core.symlinks' default to false, unless native symlinks are
    - 	 * enabled in MSys2 (via 'MSYS=winsymlinks:nativestrict'). Thus we can
    + }
    + 
    + static void get_current_user_sid(PSID *sid, HANDLE *linked_token)
  • 108: 8e2d9a909894 ! 76: 1f50763b5c47 mingw: allow git.exe to be used instead of the "Git wrapper"

    @@ compat/mingw.c: int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
      {
      	char *tmp = getenv("TMPDIR");
     @@ compat/mingw.c: static void setup_windows_environment(void)
    - 			setenv("HOME", tmp, 1);
    - 	}
    + 	if (!(tmp = getenv("MSYS")) || !strstr(tmp, "winsymlinks:nativestrict"))
    + 		has_symlinks = 0;
      
     +	if (!getenv("PLINK_PROTOCOL"))
     +		setenv("PLINK_PROTOCOL", "ssh", 0);
    @@ compat/mingw.c: static void setup_windows_environment(void)
     +
      	if (!getenv("LC_ALL") && !getenv("LC_CTYPE") && !getenv("LANG"))
      		setenv("LC_CTYPE", "C.UTF-8", 1);
    - 
    + }
     
      ## config.mak.uname ##
     @@ config.mak.uname: endif
  • 109: 9a0a9c1ded8b = 77: dd5ccb9546bc mingw: ignore HOMEDRIVE/HOMEPATH if it points to Windows' system directory

  • 110: b37402aaf8cc = 78: ede958d3f672 clink.pl: fix libexpatd.lib link error when using MSVC

  • 111: eb66711b2cba = 79: 2ca464428f29 Makefile: clean up .ilk files when MSVC=1

  • 112: 26af83426c14 = 80: ea1e3b8efbdd vcbuild: add support for compiling Windows resource files

  • 113: 4d1cc403e039 = 81: d65d716a7738 config.mak.uname: add git.rc to MSVC builds

  • 114: 4a2e42ab88bd = 82: e385d9d03c0b clink.pl: ignore no-stack-protector arg on MSVC=1 builds

  • 75: 7af555c5b979 = 83: 4306e04b6fec http: optionally load libcurl lazily

  • 115: a127f482d598 = 84: 1b19d13f9ba5 clink.pl: move default linker options for MSVC=1 builds

  • 76: 5ef2a8d03ff8 = 85: 4bcd8aaa45e9 http: support lazy-loading libcurl also on Windows

  • 116: 2910ae0743a6 = 86: 0ae6adfb6b85 cmake: install headless-git.

  • 78: dd918bd51d5e = 87: 86cba65a7b94 http: when loading libcurl lazily, allow for multiple SSL backends

  • 117: 97118b010184 = 88: 7db1b90c0e6d git.rc: include winuser.h

  • 80: 9e5b795a9a92 = 89: 66013c1ab344 mingw: do load libcurl dynamically by default

  • 81: 11ef03e8275c = 90: 86dbc5f3f07c Add a GitHub workflow to verify that Git/Scalar work in Nano Server

  • 82: 0a76992d6295 = 91: 945d05906823 mingw: suggest windows.appendAtomically in more cases

  • 83: 650a165ef91a = 92: bac2efbb8f46 win32: use native ANSI sequence processing, if possible

  • 84: 9da49cabc171 = 93: 30e26886f6d2 common-main.c: fflush stdout buffer upon exit

  • 85: 025e9a85c992 = 94: 06baf4db28a8 t5601/t7406(mingw): do run tests with symlink support

  • 86: e4e770cf1053 = 95: 917b2c71e053 win32: ensure that localtime_r() is declared even in i686 builds

  • 87: 3e9b12fd0d0a = 96: 1a7274e62cae Fallback to AppData if XDG_CONFIG_HOME is unset

  • 88: 70b9ce9fe105 = 97: b37c052e778e run-command: be helpful with Git LFS fails on Windows 7

  • 77: 7238dd9bc115 = 98: b07f2dcd2b4e ci: work around a problem with HTTP/2 vs libcurl v8.10.0

  • 79: ad2b49eaab35 = 99: be846d16469a revision: create mark_trees_uninteresting_dense()

  • 89: c99f45051c05 = 100: f29e1afe0dcb survey: stub in new experimental 'git-survey' command

  • 90: ac99a8f35263 = 101: 2b82bd8151cf survey: add command line opts to select references

  • 91: 949cf92170dd = 102: eab4df9b1f1a survey: start pretty printing data in table form

  • 92: 1743754fefa7 = 103: de71dbe4386b survey: add object count summary

  • 93: 03f77f8e970c = 104: df4139a4853b survey: summarize total sizes by object type

  • 94: 7879885db360 = 105: 22761f7b2451 survey: show progress during object walk

  • 95: c797e5aa3a57 = 106: ef2a6bb70b92 mingw: make sure errno is set correctly when socket operations fail

  • 96: 3dcc8daf863c = 107: a8ae16b26dee survey: add ability to track prioritized lists

  • 97: 6358efd8b247 = 108: abfb8f3c3127 compat/mingw: handle WSA errors in strerror

  • 98: 338e9b93604a = 109: e50b0b560fbf survey: add report of "largest" paths

  • 99: 79c8bcc69544 = 110: 867079abb2a7 compat/mingw: drop outdated comment

  • 100: 07beb8f5f5da = 111: eb8a772a86d1 survey: add --top= option and config

  • 101: 3ce045cd6641 = 112: 5892f2307ec7 t0301: actually test credential-cache on Windows

  • 102: e04c2735ea9d = 113: 5b1d3b488df3 survey: clearly note the experimental nature in the output

  • 103: fdc5dd88d808 = 114: 2d91dab7f5d0 credential-cache: handle ECONNREFUSED gracefully

  • 104: 746fc5e0ce09 = 115: 9b802c44bf54 reftable: do make sure to use custom allocators

  • 105: e1a6400f8db4 = 116: d6113d2c2dac check-whitespace: avoid alerts about upstream commits

  • 106: 541d54bc0121 < -: ------------ t/t5571-prep-push-hook.sh: Add test with writing to stderr

  • 118: a01bd81ccbb8 = 117: c9ab63f495c6 git-svn: mark it as unsupported by the Git for Windows project

  • 119: 76c3bf3653d1 = 118: 979bb887790d ci(macos): skip the git p4 tests

  • 120: 91dcc05d474e = 119: a3f19c6d18f8 Win32: make FILETIME conversion functions public

  • 133: 0bad704151f5 = 120: 4ce8d77c5b67 git-gui--askyesno: fix funny text wrapping

  • 121: 73c6e636358c = 121: 0fe5d258be99 Win32: dirent.c: Move opendir down

  • 135: 31ddaed68fe7 = 122: 490aedaf227e git-gui--askyesno (mingw): use Git for Windows' icon, if available

  • 122: c65ad577c841 = 123: 4e2b975669b6 mingw: make the dirent implementation pluggable

  • 123: cb36770f6750 = 124: 95c05c30edd9 Win32: make the lstat implementation pluggable

  • 124: 5ded7b7efcaa = 125: 043b57049675 mingw: add infrastructure for read-only file system level caches

  • 125: bf9f0e72e00b ! 126: b289e13142c2 mingw: add a cache below mingw's lstat and dirent implementations

    @@ compat/win32/fscache.c (new)
     +
     +	fse = fsentry_alloc(list, buf, len);
     +
    -+	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    -+					    fdata->EaSize);
    -+	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    -+			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
    ++	fse->st_mode = file_attr_to_st_mode(fdata->dwFileAttributes);
    ++	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
     +	fse->u.s.st_size = (((off64_t) (fdata->nFileSizeHigh)) << 32)
     +			| fdata->nFileSizeLow;
     +	filetime_to_timespec(&(fdata->ftLastAccessTime), &(fse->u.s.st_atim));
    @@ compat/win32/fscache.c (new)
     +		return -1;
     +	}
     +
    -+	/*
    -+	 * Special case symbolic links: FindFirstFile()/FindNextFile() did not
    -+	 * provide us with the length of the target path.
    -+	 */
    -+	if (fse->u.s.st_size == MAX_PATH && S_ISLNK(fse->st_mode)) {
    -+		char buf[MAX_LONG_PATH];
    -+		int len = readlink(filename, buf, sizeof(buf) - 1);
    -+
    -+		if (len > 0)
    -+			fse->u.s.st_size = len;
    -+	}
    -+
     +	/* copy stat data */
     +	st->st_ino = 0;
     +	st->st_gid = 0;
  • 126: fb4c6a513570 = 127: 30ae17cb26c2 fscache: load directories only once

  • 127: d3105b49d9c6 = 128: c4f6aee6ce09 fscache: add key for GIT_TRACE_FSCACHE

  • 128: 4650e1300875 = 129: 839da7d65d25 fscache: remember not-found directories

  • 129: bba3243fb6eb = 130: 71f9dd9b1a60 fscache: add a test for the dir-not-found optimization

  • 130: 89545a17e87e = 131: c7c3e467adbb add: use preload-index and fscache for performance

  • 131: 89d3cb27fd42 = 132: 1f1adad69aae dir.c: make add_excludes aware of fscache during status

  • 132: dd81725b2cce = 133: d48448a02ecc fscache: make fscache_enabled() public

  • 134: d74b0b0038ae = 134: 1afe61233f2b dir.c: regression fix for add_excludes with fscache

  • 136: 147e66371c58 = 135: a4ea037febce fetch-pack.c: enable fscache for stats under .git/objects

  • 137: 38e3b0b60fbf = 136: 62fe43d9f740 checkout.c: enable fscache for checkout again

  • 138: 9c26251e35a0 = 137: eb428843943e Enable the filesystem cache (fscache) in refresh_index().

  • 139: 10641751c34a = 138: 75b54250a294 fscache: use FindFirstFileExW to avoid retrieving the short name

  • 140: 8f3d874d8b74 = 139: a2150cf37b93 fscache: add GIT_TEST_FSCACHE support

  • 141: ed9e62ed1a30 = 140: 25e633bc360f fscache: add fscache hit statistics

  • 142: 16a1f1870ab6 = 141: 4ad3d6b45dbc unpack-trees: enable fscache for sparse-checkout

  • 143: a919310c6d1e = 142: f872349027c6 status: disable and free fscache at the end of the status command

  • 144: 3dd272a340d4 = 143: 1ffd26a6ac81 mem_pool: add GIT_TRACE_MEMPOOL support

  • 145: 86df8be1dee4 = 144: 1003f0f80967 fscache: fscache takes an initial size

  • 146: 7ba4643052c6 = 145: 028612a8bfb8 fscache: update fscache to be thread specific instead of global

  • 147: 7588ad517b99 ! 146: b4bcab7c7dba fscache: teach fscache to use mempool

    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsen
     -	fse = fsentry_alloc(list, buf, len);
     +	fse = fsentry_alloc(cache, list, buf, len);
      
    - 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    - 					    fdata->EaSize);
    + 	fse->st_mode = file_attr_to_st_mode(fdata->dwFileAttributes);
    + 	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
     @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsentry *list,
       * Dir should not contain trailing '/'. Use an empty string for the current
       * directory (not "."!).
  • 148: 260041ff5028 = 147: 88f6c1bb141e fscache: make fscache_enable() thread safe

  • 149: 4aa340bad70a ! 148: 597bb72f679e fscache: teach fscache to use NtQueryDirectoryFile

    @@ compat/win32/fscache.c: static void fsentry_release(struct fsentry *fse)
      
      	fse = fsentry_alloc(cache, list, buf, len);
      
    -@@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fscache *cache,
    - 					    fdata->EaSize);
    - 	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    - 			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
    +-	fse->st_mode = file_attr_to_st_mode(fdata->dwFileAttributes);
    ++	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes);
    + 	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
     -	fse->u.s.st_size = (((off64_t) (fdata->nFileSizeHigh)) << 32)
     -			| fdata->nFileSizeLow;
     -	filetime_to_timespec(&(fdata->ftLastAccessTime), &(fse->u.s.st_atim));
     -	filetime_to_timespec(&(fdata->ftLastWriteTime), &(fse->u.s.st_mtim));
     -	filetime_to_timespec(&(fdata->ftCreationTime), &(fse->u.s.st_ctim));
    -+	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_PATH :
    -+			fdata->EndOfFile.LowPart |
    -+			(((off_t)fdata->EndOfFile.HighPart) << 32);
    ++	fse->u.s.st_size = fdata->EndOfFile.LowPart |
    ++		(((off_t)fdata->EndOfFile.HighPart) << 32);
     +	filetime_to_timespec((FILETIME *)&(fdata->LastAccessTime),
     +			     &(fse->u.s.st_atim));
     +	filetime_to_timespec((FILETIME *)&(fdata->LastWriteTime),
  • 150: 91ce9a8e3c28 ! 149: 5a16a22f0e0b fscache: remember the reparse tag for each entry

    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsca
     +		fdata->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ?
     +		fdata->EaSize : 0;
     +
    - 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    - 					    fdata->EaSize);
    - 	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    + 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes);
    + 	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
    + 	fse->u.s.st_size = fdata->EndOfFile.LowPart |
  • 151: a08c6ea4096c = 150: e309c2ff9ffe fscache: implement an FSCache-aware is_mount_point()

  • 152: 691bc35e47a0 = 151: c577edd029ab clean: make use of FSCache

  • 153: 519b619b8505 = 152: 3fc98df7d895 pack-objects (mingw): demonstrate a segmentation fault with large deltas

  • 154: 0b0dc0f8082d ! 153: 2406860e5a7f mingw: support long paths

    @@ compat/mingw.c: static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOT
      int mingw_core_config(const char *var, const char *value,
      		      const struct config_context *ctx UNUSED,
      		      void *cb UNUSED)
    -@@ compat/mingw.c: process_phantom_symlink(const wchar_t *wtarget, const wchar_t *wlink)
    - {
    - 	HANDLE hnd;
    - 	BY_HANDLE_FILE_INFORMATION fdata;
    --	wchar_t relative[MAX_PATH];
    -+	wchar_t relative[MAX_LONG_PATH];
    - 	const wchar_t *rel;
    - 
    - 	/* check that wlink is still a file symlink */
     @@ compat/mingw.c: static wchar_t *normalize_ntpath(wchar_t *wbuf)
      int mingw_unlink(const char *pathname, int handle_in_use_error)
      {
    @@ compat/mingw.c: ssize_t mingw_write(int fd, const void *buf, size_t len)
      int mingw_chdir(const char *dirname)
      {
     -	wchar_t wdirname[MAX_PATH];
    --
    --	if (xutftowcs_path(wdirname, dirname) < 0)
     +	int result;
     +	wchar_t wdirname[MAX_LONG_PATH];
    + 
    +-	if (xutftowcs_path(wdirname, dirname) < 0)
     +	if (xutftowcs_long_path(wdirname, dirname) < 0)
      		return -1;
      
    @@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
      	if (wlen < 0)
      		return -1;
      
    -@@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
    - 	if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
    - 		/* for reparse points, get the link tag and length */
    - 		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
    --			char tmpbuf[MAX_PATH];
    -+			char tmpbuf[MAX_LONG_PATH];
    - 
    - 			if (read_reparse_point(wfilename, FALSE, tmpbuf,
    - 					       &link_len, &reparse_tag) < 0)
    -@@ compat/mingw.c: static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
    - 
    - int mingw_stat(const char *file_name, struct stat *buf)
    - {
    --	wchar_t wfile_name[MAX_PATH];
    -+	wchar_t wfile_name[MAX_LONG_PATH];
    - 	HANDLE hnd;
    - 	int result;
    - 
    - 	/* open the file and let Windows resolve the links */
    --	if (xutftowcs_path(wfile_name, file_name) < 0)
    -+	if (xutftowcs_long_path(wfile_name, file_name) < 0)
    - 		return -1;
    - 	hnd = CreateFileW(wfile_name, 0,
    - 			FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
     @@ compat/mingw.c: int mingw_utime (const char *file_name, const struct utimbuf *times)
      	FILETIME mft, aft;
      	int rc;
    @@ compat/mingw.c: int mingw_raise(int sig)
      		return -1;
      
      	if (!CreateHardLinkW(wnewpath, woldpath, NULL)) {
    -@@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
    - 
    - int symlink(const char *target, const char *link)
    - {
    --	wchar_t wtarget[MAX_PATH], wlink[MAX_PATH];
    -+	wchar_t wtarget[MAX_LONG_PATH], wlink[MAX_LONG_PATH];
    - 	int len;
    - 
    - 	/* fail if symlinks are disabled or API is not supported (WinXP) */
    -@@ compat/mingw.c: int symlink(const char *target, const char *link)
    - 		return -1;
    - 	}
    - 
    --	if ((len = xutftowcs_path(wtarget, target)) < 0
    --			|| xutftowcs_path(wlink, link) < 0)
    -+	if ((len = xutftowcs_long_path(wtarget, target)) < 0
    -+			|| xutftowcs_long_path(wlink, link) < 0)
    - 		return -1;
    - 
    - 	/* convert target dir separators to backslashes */
    -@@ compat/mingw.c: int symlink(const char *target, const char *link)
    - 
    - int readlink(const char *path, char *buf, size_t bufsiz)
    - {
    --	WCHAR wpath[MAX_PATH];
    --	char tmpbuf[MAX_PATH];
    -+	WCHAR wpath[MAX_LONG_PATH];
    -+	char tmpbuf[MAX_LONG_PATH];
    - 	int len;
    - 	DWORD tag;
    - 
    --	if (xutftowcs_path(wpath, path) < 0)
    -+	if (xutftowcs_long_path(wpath, path) < 0)
    - 		return -1;
    - 
    - 	if (read_reparse_point(wpath, TRUE, tmpbuf, &len, &tag) < 0)
     @@ compat/mingw.c: int mingw_is_mount_point(struct strbuf *path)
      {
      	WIN32_FIND_DATAW findbuf = { 0 };
    @@ compat/win32/fscache.c: static void fsentry_init(struct fsentry *fse, struct fse
      		BUG("Trying to allocate fsentry for long path '%.*s'",
      		    (int)len, name);
      	memcpy(fse->dirent.d_name, name, len);
    -@@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fscache *cache,
    - 		fdata->EaSize : 0;
    - 
    - 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    --					    fdata->EaSize);
    -+					    fdata->EaSize, buf);
    - 	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    - 			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
    --	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_PATH :
    -+	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
    - 			fdata->EndOfFile.LowPart |
    - 			(((off_t)fdata->EndOfFile.HighPart) << 32);
    - 	filetime_to_timespec((FILETIME *)&(fdata->LastAccessTime),
     @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fscache *cache,
      static struct fsentry *fsentry_create_list(struct fscache *cache, const struct fsentry *dir,
      					   int *dir_not_found)
    @@ compat/win32/fscache.c: static struct fsentry *fsentry_create_list(struct fscach
      
      	/* handle CWD */
      	if (!wlen) {
    -@@ compat/win32/fscache.c: int fscache_lstat(const char *filename, struct stat *st)
    - 	 * Special case symbolic links: FindFirstFile()/FindNextFile() did not
    - 	 * provide us with the length of the target path.
    - 	 */
    --	if (fse->u.s.st_size == MAX_PATH && S_ISLNK(fse->st_mode)) {
    -+	if (fse->u.s.st_size == MAX_LONG_PATH && S_ISLNK(fse->st_mode)) {
    - 		char buf[MAX_LONG_PATH];
    - 		int len = readlink(filename, buf, sizeof(buf) - 1);
    - 
     
      ## t/meson.build ##
     @@ t/meson.build: integration_tests = [
  • 155: 2b15f836a87d = 154: 5e3ab7fa4d97 win32(long path support): leave drive-less absolute paths intact

  • 156: c25c3abc6ffa = 155: e68240d43d51 compat/fsmonitor/fsm-*-win32: support long paths

  • 157: 6d5896bd3538 = 156: a2bd414d42ec clean: suggest using core.longPaths if paths are too long to remove

  • 158: 7bb8f5a48c89 = 157: f419814131d8 mingw: Support git_terminal_prompt with more terminals

  • 159: 4b6c17c67963 = 158: a01d48b9b1ee compat/terminal.c: only use the Windows console if bash 'read -r' fails

  • 160: 408161624fcd = 159: 665368e695be mingw (git_terminal_prompt): do fall back to CONIN$/CONOUT$ method

  • -: ------------ > 160: c12a49d537b8 Win32: change default of 'core.symlinks' to false

  • 161: f24db036a81f = 161: 6acc4b63be75 mingw: introduce code to detect whether we're inside a Windows container

  • 162: 379ec9de1ca7 = 162: 440e1c5d564e mingw: when running in a Windows container, try to rename() harder

  • 164: 5dcce1ae7832 = 163: bcdccb7611d6 mingw: move the file_attr_to_st_mode() function definition

  • 166: baea4cfe36cf ! 164: f8c2c32be32a mingw: Windows Docker volumes are not symbolic links

    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsca
      		fdata->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ?
      		fdata->EaSize : 0;
      
    +-	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes);
    +-	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
    +-	fse->u.s.st_size = fdata->EndOfFile.LowPart |
    +-		(((off_t)fdata->EndOfFile.HighPart) << 32);
     +	/*
     +	 * On certain Windows versions, host directories mapped into
     +	 * Windows Containers ("Volumes", see https://docs.docker.com/storage/volumes/)
    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsca
     +		buf[off + fse->len] = '\0';
     +	}
     +
    - 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    - 					    fdata->EaSize, buf);
    - 	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    ++	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    ++					    fdata->EaSize, buf);
    ++	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    ++			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
    ++	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
    ++			fdata->EndOfFile.LowPart |
    ++			(((off_t)fdata->EndOfFile.HighPart) << 32);
    + 	filetime_to_timespec((FILETIME *)&(fdata->LastAccessTime),
    + 			     &(fse->u.s.st_atim));
    + 	filetime_to_timespec((FILETIME *)&(fdata->LastWriteTime),
  • 168: cee92a642d24 = 165: e58a0e0deb18 mingw: work around rename() failing on a read-only file

  • 163: 1a6bf775968d = 166: bd7c45035799 Win32: symlink: move phantom symlink creation to a separate function

  • 165: 0960f328624f ! 167: 83e1480fadb6 Introduce helper to create symlinks that knows about index_state

    @@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
     -int symlink(const char *target, const char *link)
     +int mingw_create_symlink(struct index_state *index UNUSED, const char *target, const char *link)
      {
    - 	wchar_t wtarget[MAX_LONG_PATH], wlink[MAX_LONG_PATH];
    + 	wchar_t wtarget[MAX_PATH], wlink[MAX_PATH];
      	int len;
     
      ## entry.c ##
  • 167: 12ea88bdfbdf ! 168: 0e72f52f2910 mingw: allow to specify the symlink type in .gitattributes

    @@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
     +
     +int mingw_create_symlink(struct index_state *index, const char *target, const char *link)
      {
    - 	wchar_t wtarget[MAX_LONG_PATH], wlink[MAX_LONG_PATH];
    + 	wchar_t wtarget[MAX_PATH], wlink[MAX_PATH];
      	int len;
     @@ compat/mingw.c: int mingw_create_symlink(struct index_state *index UNUSED, const char *target, c
      		if (wtarget[len] == '/')
  • 169: c7ac5703c5ea = 169: b2230eff265e Win32: symlink: add test for symlink attribute

  • 170: dd6f4f6e151e = 170: 54aa73e67df7 mingw: explicitly specify with which cmd to prefix the cmdline

  • 171: 119a7bd28ad6 = 171: e31bba0684f1 mingw: when path_lookup() failed, try BusyBox

  • 172: 5fff04da152e = 172: 098badcfc3e0 test-tool: learn to act as a drop-in replacement for iconv

  • 173: 713de6120df7 = 173: efb595de96a8 tests(mingw): if iconv is unavailable, use test-helper --iconv

  • 174: 83ce344e4af8 = 174: 46d885e2cb82 gitattributes: mark .png files as binary

  • 175: 7a10947b3d2b = 175: ef580a3a89d2 tests: move test PNGs into t/lib-diff/

  • 176: 50e21b09548b = 176: d7ae11199bdb tests: only override sort & find if there are usable ones in /usr/bin/

  • 177: ab77ebf8c4a6 = 177: 959a456132b6 tests: use the correct path separator with BusyBox

  • 178: 6cd81171a36d = 178: b2804e37063c mingw: only use Bash-ism builtin pwd -W when available

  • 179: 495bf8e20136 = 179: ae1aa3fb1860 tests (mingw): remove Bash-specific pwd option

  • 180: 61b0aea2ca3c = 180: d7b3002a04e3 test-lib: add BUSYBOX prerequisite

  • 191: 2b232771ceef = 181: 8dde67c13dc2 Describe Git for Windows' architecture [no ci]

  • 181: b29f110fedd9 = 182: 46320651ed3f t5003: use binary file from t/lib-diff/

  • 192: 8721a8b3c0c7 = 183: 10378d8abafe Modify the Code of Conduct for Git for Windows

  • 182: bffa16ecdbff = 184: 0f826fa3f3ff t5532: workaround for BusyBox on Windows

  • 193: 0a7316bf461d = 185: 45d71614ee24 CONTRIBUTING.md: add guide for first-time contributors

  • 183: f1c208d52f19 = 186: 1bae0463813f t5605: special-case hardlink test for BusyBox-w32

  • 194: 992862351f1b = 187: 399dcfc3582b README.md: Add a Windows-specific preamble

  • 184: 19fc9e77af1e = 188: c951a50e4253 t5813: allow for $PWD to be a Windows path

  • 195: fa4ba97ec50e = 189: d3e65d0461f0 Add an issue template

  • 185: 4161847c826c = 190: ed610d02e5ef t9200: skip tests when $PWD contains a colon

  • 186: 948a2857fa5f = 191: ac84fa76298b mingw: kill child processes in a gentler way

  • 196: 58c26230ebaa = 192: c25c8eba8435 Add a GitHub workflow to monitor component updates

  • 197: 14a252e67809 = 193: 5458453c449b Modify the GitHub Pull Request template (to reflect Git for Windows)

  • 187: 49c3f4b19b36 = 194: 9489ca32f40f mingw: optionally enable wsl compability file mode bits

  • 188: 5c2a5a354922 = 195: 952b5beac469 mingw: really handle SIGINT

  • 189: cb06418701c8 = 196: d3c6d11492ec Partially un-revert "editor: save and reset terminal after calling EDITOR"

  • 190: 4ac142d37230 = 197: 2792c1a1646b reset: reinstate support for the deprecated --stdin option

  • 198: aadd41abdf3b = 198: 8ab625200601 fsmonitor: reintroduce core.useBuiltinFSMonitor

  • 199: 9e508167ec80 = 199: 018ed9cd5e73 dependabot: help keeping GitHub Actions versions up to date

  • 200: 6fda5302109b = 200: bb8289c2bdee SECURITY.md: document Git for Windows' policies

This range-diff basically recapitulates the changes that were already obvious from the overall diff, plus some changes in the merge conflict resolutions in the file_attr_to_st_mode() calls. Either I did not properly compile-test them, or the automated rebase ran afoul of the fact that we cannot test-compile on Windows (because of Windows-specific Copilot shenanigans/bugs).

I have to admit that I am quite happy with this state, if this is the worst the automation does, it will still be a great help trying to reduce friction between upstream and downstream patches.

The step summary on the workflow run's page also looks quite nice, I find. I will replicate it here for the reader's viewing pleasure:

Rebase Summary: main

From: 5f32b8db3c (Merge 'readme' into HEAD, 2018-06-07) (3de3111a1b..5f32b8db3c)

Resolved: 8b99bae1ec (mingw: change core.fsyncObjectFiles = 1 by default, 2017-09-04)

kept both adjust_symlink_flags() and fsync_object_files = 1 after maybe_redirect_std_handles()

Range-diff
1:  8b99bae1ec ! 1:  a3bbeba35e mingw: change core.fsyncObjectFiles = 1 by default
    @@ compat/mingw.c
      #include <conio.h>
      #include <sddl.h>
     @@ compat/mingw.c: int wmain(int argc, const wchar_t **wargv)
    - #endif
      
      	maybe_redirect_std_handles();
    + 	adjust_symlink_flags();
     +	fsync_object_files = 1;
      
      	/* determine size of argv and environ conversion buffer */

Resolved: 04aa4bc48e (max_tree_depth: lower it for clang builds in general on Windows, 2025-11-21)

Resolved conflict: upstream moved max_allowed_tree_depth from environment.c to git-compat-util.h; dropped the variable from environment.c (HEAD side) and applied the clang x86_64 limit (1152) change to git-compat-util.h instead

Range-diff
1:  04aa4bc48e < -:  ---------- max_tree_depth: lower it for clang builds in general on Windows
-:  ---------- > 1:  270e3dd15f max_tree_depth: lower it for clang builds in general on Windows

Resolved: 28dd1b6f63 (mingw: ensure valid CTYPE, 2017-02-21)

kept both HEAD's symlinks block and REBASE_HEAD's LC_CTYPE environment setup; pure context conflict resolved by including both changes

Range-diff
1:  28dd1b6f63 ! 1:  85d86e262a mingw: ensure valid CTYPE
    @@ Commit message
     
      ## compat/mingw.c ##
     @@ compat/mingw.c: static void setup_windows_environment(void)
    - 		if (!tmp && (tmp = getenv("USERPROFILE")))
    - 			setenv("HOME", tmp, 1);
    - 	}
    + 	 */
    + 	if (!(tmp = getenv("MSYS")) || !strstr(tmp, "winsymlinks:nativestrict"))
    + 		has_symlinks = 0;
     +
     +	if (!getenv("LC_ALL") && !getenv("LC_CTYPE") && !getenv("LANG"))
     +		setenv("LC_CTYPE", "C.UTF-8", 1);

Resolved: 32b25ffea7 (mingw: allow git.exe to be used instead of the "Git wrapper", 2020-02-01)

kept both sides: upstream's core.symlinks default block followed by the patch's PLINK_PROTOCOL and ENSURE_MSYSTEM_IS_SET blocks

Range-diff
1:  32b25ffea7 ! 1:  1f50763b5c mingw: allow `git.exe` to be used instead of the "Git wrapper"
    @@ compat/mingw.c: int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
      {
      	char *tmp = getenv("TMPDIR");
     @@ compat/mingw.c: static void setup_windows_environment(void)
    - 			setenv("HOME", tmp, 1);
    - 	}
    + 	if (!(tmp = getenv("MSYS")) || !strstr(tmp, "winsymlinks:nativestrict"))
    + 		has_symlinks = 0;
      
     +	if (!getenv("PLINK_PROTOCOL"))
     +		setenv("PLINK_PROTOCOL", "ssh", 0);

Resolved: b7965dae55 (Win32: make the lstat implementation pluggable, 2013-09-08)

Resolved by keeping lstat function pointer declaration and dropping do_stat_internal which was removed upstream

Range-diff
1:  b7965dae55 ! 1:  95c05c30ed Win32: make the lstat implementation pluggable
    @@ compat/mingw-posix.h: int mingw_fstat(int fd, struct stat *buf);
      int mingw_utime(const char *file_name, const struct utimbuf *times);
     
      ## compat/mingw.c ##
    -@@ compat/mingw.c: static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
    - 	return do_lstat(follow, alt_name, buf);
    +@@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
    + 	return -1;
      }
      
     +int (*lstat)(const char *file_name, struct stat *buf) = mingw_lstat;

Resolved: 0d86933e61 (mingw: support long paths, 2015-07-28)

resolved 4 conflicts in compat/mingw.c: kept HEAD's removal of unused ret variable in unlink/rmdir, kept HEAD's symlink support in chdir (with long path + current_directory_len update), kept HEAD's reparse_tag/link_len/wlen in lstat; applied MAX_LONG_PATH and xutftowcs_long_path in all four functions

Range-diff
1:  0d86933e61 ! 1:  2406860e5a mingw: support long paths
    @@ compat/mingw.c: static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOT
     @@ compat/mingw.c: static wchar_t *normalize_ntpath(wchar_t *wbuf)
      int mingw_unlink(const char *pathname, int handle_in_use_error)
      {
    - 	int ret, tries = 0;
    + 	int tries = 0;
     -	wchar_t wpathname[MAX_PATH];
     -	if (xutftowcs_path(wpathname, pathname) < 0)
     +	wchar_t wpathname[MAX_LONG_PATH];
    @@ compat/mingw.c: static int is_dir_empty(const wchar_t *wpath)
     @@ compat/mingw.c: static int is_dir_empty(const wchar_t *wpath)
      int mingw_rmdir(const char *pathname)
      {
    - 	int ret, tries = 0;
    + 	int tries = 0;
     -	wchar_t wpathname[MAX_PATH];
     +	wchar_t wpathname[MAX_LONG_PATH];
      	struct stat st;
    @@ compat/mingw.c: int mingw_rmdir(const char *pathname)
     +	if (xutftowcs_long_path(wpathname, pathname) < 0)
      		return -1;
      
    - 	while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
    + 	do {
     @@ compat/mingw.c: static int set_hidden_flag(const wchar_t *path, int set)
      int mingw_mkdir(const char *path, int mode UNUSED)
      {
    @@ compat/mingw.c: static int set_hidden_flag(const wchar_t *path, int set)
      		return -1;
     +
      	ret = _wmkdir(wpath);
    - 	if (!ret && needs_hiding(path))
    - 		return set_hidden_flag(wpath, 1);
    + 	if (!ret)
    + 		process_phantom_symlinks();
     @@ compat/mingw.c: int mingw_open (const char *filename, int oflags, ...)
      	va_list args;
      	unsigned mode;
    @@ compat/mingw.c: ssize_t mingw_write(int fd, const void *buf, size_t len)
      int mingw_chdir(const char *dirname)
      {
     -	wchar_t wdirname[MAX_PATH];
    --	if (xutftowcs_path(wdirname, dirname) < 0)
     +	int result;
     +	wchar_t wdirname[MAX_LONG_PATH];
    + 
    +-	if (xutftowcs_path(wdirname, dirname) < 0)
     +	if (xutftowcs_long_path(wdirname, dirname) < 0)
      		return -1;
    --	return _wchdir(wdirname);
    -+	result = _wchdir(wdirname);
    + 
    + 	if (has_symlinks) {
    +@@ compat/mingw.c: int mingw_chdir(const char *dirname)
    + 		CloseHandle(hnd);
    + 	}
    + 
    +-	return _wchdir(normalize_ntpath(wdirname));
    ++	result = _wchdir(normalize_ntpath(wdirname));
     +	current_directory_len = GetCurrentDirectoryW(0, NULL);
     +	return result;
      }
    @@ compat/mingw.c: ssize_t mingw_write(int fd, const void *buf, size_t len)
      		return -1;
      	return _wchmod(wfilename, mode);
      }
    -@@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
    - static int do_lstat(int follow, const char *file_name, struct stat *buf)
    - {
    +@@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
      	WIN32_FILE_ATTRIBUTE_DATA fdata;
    + 	DWORD reparse_tag = 0;
    + 	int link_len = 0;
     -	wchar_t wfilename[MAX_PATH];
    --	if (xutftowcs_path(wfilename, file_name) < 0)
    +-	int wlen = xutftowcs_path(wfilename, file_name);
     +	wchar_t wfilename[MAX_LONG_PATH];
    -+	if (xutftowcs_long_path(wfilename, file_name) < 0)
    ++	int wlen = xutftowcs_long_path(wfilename, file_name);
    + 	if (wlen < 0)
      		return -1;
      
    - 	if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
     @@ compat/mingw.c: int mingw_utime (const char *file_name, const struct utimbuf *times)
      	FILETIME mft, aft;
      	int rc;
    @@ compat/mingw.c: static pid_t mingw_spawnve_fd(const char *cmd, const char **argv
      	if (dir && xutftowcs_path(wdir, dir) < 0)
     @@ compat/mingw.c: int mingw_rename(const char *pold, const char *pnew)
      	static int supports_file_rename_info_ex = 1;
    - 	DWORD attrs, gle;
    + 	DWORD attrs = INVALID_FILE_ATTRIBUTES, gle;
      	int tries = 0;
     -	wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
     +	wchar_t wpold[MAX_LONG_PATH], wpnew[MAX_LONG_PATH];

Skipped (obsolete): 694f810e82 (Win32: fix 'lstat("dir/")' with long paths, 2014-07-05)

Reason: do_stat_internal was removed upstream; the PATH_MAX→MAX_LONG_PATH fix is no longer applicable

Resolved: bc025c8d63 (win32(long path support): leave drive-less absolute paths intact, 2023-09-06)

kept HEAD's phantom symlink code between is_wdir_sep and normalize_ntpath; both actual changes from REBASE_HEAD (is_wdir_sep helper and handle_long_path drive-less fix) were already applied

Range-diff
1:  bc025c8d63 ! 1:  5e3ab7fa4d win32(long path support): leave drive-less absolute paths intact
    @@ Commit message
         Signed-off-by: Johannes Schindelin <[email protected]>
     
      ## compat/mingw.c ##
    -@@ compat/mingw.c: int mingw_core_config(const char *var, const char *value,
    - 	return 0;
    - }
    - 
    -+static inline int is_wdir_sep(wchar_t wchar)
    -+{
    -+	return wchar == L'/' || wchar == L'\\';
    -+}
    -+
    - /* Normalizes NT paths as returned by some low-level APIs. */
    - static wchar_t *normalize_ntpath(wchar_t *wbuf)
    - {
     @@ compat/mingw.c: int handle_long_path(wchar_t *path, int len, int max_path, int expand)
      	 * "cwd + path" doesn't due to '..' components)
      	 */

Skipped: 86581d67da (Win32: don't call GetFileAttributes twice in mingw_lstat(), 2015-05-12)

Upstream equivalent: 3b96b99683 (mingw: don't call GetFileAttributes() twice in mingw_lstat(), 2026-01-09)

Range-diff
1:  86581d67da ! 1:  3b96b99683 Win32: don't call GetFileAttributes twice in mingw_lstat()
    @@ Metadata
     Author: Karsten Blees <[email protected]>
     
      ## Commit message ##
    -    Win32: don't call GetFileAttributes twice in mingw_lstat()
    +    mingw: don't call `GetFileAttributes()` twice in `mingw_lstat()`
     
    -    GetFileAttributes cannot handle paths with trailing dir separator. The
    -    current [l]stat implementation calls GetFileAttributes twice if the path
    -    has trailing slashes (first with the original path passed to [l]stat, and
    -    and a second time with a path copy with trailing '/' removed).
    +    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 Unicode conversion, we get the length of the path for free and also
    -    have a (wide char) buffer that can be modified.
    +    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.
     
    -    Remove trailing directory separators before calling the Win32 API.
    -
    -    Signed-off-by: Karsten Blees <[email protected]>
    +    Signed-off-by: Karsten Blees <[email protected]>
    +    Signed-off-by: Johannes Schindelin <[email protected]>
    +    Signed-off-by: Junio C Hamano <[email protected]>
     
      ## compat/mingw.c ##
    +@@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
    + }
    + 
    + /* We keep the do_lstat code in a separate function to avoid recursion.
    +- * When a path ends with a slash, the stat will fail with ENOENT. In
    +- * this case, we strip the trailing slashes and stat again.
    ++ * When a path ends with a slash, the call to `GetFileAttributedExW()`
    ++ * would fail. To prevent this, we strip any trailing slashes before that
    ++ * call.
    +  *
    +  * If follow is true then act like stat() and report on the link
    +  * target. Otherwise report on the link itself.
     @@ compat/mingw.c: static int do_lstat(int follow, const char *file_name, struct stat *buf)
      {
      	WIN32_FILE_ATTRIBUTE_DATA fdata;
    - 	wchar_t wfilename[MAX_LONG_PATH];
    --	if (xutftowcs_long_path(wfilename, file_name) < 0)
    -+	int wlen = xutftowcs_long_path(wfilename, file_name);
    + 	wchar_t wfilename[MAX_PATH];
    +-	if (xutftowcs_path(wfilename, file_name) < 0)
    ++	int wlen = xutftowcs_path(wfilename, file_name);
     +	if (wlen < 0)
    -+		return -1;
    -+
    + 		return -1;
    + 
     +	/* strip trailing '/', or GetFileAttributes will fail */
     +	while (wlen && is_dir_sep(wfilename[wlen - 1]))
     +		wfilename[--wlen] = 0;
     +	if (!wlen) {
     +		errno = ENOENT;
    - 		return -1;
    ++		return -1;
     +	}
    - 
    ++
      	if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
      		buf->st_ino = 0;
    + 		buf->st_gid = 0;
     @@ compat/mingw.c: static int do_lstat(int follow, const char *file_name, struct stat *buf)
      	return -1;
      }
    @@ compat/mingw.c: static int do_lstat(int follow, const char *file_name, struct st
     -static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
     -{
     -	size_t namelen;
    --	char alt_name[MAX_LONG_PATH];
    +-	char alt_name[PATH_MAX];
     -
     -	if (!do_lstat(follow, file_name, buf))
     -		return 0;
    @@ compat/mingw.c: static int do_lstat(int follow, const char *file_name, struct st
     -		return -1;
     -	while (namelen && file_name[namelen-1] == '/')
     -		--namelen;
    --	if (!namelen || namelen >= MAX_LONG_PATH)
    +-	if (!namelen || namelen >= PATH_MAX)
     -		return -1;
     -
     -	memcpy(alt_name, file_name, namelen);
    @@ compat/mingw.c: static int do_lstat(int follow, const char *file_name, struct st
     -	return do_lstat(follow, alt_name, buf);
     -}
     -
    - int (*lstat)(const char *file_name, struct stat *buf) = mingw_lstat;
    - 
      static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
    + {
    + 	BY_HANDLE_FILE_INFORMATION fdata;
     @@ compat/mingw.c: static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
      
      int mingw_lstat(const char *file_name, struct stat *buf)

Skipped: efb8e66996 (Win32: implement stat() with symlink support, 2015-05-16)

Upstream equivalent: 48bc5094de (mingw: implement stat() with symlink support, 2026-01-09)

Range-diff
1:  efb8e66996 ! 1:  48bc5094de Win32: implement stat() with symlink support
    @@ Metadata
     Author: Karsten Blees <[email protected]>
     
      ## Commit message ##
    -    Win32: implement stat() with symlink support
    +    mingw: implement `stat()` with symlink support
     
    -    With respect to symlinks, the current stat() implementation is almost the
    -    same as lstat(): except for the file type (st_mode & S_IFMT), it returns
    -    information about the link rather than the target.
    +    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 stat by opening the file with as little permissions as possible
    -    and calling GetFileInformationByHandle on it. This way, all link resoltion
    -    is handled by the Windows file system layer.
    +    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.
     
    -    If symlinks are disabled, use lstat() as before, but fail with ELOOP if a
    -    symlink would have to be resolved.
    -
    -    Signed-off-by: Karsten Blees <[email protected]>
    +    Signed-off-by: Karsten Blees <[email protected]>
         Signed-off-by: Johannes Schindelin <[email protected]>
    +    Signed-off-by: Junio C Hamano <[email protected]>
     
      ## compat/mingw.c ##
     @@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
    @@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
      int mingw_stat(const char *file_name, struct stat *buf)
      {
     -	return do_lstat(1, file_name, buf);
    -+	wchar_t wfile_name[MAX_LONG_PATH];
    ++	wchar_t wfile_name[MAX_PATH];
     +	HANDLE hnd;
     +	int result;
     +
     +	/* open the file and let Windows resolve the links */
    -+	if (xutftowcs_long_path(wfile_name, file_name) < 0)
    ++	if (xutftowcs_path(wfile_name, file_name) < 0)
     +		return -1;
     +	hnd = CreateFileW(wfile_name, 0,
     +			FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,

Skipped: f30c79a02a (Win32: remove separate do_lstat() function, 2015-05-12)

Upstream equivalent: 882e5e0528 (mingw: drop the separate do_lstat() function, 2026-01-09)

Range-diff
1:  f30c79a02a ! 1:  882e5e0528 Win32: remove separate do_lstat() function
    @@ Metadata
     Author: Karsten Blees <[email protected]>
     
      ## Commit message ##
    -    Win32: remove separate do_lstat() function
    +    mingw: drop the separate `do_lstat()` function
     
    -    With the new mingw_stat() implementation, do_lstat() is only called from
    -    mingw_lstat() (with follow == 0). Remove the extra function and the old
    -    mingw_stat()-specific (follow == 1) logic.
    +    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 <[email protected]>
    +    Signed-off-by: Karsten Blees <[email protected]>
    +    Signed-off-by: Johannes Schindelin <[email protected]>
    +    Signed-off-by: Junio C Hamano <[email protected]>
     
      ## compat/mingw.c ##
     @@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
    @@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
      }
      
     -/* We keep the do_lstat code in a separate function to avoid recursion.
    -- * When a path ends with a slash, the stat will fail with ENOENT. In
    -- * this case, we strip the trailing slashes and stat again.
    +- * When a path ends with a slash, the call to `GetFileAttributedExW()`
    +- * would fail. To prevent this, we strip any trailing slashes before that
    +- * call.
     - *
     - * If follow is true then act like stat() and report on the link
     - * target. Otherwise report on the link itself.
    @@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
     +int mingw_lstat(const char *file_name, struct stat *buf)
      {
      	WIN32_FILE_ATTRIBUTE_DATA fdata;
    - 	wchar_t wfilename[MAX_LONG_PATH];
    + 	wchar_t wfilename[MAX_PATH];
     @@ compat/mingw.c: static int do_lstat(int follow, const char *file_name, struct stat *buf)
      			if (handle != INVALID_HANDLE_VALUE) {
      				if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
    @@ compat/mingw.c: static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
     -
      int mingw_stat(const char *file_name, struct stat *buf)
      {
    - 	wchar_t wfile_name[MAX_LONG_PATH];
    + 	wchar_t wfile_name[MAX_PATH];

Skipped: e5004ccec7 (Win32: let mingw_lstat() error early upon problems with reparse points, 2015-05-24)

Upstream equivalent: 2c37842ff9 (mingw: let mingw_lstat() error early upon problems with reparse points, 2026-01-09)

Range-diff
1:  e5004ccec7 ! 1:  2c37842ff9 Win32: let mingw_lstat() error early upon problems with reparse points
    @@ Metadata
     Author: Karsten Blees <[email protected]>
     
      ## Commit message ##
    -    Win32: let mingw_lstat() error early upon problems with reparse points
    +    mingw: let `mingw_lstat()` error early upon problems with reparse points
     
         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.
    +    `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.
    +    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.
    +    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.
     
    -    Note: The indentation of the remaining reparse point code will be fixed in
    -    the next patch.
    +    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 <[email protected]>
    +    Signed-off-by: Karsten Blees <[email protected]>
    +    Signed-off-by: Johannes Schindelin <[email protected]>
    +    Signed-off-by: Junio C Hamano <[email protected]>
     
      ## compat/mingw.c ##
     @@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
    @@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
      {
      	WIN32_FILE_ATTRIBUTE_DATA fdata;
     +	WIN32_FIND_DATAW findbuf = { 0 };
    - 	wchar_t wfilename[MAX_LONG_PATH];
    - 	int wlen = xutftowcs_long_path(wfilename, file_name);
    + 	wchar_t wfilename[MAX_PATH];
    + 	int wlen = xutftowcs_path(wfilename, file_name);
      	if (wlen < 0)
     @@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
      	}
    @@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
     -			WIN32_FIND_DATAW findbuf;
     -			HANDLE handle = FindFirstFileW(wfilename, &findbuf);
     -			if (handle != INVALID_HANDLE_VALUE) {
    - 				if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
    - 						(findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
    - 					buf->st_mode = S_IFLNK | S_IREAD;
    - 					if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
    - 						buf->st_mode |= S_IWRITE;
    - 				}
    +-				if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
    +-						(findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
    +-					buf->st_mode = S_IFLNK | S_IREAD;
    +-					if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
    +-						buf->st_mode |= S_IWRITE;
    +-				}
     -				FindClose(handle);
    --			}
    ++			if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
    ++					(findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
    ++				buf->st_mode = S_IFLNK | S_IREAD;
    ++				if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
    ++					buf->st_mode |= S_IWRITE;
    + 			}
      		}
      		return 0;
      	}

Skipped: 8b0e6c192d (mingw: teach fscache and dirent about symlinks, 2017-01-10)

Upstream equivalent: 8a4f4131aa (mingw: teach dirent about symlinks, 2026-01-09)

Range-diff
1:  8b0e6c192d ! 1:  8a4f4131aa mingw: teach fscache and dirent about symlinks
    @@ Metadata
     Author: Karsten Blees <[email protected]>
     
      ## Commit message ##
    -    mingw: teach fscache and dirent about symlinks
    +    mingw: teach dirent about symlinks
     
    -    Move S_IFLNK detection to file_attr_to_st_mode() and reuse it in fscache.
    +    Move the `S_IFLNK` detection to `file_attr_to_st_mode()`.
     
    -    Implement DT_LNK detection in dirent.c and the fscache readdir version.
    +    Implement `DT_LNK` detection in dirent.c's `readdir()` function.
     
    -    Signed-off-by: Karsten Blees <[email protected]>
    +    Signed-off-by: Karsten Blees <[email protected]>
         Signed-off-by: Johannes Schindelin <[email protected]>
    +    Signed-off-by: Junio C Hamano <[email protected]>
     
      ## compat/mingw.c ##
     @@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
    @@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
      		filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
      		filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
     -		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
    --				if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
    --						(findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
    --					buf->st_mode = S_IFLNK | S_IREAD;
    --					if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
    --						buf->st_mode |= S_IWRITE;
    --				}
    +-			if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
    +-					(findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
    +-				buf->st_mode = S_IFLNK | S_IREAD;
    +-				if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
    +-					buf->st_mode |= S_IWRITE;
    +-			}
     -		}
      		return 0;
      	}
    @@ compat/win32.h
     
      ## compat/win32/dirent.c ##
     @@ compat/win32/dirent.c: static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
    - 	xwcstoutf(ent->d_name, fdata->cFileName, MAX_PATH * 3);
    + 	xwcstoutf(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
      
      	/* Set file type, based on WIN32_FIND_DATA */
     -	if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    @@ compat/win32/dirent.c: static inline void finddata2dirent(struct dirent *ent, WI
      		ent->d_type = DT_DIR;
      	else
      		ent->d_type = DT_REG;
    -
    - ## compat/win32/fscache.c ##
    -@@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fscache *cache,
    - 		fdata->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ?
    - 		fdata->EaSize : 0;
    - 
    --	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes);
    --	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
    --	fse->u.s.st_size = fdata->EndOfFile.LowPart |
    --		(((off_t)fdata->EndOfFile.HighPart) << 32);
    -+	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    -+					    fdata->EaSize);
    -+	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    -+			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
    -+	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
    -+			fdata->EndOfFile.LowPart |
    -+			(((off_t)fdata->EndOfFile.HighPart) << 32);
    - 	filetime_to_timespec((FILETIME *)&(fdata->LastAccessTime),
    - 			     &(fse->u.s.st_atim));
    - 	filetime_to_timespec((FILETIME *)&(fdata->LastWriteTime),

Skipped (obsolete): ee52791ddc (Win32: lstat(): return adequate stat.st_size for symlinks, 2015-05-16)

Reason: superseded by upstream 543a91ccf9 which computes correct symlink size via link_len instead of MAX_LONG_PATH

Skipped: 6ceb5ef587 (Win32: factor out retry logic, 2015-05-19)

Upstream equivalent: b0b32ff16f (mingw: factor out the retry logic, 2026-01-09)

Range-diff
1:  6ceb5ef587 ! 1:  b0b32ff16f Win32: factor out retry logic
    @@ Metadata
     Author: Karsten Blees <[email protected]>
     
      ## Commit message ##
    -    Win32: factor out retry logic
    +    mingw: factor out the retry logic
     
    -    The retry pattern is duplicated in three places. It also seems to be too
    -    hard to use: 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
    +    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.
    +    Introduce a `retry_ask_yes_no()` helper function that handles retry with
    +    small delay, asking the user, and restoring `errno`.
     
    -    mingw_unlink: include _wchmod in the retry loop (which may fail if the
    -    file is locked exclusively).
    +    Note that in `mingw_unlink()`, we include the `_wchmod()` call in the
    +    retry loop (which may fail if the file is locked exclusively).
     
    -    mingw_rmdir: include special error handling in the retry loop.
    +    In `mingw_rmdir()`, we include special error handling in the retry loop.
     
    -    Signed-off-by: Karsten Blees <[email protected]>
    +    Signed-off-by: Karsten Blees <[email protected]>
    +    Signed-off-by: Johannes Schindelin <[email protected]>
    +    Signed-off-by: Junio C Hamano <[email protected]>
     
      ## compat/mingw.c ##
     @@
    @@ compat/mingw.c: static wchar_t *normalize_ntpath(wchar_t *wbuf)
      {
     -	int ret, tries = 0;
     +	int tries = 0;
    - 	wchar_t wpathname[MAX_LONG_PATH];
    - 	if (xutftowcs_long_path(wpathname, pathname) < 0)
    + 	wchar_t wpathname[MAX_PATH];
    + 	if (xutftowcs_path(wpathname, pathname) < 0)
      		return -1;
     @@ compat/mingw.c: int mingw_unlink(const char *pathname, int handle_in_use_error)
      	if (DeleteFileW(wpathname))
    @@ compat/mingw.c: static int is_dir_empty(const wchar_t *wpath)
      {
     -	int ret, tries = 0;
     +	int tries = 0;
    - 	wchar_t wpathname[MAX_LONG_PATH];
    + 	wchar_t wpathname[MAX_PATH];
      	struct stat st;
      
     @@ compat/mingw.c: int mingw_rmdir(const char *pathname)
    - 	if (xutftowcs_long_path(wpathname, pathname) < 0)
    + 	if (xutftowcs_path(wpathname, pathname) < 0)
      		return -1;
      
     -	while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {

Skipped: 38cb381da6 (Win32: implement readlink(), 2015-05-24)

Upstream equivalent: 543a91ccf9 (mingw: compute the correct size for symlinks in mingw_lstat(), 2026-01-09)

Range-diff
1:  38cb381da6 ! 1:  543a91ccf9 Win32: implement readlink()
    @@
      ## Metadata ##
    -Author: Karsten Blees <[email protected]>
    +Author: Bill Zissimopoulos <[email protected]>
     
      ## Commit message ##
    -    Win32: implement readlink()
    +    mingw: compute the correct size for symlinks in `mingw_lstat()`
     
    -    Implement readlink() by reading NTFS reparse points. Works for symlinks
    -    and directory junctions. If symlinks are disabled, fail with ENOSYS.
    +    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".
     
    -    Signed-off-by: Karsten Blees <[email protected]>
    +    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.
     
    - ## compat/mingw-posix.h ##
    -@@ compat/mingw-posix.h: struct utsname {
    -  * trivial stubs
    -  */
    - 
    --static inline int readlink(const char *path UNUSED, char *buf UNUSED, size_t bufsiz UNUSED)
    --{ errno = ENOSYS; return -1; }
    - static inline int symlink(const char *oldpath UNUSED, const char *newpath UNUSED)
    - { errno = ENOSYS; return -1; }
    - static inline int fchmod(int fildes UNUSED, mode_t mode UNUSED)
    -@@ compat/mingw-posix.h: int setitimer(int type, struct itimerval *in, struct itimerval *out);
    - int sigaction(int sig, struct sigaction *in, struct sigaction *out);
    - int link(const char *oldpath, const char *newpath);
    - int uname(struct utsname *buf);
    -+int readlink(const char *path, char *buf, size_t bufsiz);
    - 
    - /*
    -  * replacements of existing functions
    +    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 <[email protected]>
    +    Signed-off-by: Johannes Schindelin <[email protected]>
    +    Signed-off-by: Junio C Hamano <[email protected]>
     
      ## compat/mingw.c ##
     @@
    @@ compat/mingw.c
      #include <winternl.h>
      
      #define STATUS_DELETE_PENDING ((NTSTATUS) 0xC0000056)
    -@@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
    - 	return 0;
    +@@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
    + 	return 1;
      }
      
     +#ifndef _WINNT_H
    @@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
     +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
     +#endif
     +
    -+int readlink(const char *path, char *buf, size_t bufsiz)
    ++static int read_reparse_point(const WCHAR *wpath, BOOL fail_on_unknown_tag,
    ++			      char *tmpbuf, int *plen, DWORD *ptag)
     +{
     +	HANDLE handle;
    -+	WCHAR wpath[MAX_LONG_PATH], *wbuf;
    ++	WCHAR *wbuf;
     +	REPARSE_DATA_BUFFER *b = alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
     +	DWORD dummy;
    -+	char tmpbuf[MAX_LONG_PATH];
    -+	int len;
    -+
    -+	if (xutftowcs_long_path(wpath, path) < 0)
    -+		return -1;
     +
     +	/* read reparse point data */
     +	handle = CreateFileW(wpath, 0,
    @@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
     +	CloseHandle(handle);
     +
     +	/* get target path for symlinks or mount points (aka 'junctions') */
    -+	switch (b->ReparseTag) {
    ++	switch ((*ptag = b->ReparseTag)) {
     +	case IO_REPARSE_TAG_SYMLINK:
     +		wbuf = (WCHAR*) (((char*) b->SymbolicLinkReparseBuffer.PathBuffer)
     +				+ b->SymbolicLinkReparseBuffer.SubstituteNameOffset);
    @@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
     +				+ b->MountPointReparseBuffer.SubstituteNameLength) = 0;
     +		break;
     +	default:
    -+		errno = EINVAL;
    -+		return -1;
    ++		if (fail_on_unknown_tag) {
    ++			errno = EINVAL;
    ++			return -1;
    ++		} else {
    ++			*plen = MAX_PATH;
    ++			return 0;
    ++		}
     +	}
     +
    -+	/*
    -+	 * Adapt to strange readlink() API: Copy up to bufsiz *bytes*, potentially
    -+	 * cutting off a UTF-8 sequence. Insufficient bufsize is *not* a failure
    -+	 * condition. There is no conversion function that produces invalid UTF-8,
    -+	 * so convert to a (hopefully large enough) temporary buffer, then memcpy
    -+	 * the requested number of bytes (including '\0' for robustness).
    -+	 */
    -+	if ((len = xwcstoutf(tmpbuf, normalize_ntpath(wbuf), MAX_LONG_PATH)) < 0)
    ++	if ((*plen =
    ++	     xwcstoutf(tmpbuf, normalize_ntpath(wbuf), MAX_PATH)) <  0)
     +		return -1;
    -+	memcpy(buf, tmpbuf, min(bufsiz, len + 1));
    -+	return min(bufsiz, len);
    ++	return 0;
     +}
     +
    - pid_t waitpid(pid_t pid, int *status, int options)
    + int mingw_lstat(const char *file_name, struct stat *buf)
      {
    - 	HANDLE h = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
    + 	WIN32_FILE_ATTRIBUTE_DATA fdata;
    +-	WIN32_FIND_DATAW findbuf = { 0 };
    ++	DWORD reparse_tag = 0;
    ++	int link_len = 0;
    + 	wchar_t wfilename[MAX_PATH];
    + 	int wlen = xutftowcs_path(wfilename, file_name);
    + 	if (wlen < 0)
    +@@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
    + 	}
    + 
    + 	if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
    +-		/* for reparse points, use FindFirstFile to get the reparse tag */
    ++		/* for reparse points, get the link tag and length */
    + 		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
    +-			HANDLE handle = FindFirstFileW(wfilename, &findbuf);
    +-			if (handle == INVALID_HANDLE_VALUE)
    +-				goto error;
    +-			FindClose(handle);
    ++			char tmpbuf[MAX_PATH];
    ++
    ++			if (read_reparse_point(wfilename, FALSE, tmpbuf,
    ++					       &link_len, &reparse_tag) < 0)
    ++				return -1;
    + 		}
    + 		buf->st_ino = 0;
    + 		buf->st_gid = 0;
    + 		buf->st_uid = 0;
    + 		buf->st_nlink = 1;
    + 		buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes,
    +-				findbuf.dwReserved0);
    +-		buf->st_size = fdata.nFileSizeLow |
    +-			(((off_t)fdata.nFileSizeHigh)<<32);
    ++				reparse_tag);
    ++		buf->st_size = S_ISLNK(buf->st_mode) ? link_len :
    ++			fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32);
    + 		buf->st_dev = buf->st_rdev = 0; /* not used by Git */
    + 		filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
    + 		filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
    + 		filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
    + 		return 0;
    + 	}
    +-error:
    ++
    + 	switch (GetLastError()) {
    + 	case ERROR_ACCESS_DENIED:
    + 	case ERROR_SHARING_VIOLATION:

Skipped: 55408628ef (mingw: lstat: compute correct size for symlinks, 2020-05-28)

Upstream equivalent: 543a91ccf9 (mingw: compute the correct size for symlinks in mingw_lstat(), 2026-01-09)

Range-diff
1:  55408628ef ! 1:  543a91ccf9 mingw: lstat: compute correct size for symlinks
    @@ Metadata
     Author: Bill Zissimopoulos <[email protected]>
     
      ## Commit message ##
    -    mingw: lstat: compute correct size for symlinks
    +    mingw: compute the correct size for symlinks in `mingw_lstat()`
     
    -    This commit fixes mingw_lstat by computing the proper size for symlinks
    -    according to POSIX. 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".
    +    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".
     
    -    Prior to this commit the mingw_lstat function returned a fixed size of
    -    4096. This caused problems in git repositories that were 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 4096; at a later time git for Cygwin or git for WSL would find
    -    that symlinks have changed size during `git status`. Vice versa doing
    -    `git reset --hard` in git for Cygwin or git for WSL would update the
    -    size of symlinks in the index with the correct value, only for git for
    -    Windows to find incorrectly at a later time that the size had changed.
    +    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 <[email protected]>
         Signed-off-by: Johannes Schindelin <[email protected]>
    +    Signed-off-by: Junio C Hamano <[email protected]>
     
      ## compat/mingw.c ##
    +@@
    + #define SECURITY_WIN32
    + #include <sspi.h>
    + #include <wchar.h>
    ++#include <winioctl.h>
    + #include <winternl.h>
    + 
    + #define STATUS_DELETE_PENDING ((NTSTATUS) 0xC0000056)
     @@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
      	return 1;
      }
      
    -+static int readlink_1(const WCHAR *wpath, BOOL fail_on_unknown_tag,
    -+		      char *tmpbuf, int *plen, DWORD *ptag);
    ++#ifndef _WINNT_H
    ++/*
    ++ * The REPARSE_DATA_BUFFER structure is defined in the Windows DDK (in
    ++ * ntifs.h) and in MSYS1's winnt.h (which defines _WINNT_H). So define
    ++ * it ourselves if we are on MSYS2 (whose winnt.h defines _WINNT_).
    ++ */
    ++typedef struct _REPARSE_DATA_BUFFER {
    ++	DWORD  ReparseTag;
    ++	WORD   ReparseDataLength;
    ++	WORD   Reserved;
    ++#ifndef _MSC_VER
    ++	_ANONYMOUS_UNION
    ++#endif
    ++	union {
    ++		struct {
    ++			WORD   SubstituteNameOffset;
    ++			WORD   SubstituteNameLength;
    ++			WORD   PrintNameOffset;
    ++			WORD   PrintNameLength;
    ++			ULONG  Flags;
    ++			WCHAR PathBuffer[1];
    ++		} SymbolicLinkReparseBuffer;
    ++		struct {
    ++			WORD   SubstituteNameOffset;
    ++			WORD   SubstituteNameLength;
    ++			WORD   PrintNameOffset;
    ++			WORD   PrintNameLength;
    ++			WCHAR PathBuffer[1];
    ++		} MountPointReparseBuffer;
    ++		struct {
    ++			BYTE   DataBuffer[1];
    ++		} GenericReparseBuffer;
    ++	} DUMMYUNIONNAME;
    ++} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
    ++#endif
    ++
    ++static int read_reparse_point(const WCHAR *wpath, BOOL fail_on_unknown_tag,
    ++			      char *tmpbuf, int *plen, DWORD *ptag)
    ++{
    ++	HANDLE handle;
    ++	WCHAR *wbuf;
    ++	REPARSE_DATA_BUFFER *b = alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
    ++	DWORD dummy;
    ++
    ++	/* read reparse point data */
    ++	handle = CreateFileW(wpath, 0,
    ++			FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
    ++			OPEN_EXISTING,
    ++			FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
    ++	if (handle == INVALID_HANDLE_VALUE) {
    ++		errno = err_win_to_posix(GetLastError());
    ++		return -1;
    ++	}
    ++	if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, b,
    ++			MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &dummy, NULL)) {
    ++		errno = err_win_to_posix(GetLastError());
    ++		CloseHandle(handle);
    ++		return -1;
    ++	}
    ++	CloseHandle(handle);
    ++
    ++	/* get target path for symlinks or mount points (aka 'junctions') */
    ++	switch ((*ptag = b->ReparseTag)) {
    ++	case IO_REPARSE_TAG_SYMLINK:
    ++		wbuf = (WCHAR*) (((char*) b->SymbolicLinkReparseBuffer.PathBuffer)
    ++				+ b->SymbolicLinkReparseBuffer.SubstituteNameOffset);
    ++		*(WCHAR*) (((char*) wbuf)
    ++				+ b->SymbolicLinkReparseBuffer.SubstituteNameLength) = 0;
    ++		break;
    ++	case IO_REPARSE_TAG_MOUNT_POINT:
    ++		wbuf = (WCHAR*) (((char*) b->MountPointReparseBuffer.PathBuffer)
    ++				+ b->MountPointReparseBuffer.SubstituteNameOffset);
    ++		*(WCHAR*) (((char*) wbuf)
    ++				+ b->MountPointReparseBuffer.SubstituteNameLength) = 0;
    ++		break;
    ++	default:
    ++		if (fail_on_unknown_tag) {
    ++			errno = EINVAL;
    ++			return -1;
    ++		} else {
    ++			*plen = MAX_PATH;
    ++			return 0;
    ++		}
    ++	}
    ++
    ++	if ((*plen =
    ++	     xwcstoutf(tmpbuf, normalize_ntpath(wbuf), MAX_PATH)) <  0)
    ++		return -1;
    ++	return 0;
    ++}
     +
      int mingw_lstat(const char *file_name, struct stat *buf)
      {
    @@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
     -	WIN32_FIND_DATAW findbuf = { 0 };
     +	DWORD reparse_tag = 0;
     +	int link_len = 0;
    - 	wchar_t wfilename[MAX_LONG_PATH];
    - 	int wlen = xutftowcs_long_path(wfilename, file_name);
    + 	wchar_t wfilename[MAX_PATH];
    + 	int wlen = xutftowcs_path(wfilename, file_name);
      	if (wlen < 0)
     @@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
      	}
    @@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
     -			if (handle == INVALID_HANDLE_VALUE)
     -				goto error;
     -			FindClose(handle);
    -+			char tmpbuf[MAX_LONG_PATH];
    ++			char tmpbuf[MAX_PATH];
     +
    -+			if (readlink_1(wfilename, FALSE, tmpbuf, &link_len,
    -+				       &reparse_tag) < 0)
    ++			if (read_reparse_point(wfilename, FALSE, tmpbuf,
    ++					       &link_len, &reparse_tag) < 0)
     +				return -1;
      		}
      		buf->st_ino = 0;
    @@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
      		buf->st_nlink = 1;
      		buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes,
     -				findbuf.dwReserved0);
    --		buf->st_size = S_ISLNK(buf->st_mode) ? MAX_LONG_PATH :
    +-		buf->st_size = fdata.nFileSizeLow |
    +-			(((off_t)fdata.nFileSizeHigh)<<32);
     +				reparse_tag);
     +		buf->st_size = S_ISLNK(buf->st_mode) ? link_len :
    - 			fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32);
    ++			fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32);
      		buf->st_dev = buf->st_rdev = 0; /* not used by Git */
      		filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
    -@@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
    + 		filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
      		filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
      		return 0;
      	}
    @@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
      	switch (GetLastError()) {
      	case ERROR_ACCESS_DENIED:
      	case ERROR_SHARING_VIOLATION:
    -@@ compat/mingw.c: typedef struct _REPARSE_DATA_BUFFER {
    - } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
    - #endif
    - 
    --int readlink(const char *path, char *buf, size_t bufsiz)
    -+static int readlink_1(const WCHAR *wpath, BOOL fail_on_unknown_tag,
    -+		      char *tmpbuf, int *plen, DWORD *ptag)
    - {
    - 	HANDLE handle;
    --	WCHAR wpath[MAX_LONG_PATH], *wbuf;
    -+	WCHAR *wbuf;
    - 	REPARSE_DATA_BUFFER *b = alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
    - 	DWORD dummy;
    --	char tmpbuf[MAX_LONG_PATH];
    --	int len;
    --
    --	if (xutftowcs_long_path(wpath, path) < 0)
    --		return -1;
    - 
    - 	/* read reparse point data */
    - 	handle = CreateFileW(wpath, 0,
    -@@ compat/mingw.c: int readlink(const char *path, char *buf, size_t bufsiz)
    - 	CloseHandle(handle);
    - 
    - 	/* get target path for symlinks or mount points (aka 'junctions') */
    --	switch (b->ReparseTag) {
    -+	switch ((*ptag = b->ReparseTag)) {
    - 	case IO_REPARSE_TAG_SYMLINK:
    - 		wbuf = (WCHAR*) (((char*) b->SymbolicLinkReparseBuffer.PathBuffer)
    - 				+ b->SymbolicLinkReparseBuffer.SubstituteNameOffset);
    -@@ compat/mingw.c: int readlink(const char *path, char *buf, size_t bufsiz)
    - 				+ b->MountPointReparseBuffer.SubstituteNameLength) = 0;
    - 		break;
    - 	default:
    --		errno = EINVAL;
    --		return -1;
    -+		if (fail_on_unknown_tag) {
    -+			errno = EINVAL;
    -+			return -1;
    -+		} else {
    -+			*plen = MAX_LONG_PATH;
    -+			return 0;
    -+		}
    - 	}
    - 
    -+	if ((*plen =
    -+	     xwcstoutf(tmpbuf, normalize_ntpath(wbuf), MAX_LONG_PATH)) <  0)
    -+		return -1;
    -+	return 0;
    -+}
    -+
    -+int readlink(const char *path, char *buf, size_t bufsiz)
    -+{
    -+	WCHAR wpath[MAX_LONG_PATH];
    -+	char tmpbuf[MAX_LONG_PATH];
    -+	int len;
    -+	DWORD tag;
    -+
    -+	if (xutftowcs_long_path(wpath, path) < 0)
    -+		return -1;
    -+
    -+	if (readlink_1(wpath, TRUE, tmpbuf, &len, &tag) < 0)
    -+		return -1;
    -+
    - 	/*
    - 	 * Adapt to strange readlink() API: Copy up to bufsiz *bytes*, potentially
    - 	 * cutting off a UTF-8 sequence. Insufficient bufsize is *not* a failure
    -@@ compat/mingw.c: int readlink(const char *path, char *buf, size_t bufsiz)
    - 	 * so convert to a (hopefully large enough) temporary buffer, then memcpy
    - 	 * the requested number of bytes (including '\0' for robustness).
    - 	 */
    --	if ((len = xwcstoutf(tmpbuf, normalize_ntpath(wbuf), MAX_LONG_PATH)) < 0)
    --		return -1;
    - 	memcpy(buf, tmpbuf, min(bufsiz, len + 1));
    - 	return min(bufsiz, len);
    - }
    -
    - ## compat/win32/fscache.c ##
    -@@ compat/win32/fscache.c: int fscache_lstat(const char *filename, struct stat *st)
    - 		return -1;
    - 	}
    - 
    -+	/*
    -+	 * Special case symbolic links: FindFirstFile()/FindNextFile() did not
    -+	 * provide us with the length of the target path.
    -+	 */
    -+	if (fse->u.s.st_size == MAX_LONG_PATH && S_ISLNK(fse->st_mode)) {
    -+		char buf[MAX_LONG_PATH];
    -+		int len = readlink(filename, buf, sizeof(buf) - 1);
    -+
    -+		if (len > 0)
    -+			fse->u.s.st_size = len;
    -+	}
    -+
    - 	/* copy stat data */
    - 	st->st_ino = 0;
    - 	st->st_gid = 0;

Skipped: c92295de6b (Win32: implement basic symlink() functionality (file symlinks only), 2015-05-24)

Upstream equivalent: 593008b95d (mingw: implement basic symlink() functionality (file symlinks only), 2026-01-09)

Range-diff
1:  c92295de6b ! 1:  593008b95d Win32: implement basic symlink() functionality (file symlinks only)
    @@ Metadata
     Author: Karsten Blees <[email protected]>
     
      ## Commit message ##
    -    Win32: implement basic symlink() functionality (file symlinks only)
    +    mingw: implement basic `symlink()` functionality (file symlinks only)
     
    -    Implement symlink() that always creates file symlinks. Fails with ENOSYS
    -    if symlinks are disabled or unsupported.
    +    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.
     
    -    Note: CreateSymbolicLinkW() was introduced with symlink support in Windows
    -    Vista. For compatibility with Windows XP, we need to load it dynamically
    -    and fail gracefully if it isnt's available.
    +    This implementation fails with `ENOSYS` if symlinks are disabled or
    +    unsupported.
     
    -    Signed-off-by: Karsten Blees <[email protected]>
    +    Signed-off-by: Karsten Blees <[email protected]>
    +    Signed-off-by: Johannes Schindelin <[email protected]>
    +    Signed-off-by: Junio C Hamano <[email protected]>
     
      ## compat/mingw-posix.h ##
     @@ compat/mingw-posix.h: struct utsname {
    @@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
      
     +int symlink(const char *target, const char *link)
     +{
    -+	wchar_t wtarget[MAX_LONG_PATH], wlink[MAX_LONG_PATH];
    ++	wchar_t wtarget[MAX_PATH], wlink[MAX_PATH];
     +	int len;
     +
     +	/* fail if symlinks are disabled or API is not supported (WinXP) */
    @@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
     +		return -1;
     +	}
     +
    -+	if ((len = xutftowcs_long_path(wtarget, target)) < 0
    -+			|| xutftowcs_long_path(wlink, link) < 0)
    ++	if ((len = xutftowcs_path(wtarget, target)) < 0
    ++			|| xutftowcs_path(wlink, link) < 0)
     +		return -1;
     +
     +	/* convert target dir separators to backslashes */
    @@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
     +	return 0;
     +}
     +
    - #ifndef _WINNT_H
    - /*
    -  * The REPARSE_DATA_BUFFER structure is defined in the Windows DDK (in
    + int readlink(const char *path, char *buf, size_t bufsiz)
    + {
    + 	WCHAR wpath[MAX_PATH];

Skipped: 6c58ba208c (Win32: symlink: add support for symlinks to directories, 2015-05-24)

Upstream equivalent: 97be7aa43a (mingw: add support for symlinks to directories, 2026-01-09)

Range-diff
1:  6c58ba208c ! 1:  97be7aa43a Win32: symlink: add support for symlinks to directories
    @@ Metadata
     Author: Karsten Blees <[email protected]>
     
      ## Commit message ##
    -    Win32: symlink: add support for symlinks to directories
    +    mingw: add support for symlinks to directories
     
    -    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).
    +    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 doesn't seem to care.
    -    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,
    +    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().
    +    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.
    +    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 <[email protected]>
    +    Signed-off-by: Karsten Blees <[email protected]>
         Signed-off-by: Johannes Schindelin <[email protected]>
    +    Signed-off-by: Junio C Hamano <[email protected]>
     
      ## compat/mingw.c ##
    -@@ compat/mingw.c: static inline int is_wdir_sep(wchar_t wchar)
    - 	return wchar == L'/' || wchar == L'\\';
    +@@ compat/mingw.c: int mingw_core_config(const char *var, const char *value,
    + 	return 0;
      }
      
    ++static inline int is_wdir_sep(wchar_t wchar)
    ++{
    ++	return wchar == L'/' || wchar == L'\\';
    ++}
    ++
     +static const wchar_t *make_relative_to(const wchar_t *path,
     +				       const wchar_t *relative_to, wchar_t *out,
     +				       size_t size)
    @@ compat/mingw.c: static inline int is_wdir_sep(wchar_t wchar)
     +{
     +	HANDLE hnd;
     +	BY_HANDLE_FILE_INFORMATION fdata;
    -+	wchar_t relative[MAX_LONG_PATH];
    ++	wchar_t relative[MAX_PATH];
     +	const wchar_t *rel;
     +
     +	/* check that wlink is still a file symlink */
    @@ compat/mingw.c: static inline int is_wdir_sep(wchar_t wchar)
      static wchar_t *normalize_ntpath(wchar_t *wbuf)
      {
     @@ compat/mingw.c: int mingw_mkdir(const char *path, int mode UNUSED)
    + 	if (xutftowcs_path(wpath, path) < 0)
      		return -1;
    - 
      	ret = _wmkdir(wpath);
     +	if (!ret)
     +		process_phantom_symlinks();
    @@ compat/mingw.c: int symlink(const char *target, const char *link)
     +	switch (process_phantom_symlink(wtarget, wlink)) {
     +	case PHANTOM_SYMLINK_RETRY:	{
     +		/* if target doesn't exist, add to phantom symlinks list */
    -+		wchar_t wfullpath[MAX_LONG_PATH];
    ++		wchar_t wfullpath[MAX_PATH];
     +		struct phantom_symlink_info *psi;
     +
     +		/* convert to absolute path to be independent of cwd */
    -+		len = GetFullPathNameW(wlink, MAX_LONG_PATH, wfullpath, NULL);
    -+		if (!len || len >= MAX_LONG_PATH) {
    ++		len = GetFullPathNameW(wlink, MAX_PATH, wfullpath, NULL);
    ++		if (!len || len >= MAX_PATH) {
     +			errno = err_win_to_posix(GetLastError());
     +			return -1;
     +		}
    @@ compat/mingw.c: int wmain(int argc, const wchar_t **wargv)
      	InitializeCriticalSection(&pinfo_cs);
     +	InitializeCriticalSection(&phantom_symlinks_cs);
      
    - 	/* initialize critical section for fscache */
    - 	InitializeCriticalSection(&fscache_cs);
    + 	/* set up default file mode and file modes for stdin/out/err */
    + 	_fmode = _O_BINARY;

Skipped: 01d54a428b (mingw: special-case index entries for symlinks with buggy size, 2020-06-04)

Upstream equivalent: 44af34bde7 (mingw: special-case index entries for symlinks with buggy size, 2026-01-09)

Range-diff
1:  01d54a428b ! 1:  44af34bde7 mingw: special-case index entries for symlinks with buggy size
    @@ Commit message
         to be re-checked as if we had just checked it out.
     
         Signed-off-by: Johannes Schindelin <[email protected]>
    +    Signed-off-by: Junio C Hamano <[email protected]>
     
      ## read-cache.c ##
     @@ read-cache.c: int ie_modified(struct index_state *istate,
    @@ read-cache.c: int ie_modified(struct index_state *istate,
     +	     * So let's just special-case symlinks with a target path length
     +	     * (i.e. `sd_size`) of 4096 and force them to be re-checked.
     +	     */
    -+	    (!S_ISLNK(st->st_mode) || ce->ce_stat_data.sd_size != MAX_LONG_PATH) &&
    ++	    (!S_ISLNK(st->st_mode) || ce->ce_stat_data.sd_size != MAX_PATH) &&
     +#endif
      	    (S_ISGITLINK(ce->ce_mode) || ce->ce_stat_data.sd_size != 0))
      		return changed;

Resolved: eb053981d6 (mingw: Windows Docker volumes are not symbolic links, 2018-04-23)

Resolved fscache.c conflict by taking REBASE_HEAD version: added Windows container detection block, updated file_attr_to_st_mode call to 3-arg signature (FileAttributes, EaSize, buf), and kept symlink-aware d_type/st_size handling

Range-diff
1:  eb053981d6 ! 1:  f8c2c32be3 mingw: Windows Docker volumes are *not* symbolic links
    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsca
      		fdata->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ?
      		fdata->EaSize : 0;
      
    +-	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes);
    +-	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
    +-	fse->u.s.st_size = fdata->EndOfFile.LowPart |
    +-		(((off_t)fdata->EndOfFile.HighPart) << 32);
     +	/*
     +	 * On certain Windows versions, host directories mapped into
     +	 * Windows Containers ("Volumes", see https://docs.docker.com/storage/volumes/)
    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsca
     +		buf[off + fse->len] = '\0';
     +	}
     +
    - 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    --					    fdata->EaSize);
    ++	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
     +					    fdata->EaSize, buf);
    - 	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    - 			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
    - 	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
    ++	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    ++			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
    ++	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
    ++			fdata->EndOfFile.LowPart |
    ++			(((off_t)fdata->EndOfFile.HighPart) << 32);
    + 	filetime_to_timespec((FILETIME *)&(fdata->LastAccessTime),
    + 			     &(fse->u.s.st_atim));
    + 	filetime_to_timespec((FILETIME *)&(fdata->LastWriteTime),

Resolved: 8fddb92a79 (Win32: symlink: move phantom symlink creation to a separate function, 2018-10-26)

Resolved by keeping REBASE_HEAD's call to create_phantom_symlink(), which replaces the inline symlink creation code that was already extracted into the new function earlier in the file

Range-diff
1:  8fddb92a79 ! 1:  bd7c450357 Win32: symlink: move phantom symlink creation to a separate function
    @@ compat/mingw.c: int symlink(const char *target, const char *link)
     -	switch (process_phantom_symlink(wtarget, wlink)) {
     -	case PHANTOM_SYMLINK_RETRY:	{
     -		/* if target doesn't exist, add to phantom symlinks list */
    --		wchar_t wfullpath[MAX_LONG_PATH];
    +-		wchar_t wfullpath[MAX_PATH];
     -		struct phantom_symlink_info *psi;
     -
     -		/* convert to absolute path to be independent of cwd */
    --		len = GetFullPathNameW(wlink, MAX_LONG_PATH, wfullpath, NULL);
    --		if (!len || len >= MAX_LONG_PATH) {
    +-		len = GetFullPathNameW(wlink, MAX_PATH, wfullpath, NULL);
    +-		if (!len || len >= MAX_PATH) {
     -			errno = err_win_to_posix(GetLastError());
     -			return -1;
     -		}
    @@ compat/mingw.c: int symlink(const char *target, const char *link)
     +	return create_phantom_symlink(wtarget, wlink);
      }
      
    - #ifndef _WINNT_H
    + int readlink(const char *path, char *buf, size_t bufsiz)

To: ee62a84b1d (Merge 'readme' into HEAD, 2018-06-07) (e07a65167e..ee62a84b1d)

Statistics

Metric Count
Total conflicts 22
Skipped (upstreamed) 13
Resolved surgically 9
Range-diff (click to expand)
  1:  b7572cc797 =   1:  9dd95041af sideband: mask control characters
  2:  c9101ecf79 =   2:  085308dec8 sideband: introduce an "escape hatch" to allow control characters
  3:  201a9e8cb7 =   3:  74d30094bf sideband: do allow ANSI color sequences by default
  4:  e2b8e90103 =   4:  7399a18903 unix-socket: avoid leak when initialization fails
  5:  0db877b1c1 =   5:  a64e5c841f grep: prevent `^$` false match at end of file
  6:  e5598f48d0 =   6:  970aa0d9b3 t9350: point out that refs are not updated correctly
  7:  3d87e0a9f8 =   7:  23b2457744 transport-helper: add trailing --
  9:  ddd5b65816 =   8:  de138a2821 remote-helper: check helper status after import/export
 10:  50eaec49b1 =   9:  208b324665 clean: do not traverse mount points
 12:  2fc76a5efa =  10:  ecb16b1eab Always auto-gc after calling a fast-import transport
 13:  e428fcd9f4 =  11:  6c01bb5c71 mingw: prevent regressions with "drive-less" absolute paths
 14:  64cc5c0809 =  12:  801f26bc09 clean: remove mount points when possible
 22:  ba8efe4645 =  13:  d77e2f64d2 vcpkg_install: detect lack of Git
 24:  1a4656368d =  14:  cf66d02957 vcpkg_install: add comment regarding slow network connections
 31:  8d6eca973f =  15:  9282194972 vcbuild: install ARM64 dependencies when building ARM64 binaries
 32:  b9a643e905 =  16:  fa7c95733c vcbuild: add an option to install individual 'features'
 33:  b74f300ff7 =  17:  ef4c024a7c cmake: allow building for Windows/ARM64
  8:  c132a44d8a =  18:  11907042cd mingw: include the Python parts in the build
 34:  deee242c90 =  19:  29f5358a50 ci(vs-build) also build Windows/ARM64 artifacts
 11:  77e482b305 =  20:  f00cfdb590 win32/pthread: avoid name clashes with winpthread
 35:  15d349650d =  21:  ecca847756 Add schannel to curl installation
 40:  26d62a9144 =  22:  361624f6aa hash-object: demonstrate a >4GB/LLP64 problem
 15:  6af4208c72 =  23:  1bccbf3b89 git-compat-util: avoid redeclaring _DEFAULT_SOURCE
 36:  981b752446 =  24:  73bfe8d60c cmake(): allow setting HOST_CPU for cross-compilation
 42:  6a4b7be272 =  25:  beb05b1f8e object-file.c: use size_t for header lengths
 92:  6ea9c7be14 =  26:  891103a95d Import the source code of mimalloc v2.2.7
 41:  1c43a63a0d =  27:  3ca88e921f CMake: default Visual Studio generator has changed
 44:  bf99aaf199 =  28:  ddee9a0d88 hash algorithms: use size_t for section lengths
 93:  9e36739c12 =  29:  d7d7ea39bc mimalloc: adjust for building inside Git
 16:  b6ed1cc104 =  30:  de383fbbd7 mingw: demonstrate a `git add` issue with NTFS junctions
 43:  12cda97585 =  31:  57c312c471 .gitignore: add Visual Studio CMakeSetting.json file
 47:  59957d333f =  32:  53f1e5c18c hash-object --stdin: verify that it works with >4GB/LLP64
 94:  8fec3de90a =  33:  2664c7924b mimalloc: offer a build-time option to enable it
 23:  52fa2ffb2c =  34:  ea230d47ec t5505/t5516: allow running without `.git/branches/` in the templates
 17:  8efcfa2c86 =  35:  80f0e00b4d strbuf_realpath(): use platform-dependent API if available
 25:  993102277a =  36:  9e4e8a51e3 http: use new "best effort" strategy for Secure Channel revoke checking
 45:  ea7b2b444a =  37:  3ebbf69a56 subtree: update `contrib/subtree` `test` target
 46:  d7f4f1ec49 =  38:  bc553a8df0 CMakeLists: add default "x64-windows" arch for Visual Studio
 50:  35f6b163c0 =  39:  a1fb068483 hash-object: add another >4GB/LLP64 test case
 51:  a0c7290dbf =  40:  f348bfc3d5 setup: properly use "%(prefix)/" when in WSL
 95:  e12539bab8 =  41:  91a9f14c43 mingw: use mimalloc
 52:  3f5f17764e =  42:  31befe2477 Add config option `windows.appendAtomically`
 26:  d941d761e9 =  43:  c64dd235a7 t5505/t5516: fix white-space around redirectors
 18:  8d3faa143e =  44:  292be1f04f transport: optionally disable side-band-64k
 19:  7b837f59c7 =  45:  91c8f85c31 mingw: fix fatal error working on mapped network drives on Windows
 20:  dafc08f6b4 =  46:  6a7a79e459 clink.pl: fix MSVC compile script to handle libcurl-d.lib
 21:  a69a076023 =  47:  0111bdbaca mingw: implement a platform-specific `strbuf_realpath()`
 27:  feb30ece07 =  48:  3f7ab56b30 t3701: verify that we can add *lots* of files interactively
 28:  49bb910a77 =  49:  9ee20f240e commit: accept "scissors" with CR/LF line endings
 29:  9a3cde11b6 =  50:  c11713f74d t0014: fix indentation
 30:  977e14f630 =  51:  b2868c46a9 git-gui: accommodate for intent-to-add files
 37:  e618f99181 =  52:  9f84c2100c mingw: allow for longer paths in `parse_interpreter()`
 38:  2d23682159 =  53:  3e8836c74f compat/vcbuild: document preferred way to build in Visual Studio
 39:  378c4b16f2 =  54:  b782a751c7 http: optionally send SSL client certificate
 48:  24dcc3df8b =  55:  acaa974178 ci: run `contrib/subtree` tests in CI builds
 49:  c19744fc3e =  56:  e1a2cb45e8 CMake: show Win32 and Generator_platform build-option values
 53:  536a9c2a52 =  57:  b7e8fde69f hash-object: add a >4GB/LLP64 test case using filtered input
 54:  cd35bf4b33 =  58:  92f4389050 compat/mingw.c: do not warn when failing to get owner
 55:  3b71603592 =  59:  894f02296f mingw: $env:TERM="xterm-256color" for newer OSes
 56:  51a43eed50 =  60:  6226116030 winansi: check result and Buffer before using Name
 96:  f474630f6a =  61:  6e11345705 windows: skip linking `git-<command>` for built-ins
 97:  c1ef2a6c16 =  62:  b6822f7f99 mingw: stop hard-coding `CC = gcc`
 98:  05de8af920 =  63:  0f179516bc mingw: drop the -D_USE_32BIT_TIME_T option
 99:  918ba0775c =  64:  7bd9abe978 mingw: only use -Wl,--large-address-aware for 32-bit builds
100:  1aa530107e =  65:  09cfd29901 mingw: avoid over-specifying `--pic-executable`
101:  72e167c2ad =  66:  351961b38d mingw: set the prefix and HOST_CPU as per MSYS2's settings
102:  0b2e0b1ea5 =  67:  169fa2d509 mingw: only enable the MSYS2-specific stuff when compiling in MSYS2
103:  d9d089e9fd =  68:  7dfa024b2f mingw: rely on MSYS2's metadata instead of hard-coding it
 57:  9a55aac24d =  69:  b21302cbde MinGW: link as terminal server aware
 58:  8b99bae1ec !  70:  a3bbeba35e mingw: change core.fsyncObjectFiles = 1 by default
    @@ compat/mingw.c
      #include <conio.h>
      #include <sddl.h>
     @@ compat/mingw.c: int wmain(int argc, const wchar_t **wargv)
    - #endif
      
      	maybe_redirect_std_handles();
    + 	adjust_symlink_flags();
     +	fsync_object_files = 1;
      
      	/* determine size of argv and environ conversion buffer */
 59:  26f1d08e22 =  71:  238ace9b31 Fix Windows version resources
 60:  e30b81fd3f =  72:  27b439df18 status: fix for old-style submodules with commondir
104:  296bcb14c0 =  73:  6b826867d5 mingw: always define `ETC_*` for MSYS2 environments
  -:  ---------- >  74:  270e3dd15f max_tree_depth: lower it for clang builds in general on Windows
  -:  ---------- >  75:  85d86e262a mingw: ensure valid CTYPE
107:  32b25ffea7 !  76:  1f50763b5c mingw: allow `git.exe` to be used instead of the "Git wrapper"
    @@ compat/mingw.c: int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
      {
      	char *tmp = getenv("TMPDIR");
     @@ compat/mingw.c: static void setup_windows_environment(void)
    - 			setenv("HOME", tmp, 1);
    - 	}
    + 	if (!(tmp = getenv("MSYS")) || !strstr(tmp, "winsymlinks:nativestrict"))
    + 		has_symlinks = 0;
      
     +	if (!getenv("PLINK_PROTOCOL"))
     +		setenv("PLINK_PROTOCOL", "ssh", 0);
108:  82251529c3 =  77:  dd5ccb9546 mingw: ignore HOMEDRIVE/HOMEPATH if it points to Windows' system directory
109:  50fbcd2bcd =  78:  ede958d3f6 clink.pl: fix libexpatd.lib link error when using MSVC
110:  725cb5896e =  79:  2ca464428f Makefile: clean up .ilk files when MSVC=1
111:  f19d985da8 =  80:  ea1e3b8efb vcbuild: add support for compiling Windows resource files
112:  c6db63d689 =  81:  d65d716a77 config.mak.uname: add git.rc to MSVC builds
113:  edbffbbcda =  82:  e385d9d03c clink.pl: ignore no-stack-protector arg on MSVC=1 builds
 61:  5e3de953b2 =  83:  4306e04b6f http: optionally load libcurl lazily
114:  2f01002018 =  84:  1b19d13f9b clink.pl: move default linker options for MSVC=1 builds
 62:  34ee3680a7 =  85:  4bcd8aaa45 http: support lazy-loading libcurl also on Windows
115:  0f05ae184a =  86:  0ae6adfb6b cmake: install headless-git.
 63:  6fa7c1f470 =  87:  86cba65a7b http: when loading libcurl lazily, allow for multiple SSL backends
116:  5f107688b5 =  88:  7db1b90c0e git.rc: include winuser.h
 64:  b7e20647d7 =  89:  66013c1ab3 mingw: do load libcurl dynamically by default
 65:  61df8a70fb =  90:  86dbc5f3f0 Add a GitHub workflow to verify that Git/Scalar work in Nano Server
 66:  3ee2cd67ce =  91:  945d059068 mingw: suggest `windows.appendAtomically` in more cases
 69:  8af6f16849 =  92:  bac2efbb8f win32: use native ANSI sequence processing, if possible
 70:  503d48a907 =  93:  30e26886f6 common-main.c: fflush stdout buffer upon exit
 71:  232239bfaa =  94:  06baf4db28 t5601/t7406(mingw): do run tests with symlink support
 72:  5c8e9d6083 =  95:  917b2c71e0 win32: ensure that `localtime_r()` is declared even in i686 builds
 73:  95025b7407 =  96:  1a7274e62c Fallback to AppData if XDG_CONFIG_HOME is unset
 74:  4b35363ba0 =  97:  b37c052e77 run-command: be helpful with Git LFS fails on Windows 7
 67:  d9992b0a49 =  98:  b07f2dcd2b ci: work around a problem with HTTP/2 vs libcurl v8.10.0
 68:  0ca7e765ad =  99:  be846d1646 revision: create mark_trees_uninteresting_dense()
 75:  1e07325385 = 100:  f29e1afe0d survey: stub in new experimental 'git-survey' command
 76:  64e2fe8dff = 101:  2b82bd8151 survey: add command line opts to select references
 77:  9ffe2fa8ee = 102:  eab4df9b1f survey: start pretty printing data in table form
 78:  07d54398bd = 103:  de71dbe438 survey: add object count summary
 79:  cb3a6493c5 = 104:  df4139a485 survey: summarize total sizes by object type
 80:  aff3c05c57 = 105:  22761f7b24 survey: show progress during object walk
 82:  f68c8747a2 = 106:  ef2a6bb70b mingw: make sure `errno` is set correctly when socket operations fail
 81:  825465c6f7 = 107:  a8ae16b26d survey: add ability to track prioritized lists
 84:  59cf350f67 = 108:  abfb8f3c31 compat/mingw: handle WSA errors in strerror
 83:  9236c2948d = 109:  e50b0b560f survey: add report of "largest" paths
 86:  ab302c5b82 = 110:  867079abb2 compat/mingw: drop outdated comment
 85:  72e6d68a74 = 111:  eb8a772a86 survey: add --top=<N> option and config
 88:  3c8010b6c3 = 112:  5892f2307e t0301: actually test credential-cache on Windows
 87:  a7462d2cc1 = 113:  5b1d3b488d survey: clearly note the experimental nature in the output
 89:  2058ce7f71 = 114:  2d91dab7f5 credential-cache: handle ECONNREFUSED gracefully
 90:  7cd468e8f6 = 115:  9b802c44bf reftable: do make sure to use custom allocators
 91:  8d4233e162 = 116:  d6113d2c2d check-whitespace: avoid alerts about upstream commits
105:  04aa4bc48e <   -:  ---------- max_tree_depth: lower it for clang builds in general on Windows
106:  28dd1b6f63 <   -:  ---------- mingw: ensure valid CTYPE
117:  f5ec36ebc9 = 117:  c9ab63f495 git-svn: mark it as unsupported by the Git for Windows project
118:  e9815f74d0 = 118:  979bb88779 ci(macos): skip the `git p4` tests
121:  7f7c7928b8 = 119:  a3f19c6d18 Win32: make FILETIME conversion functions public
119:  e644b3c397 = 120:  4ce8d77c5b git-gui--askyesno: fix funny text wrapping
122:  91f7f63284 = 121:  0fe5d258be Win32: dirent.c: Move opendir down
120:  1459c281eb = 122:  490aedaf22 git-gui--askyesno (mingw): use Git for Windows' icon, if available
123:  9f7087c4de ! 123:  4e2b975669 mingw: make the dirent implementation pluggable
    @@ compat/win32/dirent.c
     +	xwcstoutf(ent->d_name, fdata->cFileName, MAX_PATH * 3);
      
      	/* Set file type, based on WIN32_FIND_DATA */
    - 	if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    + 	if ((fdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
     @@ compat/win32/dirent.c: static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
      		ent->d_type = DT_REG;
      }
124:  b7965dae55 ! 124:  95c05c30ed Win32: make the lstat implementation pluggable
    @@ compat/mingw-posix.h: int mingw_fstat(int fd, struct stat *buf);
      int mingw_utime(const char *file_name, const struct utimbuf *times);
     
      ## compat/mingw.c ##
    -@@ compat/mingw.c: static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
    - 	return do_lstat(follow, alt_name, buf);
    +@@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
    + 	return -1;
      }
      
     +int (*lstat)(const char *file_name, struct stat *buf) = mingw_lstat;
125:  84d356004c = 125:  043b570496 mingw: add infrastructure for read-only file system level caches
126:  7c775c7eda = 126:  b289e13142 mingw: add a cache below mingw's lstat and dirent implementations
127:  45711437f1 = 127:  30ae17cb26 fscache: load directories only once
128:  e51b4ed418 = 128:  c4f6aee6ce fscache: add key for GIT_TRACE_FSCACHE
129:  7db33ae6b7 = 129:  839da7d65d fscache: remember not-found directories
130:  3c8662454d = 130:  71f9dd9b1a fscache: add a test for the dir-not-found optimization
131:  cc29af6d36 = 131:  c7c3e467ad add: use preload-index and fscache for performance
132:  bebad966d0 = 132:  1f1adad69a dir.c: make add_excludes aware of fscache during status
133:  b922002c7c = 133:  d48448a02e fscache: make fscache_enabled() public
134:  14874a1158 = 134:  1afe61233f dir.c: regression fix for add_excludes with fscache
135:  92c2cee2bc = 135:  a4ea037feb fetch-pack.c: enable fscache for stats under .git/objects
136:  a583117f1b = 136:  62fe43d9f7 checkout.c: enable fscache for checkout again
137:  183a9fd2fe = 137:  eb42884394 Enable the filesystem cache (fscache) in refresh_index().
138:  57a05d9dbc = 138:  75b54250a2 fscache: use FindFirstFileExW to avoid retrieving the short name
139:  1623b4bf9a = 139:  a2150cf37b fscache: add GIT_TEST_FSCACHE support
140:  80ad05d4e6 = 140:  25e633bc36 fscache: add fscache hit statistics
141:  9087916a3f = 141:  4ad3d6b45d unpack-trees: enable fscache for sparse-checkout
142:  f80f1228b6 = 142:  f872349027 status: disable and free fscache at the end of the status command
143:  d677459ec2 = 143:  1ffd26a6ac mem_pool: add GIT_TRACE_MEMPOOL support
144:  5a6185162e = 144:  1003f0f809 fscache: fscache takes an initial size
145:  d3f50e9738 = 145:  028612a8bf fscache: update fscache to be thread specific instead of global
146:  06d8c74397 = 146:  b4bcab7c7d fscache: teach fscache to use mempool
147:  afa186da63 ! 147:  88f6c1bb14 fscache: make fscache_enable() thread safe
    @@ compat/mingw.c
      #include "wrapper.h"
      #include "write-or-die.h"
     @@ compat/mingw.c: int wmain(int argc, const wchar_t **wargv)
    - 	/* initialize critical section for waitpid pinfo_t list */
      	InitializeCriticalSection(&pinfo_cs);
    + 	InitializeCriticalSection(&phantom_symlinks_cs);
      
     +	/* initialize critical section for fscache */
     +	InitializeCriticalSection(&fscache_cs);
148:  a0173342a6 = 148:  597bb72f67 fscache: teach fscache to use NtQueryDirectoryFile
149:  664e5ab39d = 149:  5a16a22f0e fscache: remember the reparse tag for each entry
150:  463483aeb0 = 150:  e309c2ff9f fscache: implement an FSCache-aware is_mount_point()
151:  ed191e3f82 = 151:  c577edd029 clean: make use of FSCache
152:  2183eb6221 = 152:  3fc98df7d8 pack-objects (mingw): demonstrate a segmentation fault with large deltas
153:  0d86933e61 ! 153:  2406860e5a mingw: support long paths
    @@ compat/mingw.c: static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOT
     @@ compat/mingw.c: static wchar_t *normalize_ntpath(wchar_t *wbuf)
      int mingw_unlink(const char *pathname, int handle_in_use_error)
      {
    - 	int ret, tries = 0;
    + 	int tries = 0;
     -	wchar_t wpathname[MAX_PATH];
     -	if (xutftowcs_path(wpathname, pathname) < 0)
     +	wchar_t wpathname[MAX_LONG_PATH];
    @@ compat/mingw.c: static int is_dir_empty(const wchar_t *wpath)
     @@ compat/mingw.c: static int is_dir_empty(const wchar_t *wpath)
      int mingw_rmdir(const char *pathname)
      {
    - 	int ret, tries = 0;
    + 	int tries = 0;
     -	wchar_t wpathname[MAX_PATH];
     +	wchar_t wpathname[MAX_LONG_PATH];
      	struct stat st;
    @@ compat/mingw.c: int mingw_rmdir(const char *pathname)
     +	if (xutftowcs_long_path(wpathname, pathname) < 0)
      		return -1;
      
    - 	while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
    + 	do {
     @@ compat/mingw.c: static int set_hidden_flag(const wchar_t *path, int set)
      int mingw_mkdir(const char *path, int mode UNUSED)
      {
    @@ compat/mingw.c: static int set_hidden_flag(const wchar_t *path, int set)
      		return -1;
     +
      	ret = _wmkdir(wpath);
    - 	if (!ret && needs_hiding(path))
    - 		return set_hidden_flag(wpath, 1);
    + 	if (!ret)
    + 		process_phantom_symlinks();
     @@ compat/mingw.c: int mingw_open (const char *filename, int oflags, ...)
      	va_list args;
      	unsigned mode;
    @@ compat/mingw.c: ssize_t mingw_write(int fd, const void *buf, size_t len)
      int mingw_chdir(const char *dirname)
      {
     -	wchar_t wdirname[MAX_PATH];
    --	if (xutftowcs_path(wdirname, dirname) < 0)
     +	int result;
     +	wchar_t wdirname[MAX_LONG_PATH];
    + 
    +-	if (xutftowcs_path(wdirname, dirname) < 0)
     +	if (xutftowcs_long_path(wdirname, dirname) < 0)
      		return -1;
    --	return _wchdir(wdirname);
    -+	result = _wchdir(wdirname);
    + 
    + 	if (has_symlinks) {
    +@@ compat/mingw.c: int mingw_chdir(const char *dirname)
    + 		CloseHandle(hnd);
    + 	}
    + 
    +-	return _wchdir(normalize_ntpath(wdirname));
    ++	result = _wchdir(normalize_ntpath(wdirname));
     +	current_directory_len = GetCurrentDirectoryW(0, NULL);
     +	return result;
      }
    @@ compat/mingw.c: ssize_t mingw_write(int fd, const void *buf, size_t len)
      		return -1;
      	return _wchmod(wfilename, mode);
      }
    -@@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
    - static int do_lstat(int follow, const char *file_name, struct stat *buf)
    - {
    +@@ compat/mingw.c: int mingw_lstat(const char *file_name, struct stat *buf)
      	WIN32_FILE_ATTRIBUTE_DATA fdata;
    + 	DWORD reparse_tag = 0;
    + 	int link_len = 0;
     -	wchar_t wfilename[MAX_PATH];
    --	if (xutftowcs_path(wfilename, file_name) < 0)
    +-	int wlen = xutftowcs_path(wfilename, file_name);
     +	wchar_t wfilename[MAX_LONG_PATH];
    -+	if (xutftowcs_long_path(wfilename, file_name) < 0)
    ++	int wlen = xutftowcs_long_path(wfilename, file_name);
    + 	if (wlen < 0)
      		return -1;
      
    - 	if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
     @@ compat/mingw.c: int mingw_utime (const char *file_name, const struct utimbuf *times)
      	FILETIME mft, aft;
      	int rc;
    @@ compat/mingw.c: static pid_t mingw_spawnve_fd(const char *cmd, const char **argv
      	if (dir && xutftowcs_path(wdir, dir) < 0)
     @@ compat/mingw.c: int mingw_rename(const char *pold, const char *pnew)
      	static int supports_file_rename_info_ex = 1;
    - 	DWORD attrs, gle;
    + 	DWORD attrs = INVALID_FILE_ATTRIBUTES, gle;
      	int tries = 0;
     -	wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
     +	wchar_t wpold[MAX_LONG_PATH], wpnew[MAX_LONG_PATH];
154:  694f810e82 <   -:  ---------- Win32: fix 'lstat("dir/")' with long paths
155:  bc025c8d63 ! 154:  5e3ab7fa4d win32(long path support): leave drive-less absolute paths intact
    @@ Commit message
         Signed-off-by: Johannes Schindelin <[email protected]>
     
      ## compat/mingw.c ##
    -@@ compat/mingw.c: int mingw_core_config(const char *var, const char *value,
    - 	return 0;
    - }
    - 
    -+static inline int is_wdir_sep(wchar_t wchar)
    -+{
    -+	return wchar == L'/' || wchar == L'\';
    -+}
    -+
    - /* Normalizes NT paths as returned by some low-level APIs. */
    - static wchar_t *normalize_ntpath(wchar_t *wbuf)
    - {
     @@ compat/mingw.c: int handle_long_path(wchar_t *path, int len, int max_path, int expand)
      	 * "cwd + path" doesn't due to '..' components)
      	 */
156:  f87de62e94 = 155:  e68240d43d compat/fsmonitor/fsm-*-win32: support long paths
157:  9943bd66d9 = 156:  a2bd414d42 clean: suggest using `core.longPaths` if paths are too long to remove
158:  88524ea323 = 157:  f419814131 mingw: Support `git_terminal_prompt` with more terminals
159:  6b65235ccc = 158:  a01d48b9b1 compat/terminal.c: only use the Windows console if bash 'read -r' fails
160:  cd6db54876 = 159:  665368e695 mingw (git_terminal_prompt): do fall back to CONIN$/CONOUT$ method
161:  86581d67da <   -:  ---------- Win32: don't call GetFileAttributes twice in mingw_lstat()
162:  efb8e66996 <   -:  ---------- Win32: implement stat() with symlink support
163:  f30c79a02a <   -:  ---------- Win32: remove separate do_lstat() function
164:  e5004ccec7 <   -:  ---------- Win32: let mingw_lstat() error early upon problems with reparse points
165:  8b0e6c192d <   -:  ---------- mingw: teach fscache and dirent about symlinks
166:  ee52791ddc <   -:  ---------- Win32: lstat(): return adequate stat.st_size for symlinks
167:  6ceb5ef587 <   -:  ---------- Win32: factor out retry logic
168:  ae6ec2e615 = 160:  c12a49d537 Win32: change default of 'core.symlinks' to false
169:  bd618741bd <   -:  ---------- Win32: add symlink-specific error codes
170:  830ffaca99 <   -:  ---------- Win32: mingw_unlink: support symlinks to directories
171:  365d9ef0d5 <   -:  ---------- Win32: mingw_rename: support renaming symlinks
172:  ef76ad774a <   -:  ---------- Win32: mingw_chdir: change to symlink-resolved directory
173:  38cb381da6 <   -:  ---------- Win32: implement readlink()
174:  55408628ef <   -:  ---------- mingw: lstat: compute correct size for symlinks
175:  c92295de6b <   -:  ---------- Win32: implement basic symlink() functionality (file symlinks only)
176:  6c58ba208c <   -:  ---------- Win32: symlink: add support for symlinks to directories
177:  797f4d27d1 <   -:  ---------- mingw: try to create symlinks without elevated permissions
178:  daac5811a8 <   -:  ---------- mingw: emulate stat() a little more faithfully
179:  01d54a428b <   -:  ---------- mingw: special-case index entries for symlinks with buggy size
180:  4958bab6ad = 161:  6acc4b63be mingw: introduce code to detect whether we're inside a Windows container
181:  1cb648f4da = 162:  440e1c5d56 mingw: when running in a Windows container, try to rename() harder
182:  a86a9ba5e1 = 163:  bcdccb7611 mingw: move the file_attr_to_st_mode() function definition
183:  eb053981d6 ! 164:  f8c2c32be3 mingw: Windows Docker volumes are *not* symbolic links
    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsca
      		fdata->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ?
      		fdata->EaSize : 0;
      
    +-	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes);
    +-	fse->dirent.d_type = S_ISDIR(fse->st_mode) ? DT_DIR : DT_REG;
    +-	fse->u.s.st_size = fdata->EndOfFile.LowPart |
    +-		(((off_t)fdata->EndOfFile.HighPart) << 32);
     +	/*
     +	 * On certain Windows versions, host directories mapped into
     +	 * Windows Containers ("Volumes", see https://docs.docker.com/storage/volumes/)
    @@ compat/win32/fscache.c: static struct fsentry *fseentry_create_entry(struct fsca
     +		buf[off + fse->len] = '';
     +	}
     +
    - 	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
    --					    fdata->EaSize);
    ++	fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
     +					    fdata->EaSize, buf);
    - 	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    - 			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
    - 	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
    ++	fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
    ++			S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
    ++	fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
    ++			fdata->EndOfFile.LowPart |
    ++			(((off_t)fdata->EndOfFile.HighPart) << 32);
    + 	filetime_to_timespec((FILETIME *)&(fdata->LastAccessTime),
    + 			     &(fse->u.s.st_atim));
    + 	filetime_to_timespec((FILETIME *)&(fdata->LastWriteTime),
185:  82b409f971 = 165:  e58a0e0deb mingw: work around rename() failing on a read-only file
184:  8fddb92a79 ! 166:  bd7c450357 Win32: symlink: move phantom symlink creation to a separate function
    @@ compat/mingw.c: int symlink(const char *target, const char *link)
     -	switch (process_phantom_symlink(wtarget, wlink)) {
     -	case PHANTOM_SYMLINK_RETRY:	{
     -		/* if target doesn't exist, add to phantom symlinks list */
    --		wchar_t wfullpath[MAX_LONG_PATH];
    +-		wchar_t wfullpath[MAX_PATH];
     -		struct phantom_symlink_info *psi;
     -
     -		/* convert to absolute path to be independent of cwd */
    --		len = GetFullPathNameW(wlink, MAX_LONG_PATH, wfullpath, NULL);
    --		if (!len || len >= MAX_LONG_PATH) {
    +-		len = GetFullPathNameW(wlink, MAX_PATH, wfullpath, NULL);
    +-		if (!len || len >= MAX_PATH) {
     -			errno = err_win_to_posix(GetLastError());
     -			return -1;
     -		}
    @@ compat/mingw.c: int symlink(const char *target, const char *link)
     +	return create_phantom_symlink(wtarget, wlink);
      }
      
    - #ifndef _WINNT_H
    + int readlink(const char *path, char *buf, size_t bufsiz)
186:  ceb439c192 ! 167:  83e1480fad Introduce helper to create symlinks that knows about index_state
    @@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
     -int symlink(const char *target, const char *link)
     +int mingw_create_symlink(struct index_state *index UNUSED, const char *target, const char *link)
      {
    - 	wchar_t wtarget[MAX_LONG_PATH], wlink[MAX_LONG_PATH];
    + 	wchar_t wtarget[MAX_PATH], wlink[MAX_PATH];
      	int len;
     
      ## entry.c ##
187:  2b8f87faea ! 168:  0e72f52f29 mingw: allow to specify the symlink type in .gitattributes
    @@ compat/mingw.c: int link(const char *oldpath, const char *newpath)
     +
     +int mingw_create_symlink(struct index_state *index, const char *target, const char *link)
      {
    - 	wchar_t wtarget[MAX_LONG_PATH], wlink[MAX_LONG_PATH];
    + 	wchar_t wtarget[MAX_PATH], wlink[MAX_PATH];
      	int len;
     @@ compat/mingw.c: int mingw_create_symlink(struct index_state *index UNUSED, const char *target, c
      		if (wtarget[len] == '/')
    @@ compat/mingw.c: int mingw_create_symlink(struct index_state *index UNUSED, const
     +	return -1;
      }
      
    - #ifndef _WINNT_H
    + int readlink(const char *path, char *buf, size_t bufsiz)
188:  746dcb6552 = 169:  b2230eff26 Win32: symlink: add test for `symlink` attribute
189:  1d994c000c = 170:  54aa73e67d mingw: explicitly specify with which cmd to prefix the cmdline
190:  f69430461b = 171:  e31bba0684 mingw: when path_lookup() failed, try BusyBox
191:  c39fd215fe = 172:  098badcfc3 test-tool: learn to act as a drop-in replacement for `iconv`
192:  a3df806c92 = 173:  efb595de96 tests(mingw): if `iconv` is unavailable, use `test-helper --iconv`
193:  06e2aff66d = 174:  46d885e2cb gitattributes: mark .png files as binary
194:  1173e4bb9c = 175:  ef580a3a89 tests: move test PNGs into t/lib-diff/
195:  5e360db826 = 176:  d7ae11199b tests: only override sort & find if there are usable ones in /usr/bin/
196:  4ff2d8e14e = 177:  959a456132 tests: use the correct path separator with BusyBox
197:  1e820ff0f1 = 178:  b2804e3706 mingw: only use Bash-ism `builtin pwd -W` when available
198:  8a93857eac = 179:  ae1aa3fb18 tests (mingw): remove Bash-specific pwd option
199:  f77554da92 = 180:  d7b3002a04 test-lib: add BUSYBOX prerequisite
206:  07fca2f81a = 181:  8dde67c13d Describe Git for Windows' architecture [no ci]
200:  da2dc16cc7 = 182:  46320651ed t5003: use binary file from t/lib-diff/
207:  e488bbd3cd = 183:  10378d8aba Modify the Code of Conduct for Git for Windows
201:  cf34617055 = 184:  0f826fa3f3 t5532: workaround for BusyBox on Windows
208:  6fdb617bd4 = 185:  45d71614ee CONTRIBUTING.md: add guide for first-time contributors
202:  11fdc41094 = 186:  1bae046381 t5605: special-case hardlink test for BusyBox-w32
209:  ae446853c5 = 187:  399dcfc358 README.md: Add a Windows-specific preamble
203:  e52cd4afce = 188:  c951a50e42 t5813: allow for $PWD to be a Windows path
210:  e54f74d98d = 189:  d3e65d0461 Add an issue template
204:  25b6a2d38a = 190:  ed610d02e5 t9200: skip tests when $PWD contains a colon
211:  4292bc3ca4 = 191:  ac84fa7629 mingw: kill child processes in a gentler way
212:  70d123f805 = 192:  c25c8eba84 Add a GitHub workflow to monitor component updates
213:  67eb5e48ad = 193:  5458453c44 Modify the GitHub Pull Request template (to reflect Git for Windows)
205:  997c3b5ffe = 194:  9489ca32f4 mingw: optionally enable wsl compability file mode bits
214:  5ab983f589 = 195:  952b5beac4 mingw: really handle SIGINT
215:  902e343c3d = 196:  d3c6d11492 Partially un-revert "editor: save and reset terminal after calling EDITOR"
216:  51e1360629 = 197:  2792c1a164 reset: reinstate support for the deprecated --stdin option
217:  5ea59fd7f3 = 198:  8ab6252006 fsmonitor: reintroduce core.useBuiltinFSMonitor
218:  95d9d1ce1e = 199:  018ed9cd5e dependabot: help keeping GitHub Actions versions up to date
219:  b810f3da87 = 200:  bb8289c2bd SECURITY.md: document Git for Windows' policies

@dscho

dscho commented Feb 17, 2026

Copy link
Copy Markdown
Member Author

While collecting the material for my two preceding PR comments, I noticed a couple of issues that I fixed via this push.

Range-diff
  • 1: d5a284c ! 1: f9bf5de ci(rebase-shears): accept branch as workflow input

    @@ .github/workflows/rebase-shears.yml
     @@
      name: Merging-Rebase Automation
     -run-name: Rebase the shears/seen branch
    -+run-name: Rebase the shears/{{ inputs.branch }} branch
    ++run-name: Rebase the shears/${{ inputs.branch }} branch
      
      on:
        workflow_dispatch:
  • 2: 12595ed = 2: eb64b3d ci(rebase-branch): enable command tracing

  • 3: 8ddf6f8 = 3: b321423 ci(rebase-branch): generate range-diff report and job summary

  • 4: cd847f7 = 4: 45b3f22 ci(rebase-branch): sync with origin/main before rebasing

  • 5: 38e880d = 5: 772e6ae ci(rebase-branch): write step summary even when already up to date

  • 6: 0cdac43 = 6: 28747ee ci(rebase-branch): refactor rebase call into run_rebase()

  • 7: 5997788 = 7: 22bc539 ci(rebase-branch): add upstream correspondence map for trivial skips

  • 8: e4e81eb = 8: ed9aa24 ci(rebase-branch): add sibling correspondence map

  • 9: efbc2a6 = 9: 4b980ea ci(rebase-shears): fetch all shears branches

  • 10: c026f49 = 10: d44fd42 ci(rebase-branch): add previous correspondence map for adoption path

  • 11: 33a6fbb = 11: b81927a ci: add conflict-resolver agent instructions

  • 12: 2d51a23 = 12: 67a3b30 ci(rebase-branch): add minimal AI conflict resolution

  • 13: b122cb1 = 13: e128f42 ci(rebase-branch): add log-L investigation commands to AI prompt

  • 14: ddd7514 = 14: 052fc83 ci(rebase-branch): pass tried correspondences to AI prompt

  • 15: 9c3e111 ! 15: 3faf4da ci(rebase-branch): verify build after AI conflict resolution

    @@ Commit message
         semantically reasonable but syntactically broken (e.g. a missing
         comma, a mismatched brace, or a reference to a renamed symbol).
     
    +    The ideal case would be, of course, to build on Windows. But I had so
    +    many troubles, hitting so many bugs, trying to run GitHub Copilot CLI
    +    on a Windows runner, that I gave up on that. For now.
    +
         Assisted-by: Claude Opus 4.6
         Signed-off-by: Johannes Schindelin <[email protected]>
     
    + ## .github/workflows/rebase-shears.yml ##
    +@@ .github/workflows/rebase-shears.yml: jobs:
    +           git remote add upstream https://github.com/git/git.git || true
    +           git fetch upstream --no-tags
    + 
    ++      - name: Install build dependencies
    ++        run: |
    ++          sudo apt-get update -q
    ++          sudo apt-get install -y -q libcurl4-openssl-dev libexpat-dev gettext zlib1g-dev
    ++
    +       - name: Run rebase
    +         id: rebase
    +         working-directory: git
    +
      ## rebase-branch.sh ##
     @@ rebase-branch.sh: Your FINAL line must be exactly: skip <oid>, continue, or fail"
      		;;
    @@ rebase-branch.sh: Your FINAL line must be exactly: skip <oid>, continue, or fail
     +
     +		# Verify build before committing the resolution
     +		echo "::group::Verifying build"
    -+		if ! make -j$(nproc) 2>&1 | tee make.log; then
    ++		{ make -j$(nproc) 2>&1; echo $? >make.exitcode; } | tee make.log
    ++		if test "$(cat make.exitcode)" != 0; then
     +			echo "::endgroup::"
     +			echo "::warning::Build failed after conflict resolution, giving AI another chance"
     +
    @@ rebase-branch.sh: Your FINAL line must be exactly: skip <oid>, continue, or fail
     +
     +			# Verify build again
     +			echo "::group::Verifying build (retry)"
    -+			if ! make -j$(nproc) 2>&1 | tee make.log; then
    ++			{ make -j$(nproc) 2>&1; echo $? >make.exitcode; } | tee make.log
    ++			if test "$(cat make.exitcode)" != 0; then
     +				echo "::endgroup::"
     +				echo "::error::Build still fails after retry"
     +				cat >>"$REPORT_FILE" <<-BUILD_FAIL_EOF
  • 16: b1e5943 = 16: cbafd9a ci(rebase-branch): robust decision parsing for Copilot output

  • 17: ba99e4e ! 17: 2356d22 ci(rebase-branch): add detailed conflict reporting and statistics

    @@ rebase-branch.sh: test "$PARENT_COUNT" -eq 3 || # commit itself + 2 parents
     +	SED_SCRIPT=$(sed 's/\([^ ]*\) \(.*\)/s,\1,\1 (upstream: \2),/' "$SKIPPED_MAP_FILE")
     +	RANGE_DIFF=$(echo "$RANGE_DIFF" | sed "$SED_SCRIPT")
     +fi
    ++# Also annotate from the upstream correspondence map (catches commits that
    ++# git rebase dropped silently without ever entering the conflict loop)
    ++if test -s "$UPSTREAM_MAP"; then
    ++	SED_SCRIPT=$(sed -n 's/^[0-9]*: \([0-9a-f]*\) [!=] [0-9]*: \([0-9a-f]*\).*/s,\1 \([^(]\),\1 (upstream: \2) \\1,/p' "$UPSTREAM_MAP")
    ++	if test -n "$SED_SCRIPT"; then
    ++		RANGE_DIFF=$(echo "$RANGE_DIFF" | sed "$SED_SCRIPT")
    ++	fi
    ++fi
     +
      # Finalize report
      NEW_TIP=$(git rev-parse HEAD)
  • 18: 6f69adf = 18: cc7dffb ci(rebase-branch): support skipping obsolete patches with a reason

  • -: ------- > 19: 36eeb63 ci(rebase-branch): report AI conflict resolutions in step summary

  • 19: bcf58ec ! 20: dcd54f1 ci(rebase-shears): support rebasing all branches at once

    @@ Commit message
      ## .github/workflows/rebase-shears.yml ##
     @@
      name: Merging-Rebase Automation
    --run-name: Rebase the shears/{{ inputs.branch }} branch
    -+run-name: Rebase {{ inputs.branch == 'all' && 'all shears/* branches' || format('the shears/{} branch', inputs.branch) }}
    +-run-name: Rebase the shears/${{ inputs.branch }} branch
    ++run-name: Rebase ${{ inputs.branch == 'all' && 'all shears/* branches' || format('the shears/{} branch', inputs.branch) }}
      
      on:
        workflow_dispatch:
    @@ .github/workflows/rebase-shears.yml
      
      jobs:
     @@ .github/workflows/rebase-shears.yml: jobs:
    -           git remote add upstream https://github.com/git/git.git || true
    -           git fetch upstream --no-tags
    +           sudo apt-get update -q
    +           sudo apt-get install -y -q libcurl4-openssl-dev libexpat-dev gettext zlib1g-dev
      
     -      - name: Run rebase
     -        id: rebase
  • 20: 7b828fa = 21: b042e8e ci(rebase-shears): add push step with app token

  • 21: 0a88373 ! 22: 52ef153 ci(rebase-shears): run on schedule with ref-change detection

    @@ Commit message
         Signed-off-by: Johannes Schindelin <[email protected]>
     
      ## .github/workflows/rebase-shears.yml ##
    -@@ .github/workflows/rebase-shears.yml: name: Merging-Rebase Automation
    - run-name: Rebase {{ inputs.branch == 'all' && 'all shears/* branches' || format('the shears/{} branch', inputs.branch) }}
    +@@
    + name: Merging-Rebase Automation
    +-run-name: Rebase ${{ inputs.branch == 'all' && 'all shears/* branches' || format('the shears/{} branch', inputs.branch) }}
    ++run-name: Rebase ${{ (inputs.branch == '' || inputs.branch == 'all') && 'all shears/* branches' || format('the shears/{} branch', inputs.branch) }}
      
      on:
     +  schedule:
    @@ .github/workflows/rebase-shears.yml: jobs:
     +              }
     +            })
      
    +       - name: Install build dependencies
    ++        if: steps.precheck.outputs.skip != 'true'
    +         run: |
    +           sudo apt-get update -q
    +           sudo apt-get install -y -q libcurl4-openssl-dev libexpat-dev gettext zlib1g-dev
    + 
            - name: Run rebase (single branch)
              id: rebase-single
     -        if: env.BRANCH != 'all'
  • 22: 4b3103f = 23: b19c833 ci(rebase-shears): track which run originally observed the ref state

@dscho

dscho commented Feb 18, 2026

Copy link
Copy Markdown
Member Author

After thinking about it a bit more, I am concerned that the way the scheduled trigger is implemented right now will result in a long list of workflow runs that are totally uninteresting ("skipped" but not skipped). And in between, sparsely sprinkled and hard to find, there are the actual runs that do something. This is a terrible user experience. I would not be able to find all the runs that did actually resolve some merge conflicts, but it is highly important that those are closely monitored. After all, I compare full Git for Windows rebases against the shears/main branch to increase confidence in releases, and the shears/main branch will have its mere conflicts resolved by AI.

Here are a couple of ideas I had, but none of them strike me as a really good silver bullet:

  • Trigger this workflow by a separate scheduled workflow. This might be the most robust, but it strikes me as inelegant. It would still litter the "all workflow runs" list.

  • Install webhooks on git-for-windows/git and git/git that trigger the GitForWindowsHelper GitHub App, and let that App trigger the actual workflow run. This might look most elegant in the output, but it is the most fragile of the viable options that I could think of. It would be in line with how slash commands are handled, but the logs would be opaque (hard to debug when runs were unexpectedly not triggered).

    • A slight variation: Add a scheduled function to the GitForWindowsHelper that would poll and trigger. This would be quite a bit more opaque, still.
  • Instead of attaching the check runs to git-for-windows-automation, attach them to the main branch's tip commit in the git repository. And then simply not care about the list of workflow runs but care about the list of check runs instead. However, there would be more than one check run for the same commit because the upstream branches change more frequently than main. And GitHub has no way to visualize a list of previous check runs of the same name. It only shows the latest one that is attached to the commit.

  • Mirror the report to Issues (or even Pull Requests) that might work but I would want a new repository for that, otherwise it would litter the bug tracker of Git for Windows which would be no good.

  • Send the reports via email. However, I have really bad experience with sending email from CI because too many spammers seem to have done that and therefore those IPs are usually blocked from sending emails.

Does anybody have any better idea? I don't really like any of the ones I've just presented.

@dscho

dscho commented Feb 18, 2026

Copy link
Copy Markdown
Member Author
  • Mirror the report to Issues (or even Pull Requests)

That might actually be the best idea: Fork git to shears-builds, and open a PR for every newly-updated shears/* branch tip. It might be a bit hard on resources, but it would provide for the best documentation overall: Everything is traceable, no log to be culled after 30 or 90 days.

Thoughts?

@mjcheetham

Copy link
Copy Markdown
Member
  • Mirror the report to Issues (or even Pull Requests)

That might actually be the best idea: Fork git to shears-builds, and open a PR for every newly-updated shears/* branch tip. It might be a bit hard on resources, but it would provide for the best documentation overall: Everything is traceable, no log to be culled after 30 or 90 days.

Thoughts?

A separate repository to hold the PRs (or issues) would make the most sense IMO.

@dscho

dscho commented Feb 18, 2026

Copy link
Copy Markdown
Member Author
  • Mirror the report to Issues (or even Pull Requests)

That might actually be the best idea: Fork git to shears-builds, and open a PR for every newly-updated shears/* branch tip. It might be a bit hard on resources, but it would provide for the best documentation overall: Everything is traceable, no log to be culled after 30 or 90 days.
Thoughts?

A separate repository to hold the PRs (or issues) would make the most sense IMO.

@mjcheetham Et voilà: git-for-windows/shears-builds#2

Add a script that rebases a shears/* branch onto a new upstream base
using the merging-rebase strategy. This is the foundation for automating
the Git for Windows branch maintenance workflow.

The script creates a worktree, finds the existing merging-rebase marker,
creates a new marker commit with the upstream and origin/main parents,
temporarily grafts it to look single-parent during rebase, runs the
rebase, then verifies the marker has the expected two parents.

No conflict resolution is included yet; the rebase will simply fail if
there are conflicts.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Add a GitHub Actions workflow that rebases shears/seen onto upstream/seen
using the rebase-branch.sh script. This is a starting point for testing
the automation end-to-end.

The workflow checks out the automation repo (for the script) and clones
git-for-windows/git (the repo to rebase). Since git-for-windows/git is
public, no special token is needed for read-only access at this stage.

For now this hardcodes the seen branch and has no push step; later
commits will generalize to all branches and add push capability.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Upload the rebased branch as a bundle artifact so the result can be
inspected (and potentially fetched) without needing push access. On
failure, upload both a bundle and a tar archive of the worktree so
the in-progress rebase state is preserved for debugging. The failure
bundle includes all refs (to capture refs/rewritten/* from the
in-progress rebase) while excluding origin/main to keep the size
manageable.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Allow selecting which shears branch to rebase (seen, next, main, or
maint) via workflow_dispatch input. The push trigger defaults to seen.
The main branch is mapped to upstream/master since that is what
upstream Git calls its primary branch.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Add -x to the set flags so that every command is echoed before
execution. This is invaluable for debugging CI failures where the
log is the only way to understand what happened.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
After a successful rebase, generate a range-diff comparing the original
patches with the rebased result. This is written to a markdown report
file and, when running in GitHub Actions, appended to the job summary
so the result is visible directly in the workflow run UI.

The original marker and tip OIDs are saved before the rebase starts so
the range-diff compares the right commit ranges even if future commits
add sync or adoption logic that modifies them beforehand.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Before rebasing onto the new upstream, check if origin/main has commits
that are not yet in the shears branch. All shears branches contain the
GfW-specific patches from origin/main, so new patches pushed there need
to be incorporated.

If origin/main was itself rebased (detected by the presence of a
merging-rebase marker in the new commits), adopt its state directly
rather than trying to rebase on top of it. Otherwise, rebase the new
commits from origin/main onto the shears branch tip.

After syncing, re-check whether upstream still has new commits; if not,
exit early since there is nothing left to rebase.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
When the upstream branch has no new commits, the script exits early
without producing any job summary. This leaves the GitHub Actions run
with a blank summary, giving no indication of what happened or whether
the branch is in good shape.

Write a concise summary section for this case too, with compare links
from the current tip to both `origin/main` and the upstream branch.
The links are oriented so that an up-to-date branch shows zero commits
in the comparison, making correctness easy to verify at a glance.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Introduce a thin `run_rebase()`` wrapper around the bare `git rebase`
call. For now, this function does nothing more than set GIT_EDITOR=:
to prevent the editor from opening during rebase, but it lays the
groundwork for adding conflict resolution logic in a later commit
without having to touch the main script flow.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
@dscho

dscho commented Feb 18, 2026

Copy link
Copy Markdown
Member Author

I squashed a couple of changes in and force-pushed. These are the themes:

Full range-diff between pre-/post-force-push
  • 1: a2f12b7 ! 1: e9a6fa8 ci: add minimal merging-rebase script

    @@ rebase-branch.sh (new)
     +
     +echo "::group::Setup worktree"
     +echo "Creating worktree at $WORKTREE_DIR..."
    -+git worktree add -B "$SHEARS_BRANCH" "$WORKTREE_DIR" "origin/$SHEARS_BRANCH"
    ++git worktree add -B "$SHEARS_BRANCH" "$WORKTREE_DIR" "origin/$SHEARS_BRANCH" ||
    ++	die "Could not create worktree at $WORKTREE_DIR"
     +cd "$WORKTREE_DIR"
     +echo "::endgroup::"
     +
    @@ rebase-branch.sh (new)
     +	-p "$GFW_MAIN_BRANCH" \
     +	-m "Start the merging-rebase to $UPSTREAM_BRANCH
     +
    -+This commit starts the rebase of $OLD_MARKER to $NEW_UPSTREAM")
    ++This commit starts the rebase of $OLD_MARKER to $NEW_UPSTREAM") ||
    ++	die "Could not create marker commit"
     +
     +# Use a graft so that the marker looks like a single-parent commit during rebase
    -+git replace --graft "$MARKER_OID" "$UPSTREAM_BRANCH"
    ++git replace --graft "$MARKER_OID" "$UPSTREAM_BRANCH" ||
    ++	die "Could not create graft for marker $MARKER_OID"
     +
     +REBASE_TODO_COUNT=$(git rev-list --count "$OLD_MARKER..$TIP_OID")
     +echo "Rebasing $REBASE_TODO_COUNT commits onto $MARKER_OID"
    @@ rebase-branch.sh (new)
     +echo "::endgroup::"
     +
     +# Clean up graft and verify
    -+git replace -d "$MARKER_OID"
    ++git replace -d "$MARKER_OID" ||
    ++	die "Could not remove graft for marker $MARKER_OID"
     +MARKER_IN_RESULT=$(git rev-parse "HEAD^{/Start.the.merging-rebase}")
     +PARENT_COUNT=$(git rev-list --parents -1 "$MARKER_IN_RESULT" | wc -w)
     +test "$PARENT_COUNT" -eq 3 || # commit itself + 2 parents
  • 2: bc8b0ad = 2: 4ca1247 ci: add minimal rebase-shears workflow

  • 3: 969f24a = 3: c228a0f ci(rebase-shears): upload result as bundle artifact

  • 4: f9bf5de = 4: 6ac45fb ci(rebase-shears): accept branch as workflow input

  • 5: eb64b3d = 5: 765cc37 ci(rebase-branch): enable command tracing

  • 6: b321423 = 6: b2b3295 ci(rebase-branch): generate range-diff report and job summary

  • 7: 45b3f22 ! 7: 38f0474 ci(rebase-branch): sync with origin/main before rebasing

    @@ rebase-branch.sh: echo "::notice::Old upstream: $OLD_UPSTREAM"
     +
     +# Sync with origin/main if it has commits we don't have yet
     +GFW_MAIN_BRANCH="origin/main"
    -+BEHIND_COUNT=$(git rev-list --count "$TIP_OID..$GFW_MAIN_BRANCH" || echo "0")
    ++BEHIND_COUNT=$(git rev-list --count "$TIP_OID..$GFW_MAIN_BRANCH") ||
    ++	die "Could not determine how far behind $GFW_MAIN_BRANCH we are"
     +
     +if test "$BEHIND_COUNT" -gt 0; then
     +	if git rev-list --grep='^Start the merging-rebase' "$TIP_OID..$GFW_MAIN_BRANCH" | grep -q .; then
     +		# origin/main was rebased — adopt its state directly
     +		echo "::notice::origin/main was rebased, adopting its $BEHIND_COUNT commits"
    -+		git checkout -B "$SHEARS_BRANCH" "$GFW_MAIN_BRANCH"
    ++		git checkout -B "$SHEARS_BRANCH" "$GFW_MAIN_BRANCH" ||
    ++			die "Could not adopt $GFW_MAIN_BRANCH"
     +		TIP_OID=$(git rev-parse HEAD)
     +		OLD_MARKER=$(git rev-parse "HEAD^{/Start.the.merging-rebase}")
     +		OLD_UPSTREAM=$(git rev-parse "$OLD_MARKER^1")
    @@ rebase-branch.sh: echo "::notice::Old upstream: $OLD_UPSTREAM"
     +		echo "::notice::Syncing $BEHIND_COUNT commits from $GFW_MAIN_BRANCH"
     +		echo "::group::Rebasing $BEHIND_COUNT commits from $GFW_MAIN_BRANCH on top of $SHEARS_BRANCH"
     +		GIT_EDITOR=: git rebase -r HEAD "$GFW_MAIN_BRANCH"
    -+		git checkout -B "$SHEARS_BRANCH"
    ++		git checkout -B "$SHEARS_BRANCH" ||
    ++			die "Could not update $SHEARS_BRANCH"
     +		TIP_OID=$(git rev-parse HEAD)
     +		echo "::endgroup::"
     +	fi
  • 8: 772e6ae = 8: 08485f3 ci(rebase-branch): write step summary even when already up to date

  • 9: 28747ee ! 9: 372f684 ci(rebase-branch): refactor rebase call into run_rebase()

    @@ rebase-branch.sh: if test "$BEHIND_COUNT" -gt 0; then
      		echo "::group::Rebasing $BEHIND_COUNT commits from $GFW_MAIN_BRANCH on top of $SHEARS_BRANCH"
     -		GIT_EDITOR=: git rebase -r HEAD "$GFW_MAIN_BRANCH"
     +		run_rebase -r HEAD "$GFW_MAIN_BRANCH"
    - 		git checkout -B "$SHEARS_BRANCH"
    + 		git checkout -B "$SHEARS_BRANCH" ||
    + 			die "Could not update $SHEARS_BRANCH"
      		TIP_OID=$(git rev-parse HEAD)
    - 		echo "::endgroup::"
    -@@ rebase-branch.sh: git replace --graft "$MARKER_OID" "$UPSTREAM_BRANCH"
    +@@ rebase-branch.sh: git replace --graft "$MARKER_OID" "$UPSTREAM_BRANCH" ||
      REBASE_TODO_COUNT=$(git rev-list --count "$OLD_MARKER..$TIP_OID")
      echo "Rebasing $REBASE_TODO_COUNT commits onto $MARKER_OID"
      
  • 10: 22bc539 ! 10: 9c37f62 ci(rebase-branch): add upstream correspondence map for trivial skips

    @@ rebase-branch.sh: usage () {
     +# Generate a correspondence map between two commit ranges using range-diff
     +# Usage: generate_correspondence_map <our-range> <their-range> <output-file>
     +generate_correspondence_map () {
    -+	git -c core.abbrev=false range-diff --no-color "$1" "$2" >"$3" 2>/dev/null || :
    ++	git -c core.abbrev=false range-diff --no-color "$1" "$2" >"$3" || :
     +}
     +
     +# Find a corresponding commit in a map file
  • 11: ed9aa24 ! 11: f9bb5c0 ci(rebase-branch): add sibling correspondence map

    @@ rebase-branch.sh: run_rebase () {
     +		if test -n "$SIBLING_MAP" &&
     +		   corresponding_oid=$(find_correspondence "$rebase_head_oid" "$SIBLING_MAP"); then
     +			echo "::notice::Found sibling correspondence: $corresponding_oid for $rebase_head_oneline"
    -+			if result_tree=$(git merge-tree --write-tree HEAD^ REBASE_HEAD "$corresponding_oid" 2>/dev/null) &&
    ++			if result_tree=$(git merge-tree --write-tree HEAD^ REBASE_HEAD "$corresponding_oid") &&
     +			   git read-tree --reset -u "$result_tree" &&
     +			   git commit -C REBASE_HEAD; then
     +				echo "::notice::Used sibling resolution from: $corresponding_oid"
  • 12: 4b980ea = 12: bdab7f4 ci(rebase-shears): fetch all shears branches

  • 13: d44fd42 ! 13: 311e718 ci(rebase-branch): add previous correspondence map for adoption path

    @@ rebase-branch.sh: run_rebase () {
     +			test -s "$map_file" || continue
     +			corresponding_oid=$(find_correspondence "$rebase_head_oid" "$map_file") || continue
     +			echo "::notice::Found correspondence: $corresponding_oid for $rebase_head_oneline"
    - 			if result_tree=$(git merge-tree --write-tree HEAD^ REBASE_HEAD "$corresponding_oid" 2>/dev/null) &&
    + 			if result_tree=$(git merge-tree --write-tree HEAD^ REBASE_HEAD "$corresponding_oid") &&
      			   git read-tree --reset -u "$result_tree" &&
      			   git commit -C REBASE_HEAD; then
     -				echo "::notice::Used sibling resolution from: $corresponding_oid"
    @@ rebase-branch.sh: run_rebase () {
      
      		die "Conflict requires manual resolution: $rebase_head_oneline"
      	done
    -@@ rebase-branch.sh: ORIG_TIP_OID=$TIP_OID
    - GFW_MAIN_BRANCH="origin/main"
    - BEHIND_COUNT=$(git rev-list --count "$TIP_OID..$GFW_MAIN_BRANCH" || echo "0")
    +@@ rebase-branch.sh: GFW_MAIN_BRANCH="origin/main"
    + BEHIND_COUNT=$(git rev-list --count "$TIP_OID..$GFW_MAIN_BRANCH") ||
    + 	die "Could not determine how far behind $GFW_MAIN_BRANCH we are"
      
     +PREVIOUS_MAP=""
     +
    @@ rebase-branch.sh: ORIG_TIP_OID=$TIP_OID
     +		generate_correspondence_map "$MAIN_MARKER..$GFW_MAIN_BRANCH" "$OLD_MARKER..$TIP_OID" "$PREVIOUS_MAP"
     +
      		echo "::notice::origin/main was rebased, adopting its $BEHIND_COUNT commits"
    - 		git checkout -B "$SHEARS_BRANCH" "$GFW_MAIN_BRANCH"
    - 		TIP_OID=$(git rev-parse HEAD)
    + 		git checkout -B "$SHEARS_BRANCH" "$GFW_MAIN_BRANCH" ||
    + 			die "Could not adopt $GFW_MAIN_BRANCH"
  • 14: b81927a ! 14: bb9b37d ci: add conflict-resolver agent instructions

    @@ rebase-branch.sh: run_rebase () {
      SHEARS_BRANCH=$1
      UPSTREAM_BRANCH=$2
     +SCRIPTS_DIR=${3:-$(cd "$(dirname "$0")" && pwd)}
    -+AGENTS_DIR=$(dirname "$SCRIPTS_DIR")/.github/agents
      
      # Validate branches exist
      git rev-parse --verify "origin/$SHEARS_BRANCH" >/dev/null 2>&1 ||
  • 15: 67a3b30 ! 15: 349d151 ci(rebase-branch): add minimal AI conflict resolution

    @@ Commit message
         PAT with the "Copilot Requests" permission). The script validates
         that both `copilot` and a GitHub token are available.
     
    +    Copilot must be launched from the automation repository directory so
    +    `--agent conflict-resolver` resolves to the trusted profile in
    +    `.github/agents/`. Launching from the rebase worktree can fail agent
    +    discovery and may load untrusted agent definitions from rebased
    +    content. Running from automation and exposing the worktree only via
    +    `--add-dir` keeps discovery and trust boundaries explicit.
    +
         Assisted-by: Claude Opus 4.6
         Signed-off-by: Johannes Schindelin <[email protected]>
     
    @@ rebase-branch.sh: run_rebase () {
     +# Run copilot with standard tool permissions
     +# Usage: run_copilot <prompt>
     +# Outputs to stdout (also tees to stderr for logging)
    ++# Runs from the automation repo root so that .github/agents/ there is used
    ++# (not any .github/agents/ in the worktree, which is untrusted remote code).
     +run_copilot () {
    -+	copilot -p "$1" \
    ++	(cd "$SCRIPTS_DIR" &&
    ++	  copilot -p "$1" \
    ++		--add-dir "$WORKTREE_DIR" \
     +		${COPILOT_MODEL:+--model "$COPILOT_MODEL"} \
    ++		--agent conflict-resolver \
     +		--allow-tool 'view' \
     +		--allow-tool 'write' \
    ++		--allow-tool 'shell(awk)' \
    ++		--allow-tool 'shell(cat)' \
     +		--allow-tool 'shell(git show)' \
     +		--allow-tool 'shell(git diff)' \
     +		--allow-tool 'shell(git log)' \
    @@ rebase-branch.sh: run_rebase () {
     +		--allow-tool 'shell(git rm)' \
     +		--allow-tool 'shell(grep)' \
     +		--allow-tool 'shell(head)' \
    -+		--allow-tool 'shell(tail)' \
    ++		--allow-tool 'shell(ls)' \
     +		--allow-tool 'shell(sed)' \
    -+		--allow-tool 'shell(cat)' \
    -+		--allow-tool 'shell(awk)' \
    -+		2>&1 | tee /dev/stderr
    ++		--allow-tool 'shell(tail)' \
    ++		2>&1
    ++	  echo $? >"$WORKTREE_DIR/copilot.exitcode") | tee /dev/stderr
    ++	return $(cat "$WORKTREE_DIR/copilot.exitcode")
     +}
     +
     +# Resolve a single conflict with AI
    @@ rebase-branch.sh: run_rebase () {
     +
     +	prompt="Resolve merge conflict during rebase of commit REBASE_HEAD.
     +
    ++IMPORTANT:
    ++- The target repository/worktree is: $WORKTREE_DIR
    ++- You are launched from a different directory only to load the custom agent.
    ++- Run every git command as: git -C \"$WORKTREE_DIR\" <command>
    ++- Read and edit files only inside: $WORKTREE_DIR
    ++
     +Conflicting files: $conflicting_files
     +
     +Investigation commands:
    -+- See the patch: git show REBASE_HEAD
    -+- See conflict markers: view <file>
    -+- Check if upstreamed: git range-diff REBASE_HEAD^! REBASE_HEAD..
    ++- See the patch: git -C \"$WORKTREE_DIR\" show REBASE_HEAD
    ++- See conflict markers: view \"$WORKTREE_DIR/<file>\"
    ++- Check if upstreamed: git -C \"$WORKTREE_DIR\" range-diff REBASE_HEAD^! REBASE_HEAD..
     +
     +Decision rules:
     +1. If range-diff shows correspondence (e.g. '1: abc = 1: def'), output: skip <upstream-oid>
    -+2. If patch needs surgical resolution, edit files, stage with 'git add', output: continue
    ++2. If patch needs surgical resolution, edit files, stage with 'git -C \"$WORKTREE_DIR\" add', output: continue
     +3. If unresolvable, output: fail
     +
     +Your FINAL line must be exactly: skip <oid>, continue, or fail"
    @@ rebase-branch.sh: run_rebase () {
      # Parse arguments
      test $# -ge 2 || usage
      SHEARS_BRANCH=$1
    -@@ rebase-branch.sh: UPSTREAM_BRANCH=$2
    + UPSTREAM_BRANCH=$2
      SCRIPTS_DIR=${3:-$(cd "$(dirname "$0")" && pwd)}
    - AGENTS_DIR=$(dirname "$SCRIPTS_DIR")/.github/agents
      
     +# Validate environment
     +command -v copilot >/dev/null 2>&1 || die "copilot CLI not found in PATH"
  • 16: e128f42 ! 16: 8930766 ci(rebase-branch): add log-L investigation commands to AI prompt

    @@ rebase-branch.sh: run_rebase () {
     +			count=${count:-1}
     +			end=$((start + count - 1))
     +			if test "$end" -lt "$start"; then end=$start; fi
    -+			echo "git log -L $start,$end:$1 REBASE_HEAD..HEAD"
    ++			echo "git -C \"$WORKTREE_DIR\" log -L $start,$end:$1 REBASE_HEAD..HEAD"
     +		fi
     +	done
     +}
    @@ rebase-branch.sh: resolve_conflict_with_ai () {
     +
      	prompt="Resolve merge conflict during rebase of commit REBASE_HEAD.
      
    - Conflicting files: $conflicting_files
    + IMPORTANT:
     @@ rebase-branch.sh: Investigation commands:
    - - See the patch: git show REBASE_HEAD
    - - See conflict markers: view <file>
    - - Check if upstreamed: git range-diff REBASE_HEAD^! REBASE_HEAD..
    + - See the patch: git -C \"$WORKTREE_DIR\" show REBASE_HEAD
    + - See conflict markers: view \"$WORKTREE_DIR/<file>\"
    + - Check if upstreamed: git -C \"$WORKTREE_DIR\" range-diff REBASE_HEAD^! REBASE_HEAD..
     -
    -+- Try higher creation factor: git range-diff --creation-factor=200 REBASE_HEAD^! REBASE_HEAD..
    ++- Try higher creation factor: git -C \"$WORKTREE_DIR\" range-diff --creation-factor=200 REBASE_HEAD^! REBASE_HEAD..
     +- See upstream changes to conflicting lines:
     +${log_l_commands}
      Decision rules:
      1. If range-diff shows correspondence (e.g. '1: abc = 1: def'), output: skip <upstream-oid>
    - 2. If patch needs surgical resolution, edit files, stage with 'git add', output: continue
    + 2. If patch needs surgical resolution, edit files, stage with 'git -C \"$WORKTREE_DIR\" add', output: continue
  • 17: 052fc83 ! 17: 7a49aa8 ci(rebase-branch): pass tried correspondences to AI prompt

    @@ rebase-branch.sh: run_rebase () {
      			corresponding_oid=$(find_correspondence "$rebase_head_oid" "$map_file") || continue
      			echo "::notice::Found correspondence: $corresponding_oid for $rebase_head_oneline"
     +			tried_correspondences="${tried_correspondences:+$tried_correspondences }$corresponding_oid"
    - 			if result_tree=$(git merge-tree --write-tree HEAD^ REBASE_HEAD "$corresponding_oid" 2>/dev/null) &&
    + 			if result_tree=$(git merge-tree --write-tree HEAD^ REBASE_HEAD "$corresponding_oid") &&
      			   git read-tree --reset -u "$result_tree" &&
      			   git commit -C REBASE_HEAD; then
     @@ rebase-branch.sh: run_rebase () {
    @@ rebase-branch.sh: resolve_conflict_with_ai () {
     +		correspondence_context="
     +Note: We found corresponding commits from previous/sibling rebases but they did not apply cleanly:
     +$tried_correspondences
    -+You may want to examine these with 'git show <oid>' for hints on how to resolve."
    ++You may want to examine these with 'git -C \"$WORKTREE_DIR\" show <oid>' for hints on how to resolve."
     +	fi
     +
      	prompt="Resolve merge conflict during rebase of commit REBASE_HEAD.
      
    + IMPORTANT:
    +@@ rebase-branch.sh: IMPORTANT:
    + - Read and edit files only inside: $WORKTREE_DIR
    + 
      Conflicting files: $conflicting_files
     -
     +$correspondence_context
      Investigation commands:
    - - See the patch: git show REBASE_HEAD
    - - See conflict markers: view <file>
    + - See the patch: git -C \"$WORKTREE_DIR\" show REBASE_HEAD
    + - See conflict markers: view \"$WORKTREE_DIR/<file>\"
  • 18: 3faf4da ! 18: 44bab2c ci(rebase-branch): verify build after AI conflict resolution

    @@ rebase-branch.sh: Your FINAL line must be exactly: skip <oid>, continue, or fail
     +
     +			retry_prompt="Build failed after your conflict resolution. Fix the compilation error.
     +
    ++IMPORTANT:
    ++- The target repository/worktree is: $WORKTREE_DIR
    ++- Run every git command as: git -C \"$WORKTREE_DIR\" <command>
    ++- Read and edit files only inside: $WORKTREE_DIR
    ++
     +Files with conflicts: $(git diff --name-only --diff-filter=U)
     +
     +Investigation:
    -+- See full build log: view make.log
    -+- See your changes: git diff
    -+- Edit files to fix, then: git add <file>
    ++- See full build log: view \"$WORKTREE_DIR/make.log\"
    ++- See your changes: git -C \"$WORKTREE_DIR\" diff
    ++- Edit files to fix, then: git -C \"$WORKTREE_DIR\" add <file>
     +
     +Build errors (last 15 lines):
     +$(tail -15 make.log)
  • 19: cbafd9a = 19: cd27a69 ci(rebase-branch): robust decision parsing for Copilot output

  • 20: 2356d22 ! 20: b0e1f37 ci(rebase-branch): add detailed conflict reporting and statistics

    @@ rebase-branch.sh: run_rebase () {
     +
     +			#### Skipped (trivial): $rebase_head_ref
     +
    -+			Upstream equivalent: $(git show --no-patch --format=reference "$corresponding_oid" 2>/dev/null || echo "$corresponding_oid")
    ++			Upstream equivalent: $(git show --no-patch --format=reference "$corresponding_oid" || echo "$corresponding_oid")
     +
     +			Detected via exact range-diff match (no AI needed).
     +
    @@ rebase-branch.sh: run_rebase () {
     +
     +				#### Resolved via correspondence: $rebase_head_ref
     +
    -+				Used resolution from: $(git show --no-patch --format=reference "$corresponding_oid" 2>/dev/null || echo "$corresponding_oid")
    ++				Used resolution from: $(git show --no-patch --format=reference "$corresponding_oid" || echo "$corresponding_oid")
     +
     +				CORR_EOF
     +				CONFLICTS_RESOLVED=$((CONFLICTS_RESOLVED + 1))
    @@ rebase-branch.sh: Your FINAL line must be exactly: skip <oid>, continue, or fail
     +
     +			#### Skipped: $rebase_head_ref
     +
    -+			Upstream equivalent: $(git show --no-patch --format=reference "$upstream_oid" 2>/dev/null || echo "$upstream_oid")
    ++			Upstream equivalent: $(git show --no-patch --format=reference "$upstream_oid" || echo "$upstream_oid")
     +
     +			<details>
     +			<summary>Range-diff</summary>
     +
     +			\`\`\`
    -+			$(git range-diff --creation-factor=999 "$rebase_head_oid^!" "$upstream_oid^!" 2>/dev/null || echo "Unable to generate range-diff")
    ++			$(git range-diff --creation-factor=999 "$rebase_head_oid^!" "$upstream_oid^!" || echo "Unable to generate range-diff")
     +			\`\`\`
     +
     +			</details>
  • 21: cc7dffb ! 21: 44a21a5 ci(rebase-branch): support skipping obsolete patches with a reason

    @@ rebase-branch.sh: Investigation commands:
      ${log_l_commands}
      Decision rules:
      1. If range-diff shows correspondence (e.g. '1: abc = 1: def'), output: skip <upstream-oid>
    --2. If patch needs surgical resolution, edit files, stage with 'git add', output: continue
    +-2. If patch needs surgical resolution, edit files, stage with 'git -C \"$WORKTREE_DIR\" add', output: continue
     -3. If unresolvable, output: fail
     +2. If the patch is obsolete (e.g. fixes code removed upstream), output: skip -- <reason>
    -+3. If patch needs surgical resolution, edit files, stage with 'git add', output: continue
    ++3. If patch needs surgical resolution, edit files, stage with 'git -C \"$WORKTREE_DIR\" add', output: continue
     +4. If unresolvable, output: fail
      
     -Your FINAL line must be exactly: skip <oid>, continue, or fail"
  • 22: 36eeb63 ! 22: ec9c27e ci(rebase-branch): report AI conflict resolutions in step summary

    @@ rebase-branch.sh: ${log_l_commands}
      Decision rules:
      1. If range-diff shows correspondence (e.g. '1: abc = 1: def'), output: skip <upstream-oid>
      2. If the patch is obsolete (e.g. fixes code removed upstream), output: skip -- <reason>
    --3. If patch needs surgical resolution, edit files, stage with 'git add', output: continue
    -+3. If patch needs surgical resolution, edit files, stage with 'git add', output: continue -- <brief summary of what you changed>
    +-3. If patch needs surgical resolution, edit files, stage with 'git -C \"$WORKTREE_DIR\" add', output: continue
    ++3. If patch needs surgical resolution, edit files, stage with 'git -C \"$WORKTREE_DIR\" add', output: continue -- <brief summary of what you changed>
      4. If unresolvable, output: fail
      
     -Your FINAL line must be exactly: skip <oid>, skip -- <reason>, continue, or fail"
    @@ rebase-branch.sh: Your FINAL line must be exactly: continue or fail"
     +		else
     +			git commit -C REBASE_HEAD ||
     +				die "git commit failed for $rebase_head_oneline"
    -+			resolution_rangediff=$(git range-diff --creation-factor=999 "$rebase_head_oid^!" HEAD^! 2>/dev/null || echo "Unable to generate range-diff")
    ++			resolution_rangediff=$(git range-diff --creation-factor=999 "$rebase_head_oid^!" HEAD^! || echo "Unable to generate range-diff")
     +			cat >>"$REPORT_FILE" <<-CONTINUE_EOF
     +
     +			#### Resolved: $rebase_head_ref
  • 23: dcd54f1 ! 23: ddc03ff ci(rebase-shears): support rebasing all branches at once

    @@ .github/workflows/rebase-shears.yml: jobs:
     +
     +            if test 0 = "$(git rev-list --count "origin/shears/$BRANCH..$UPSTREAM")"; then
     +              echo "::notice::Nothing to do for shears/$BRANCH: $UPSTREAM has no new commits"
    -+              continue
    -+            fi
    ++              if test -n "$GITHUB_STEP_SUMMARY"; then
    ++                cat >>"$GITHUB_STEP_SUMMARY" <<-UPTODATE_EOF
    ++                ## Rebase Summary: $BRANCH
    ++
    ++                Already up to date with \`$UPSTREAM\`; nothing to rebase.
      
     -      - name: Create worktree archive on failure
     -        if: always() && steps.rebase.outputs.failed_worktree
    ++                UPTODATE_EOF
    ++              fi
    ++              continue
    ++            fi
    ++
     +            if "$GITHUB_WORKSPACE/automation/rebase-branch.sh" "shears/$BRANCH" "$UPSTREAM" "$GITHUB_WORKSPACE/automation"; then
     +              to_push="${to_push:+$to_push }shears/$BRANCH"
     +            else
  • 24: b042e8e ! 24: 5ec94b2 ci(rebase-shears): add push step with app token

    @@ .github/workflows/rebase-shears.yml: jobs:
                if-no-files-found: warn
     +
     +      - name: Obtain installation access token
    -+        if: env.PUSH == 'true' && (steps.rebase-single.outputs.to_push || steps.rebase-all.outputs.to_push)
    ++        if: always() && env.PUSH == 'true' && (steps.rebase-single.outputs.to_push || steps.rebase-all.outputs.to_push)
     +        uses: actions/create-github-app-token@v2
     +        id: app-token
     +        with:
    @@ .github/workflows/rebase-shears.yml: jobs:
     +          repositories: git
     +
     +      - name: Push results
    -+        if: env.PUSH == 'true' && (steps.rebase-single.outputs.to_push || steps.rebase-all.outputs.to_push)
    ++        if: always() && env.PUSH == 'true' && steps.app-token.outputs.token && (steps.rebase-single.outputs.to_push || steps.rebase-all.outputs.to_push)
     +        working-directory: git
     +        run: |
     +          AUTH="$(echo -n 'x-access-token:${{ steps.app-token.outputs.token }}' | base64)"
  • 25: 52ef153 = 25: e32404c ci(rebase-shears): run on schedule with ref-change detection

  • 26: b19c833 = 26: 324aae8 ci(rebase-shears): track which run originally observed the ref state

  • -: ------- > 27: b9598c9 ci(rebase-shears): create per-branch check runs on git-for-windows/git

  • -: ------- > 28: 02bc71b Add stash-with-conflicts.sh and apply-stash-with-conflicts.sh

  • -: ------- > 29: aeb7164 ci(rebase-shears): mirror rebase outcomes to PRs in shears-builds

@dscho

dscho commented Feb 19, 2026

Copy link
Copy Markdown
Member Author

Sorry, there was an incorrectly-indented heredoc, still, which I realized only after this run failed. After I fixed it, this run succeeded (but it did not need to update any shears/* branch). Hence this force-push.

Range-diff
  • 1: ddc03ff ! 1: 5b053e2 ci(rebase-shears): support rebasing all branches at once

    @@ .github/workflows/rebase-shears.yml: jobs:
     +              echo "::notice::Nothing to do for shears/$BRANCH: $UPSTREAM has no new commits"
     +              if test -n "$GITHUB_STEP_SUMMARY"; then
     +                cat >>"$GITHUB_STEP_SUMMARY" <<-UPTODATE_EOF
    -+                ## Rebase Summary: $BRANCH
    ++          ## Rebase Summary: $BRANCH
     +
    -+                Already up to date with \`$UPSTREAM\`; nothing to rebase.
    ++          Already up to date with \`$UPSTREAM\`; nothing to rebase.
      
     -      - name: Create worktree archive on failure
     -        if: always() && steps.rebase.outputs.failed_worktree
    -+                UPTODATE_EOF
    ++          UPTODATE_EOF
     +              fi
     +              continue
     +            fi
  • 2: 5ec94b2 = 2: 12c541a ci(rebase-shears): add push step with app token

  • 3: e32404c = 3: c45e8a7 ci(rebase-shears): run on schedule with ref-change detection

  • 4: 324aae8 = 4: f7d6759 ci(rebase-shears): track which run originally observed the ref state

  • 5: b9598c9 = 5: 4ee947d ci(rebase-shears): create per-branch check runs on git-for-windows/git

  • 6: 02bc71b = 6: dd545cf Add stash-with-conflicts.sh and apply-stash-with-conflicts.sh

  • 7: aeb7164 = 7: 0ba0804 ci(rebase-shears): mirror rebase outcomes to PRs in shears-builds

When rebasing onto a new upstream, some of our commits may have been
upstreamed verbatim (cherry-picked or applied identically). These
commits produce conflicts during rebase that are trivially resolvable
by skipping them entirely.

To detect this automatically, generate a "correspondence map" using
`git range-diff` between our commit range and the upstream commit
range before starting the rebase. The `find_correspondence()`
function then looks up each conflicting REBASE_HEAD in this map: if
the range-diff reports an exact match (`=`), the commit is skipped
without any AI involvement.

The `run_rebase()` function is extended from a simple wrapper into a
conflict-handling loop: it runs the initial rebase, and if conflicts
arise, iterates over REBASE_HEAD states. For now, only exact upstream
matches are handled automatically; any other conflict is a fatal error.
Later commits will add sibling/previous correspondence maps and AI
resolution for the remaining cases.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Git for Windows maintains a hierarchy of shears branches (seen, next,
main, maint) that track the corresponding upstream branches. When
rebasing `main`, the `next` branch has likely already been rebased
onto the same upstream and resolved the same conflicts. Similarly,
`seen` is the sibling for `next`, and `main` for `maint`.

Generate a sibling correspondence map using range-diff between our
commits and the sibling branch's commits. When a conflict arises and
the upstream map does not yield an exact match, look up the conflicting
commit in the sibling map and try `git merge-tree` with the sibling's
version of the commit to produce a clean resolution without AI.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
The sibling correspondence map needs access to sibling shears branches
(e.g. shears/next when rebasing shears/main). Fetch all four shears
branches up front instead of only the one being rebased.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
When origin/main has been rebased (e.g. onto a new Git version, or to
restructure the branch thicket), the current shears branch needs to
adopt that state. Before doing so, generate a correspondence map
between origin/main's rebased commits and our current commits, so that
when the subsequent rebase hits a conflict, we can try reusing
origin/main's resolution via `git merge-tree`.

The `run_rebase()` conflict loop now iterates over both the previous
map and the sibling map (in that order) when looking for reusable
resolutions, using `continue 2` to break out of both the for loop
and the while loop iteration on success.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Add an agent instructions file that teaches an LLM how to think about
merge conflicts during rebases. The key insight is that resolving a
conflict is not about mechanically choosing "ours" or "theirs" but
about understanding the motivation behind both changes: what the
downstream patch intended to accomplish, and how upstream independently
modified the same lines. Once both motivations are understood, the
resolution follows naturally by reapplying the patch's intent on top of
the upstream's current state.

The agent also learns to detect upstreamed patches via `git range-diff`
before attempting any surgical resolution, to avoid wasted effort on
commits that can simply be skipped.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
dscho added 15 commits February 20, 2026 12:41
When a conflict cannot be resolved by the correspondence maps (exact
upstream match or merge-tree from a sibling/previous rebase), invoke
the Copilot CLI to attempt a surgical resolution. The AI receives the
list of conflicting files and a small set of investigation commands,
then outputs one of `skip <oid>`, `continue`, or `fail`.

This is intentionally minimal: the prompt just points the AI at the
right commands and decision rules. Later commits will enrich the
prompt with `git log -L` commands for understanding upstream changes,
correspondence context from failed merge-tree attempts, and build
verification after resolution.

The workflow gains a Copilot CLI installation step (via npm), a
COPILOT_MODEL environment variable (defaulting to claude-opus-4.6),
and GH_TOKEN set to the COPILOT_GITHUB_TOKEN secret (a fine-grained
PAT with the "Copilot Requests" permission). The script validates
that both `copilot` and a GitHub token are available.

Copilot must be launched from the automation repository directory so
`--agent conflict-resolver` resolves to the trusted profile in
`.github/agents/`. Launching from the rebase worktree can fail agent
discovery and may load untrusted agent definitions from rebased
content. Running from automation and exposing the worktree only via
`--add-dir` keeps discovery and trust boundaries explicit.

For that reason, Copilot must be told explicitly to run every command
prefixed by `cd $WORKTREE_DIR` (and we need to pass that environment
variable to Copilot, too).

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Give the AI two more investigation tools for understanding conflicts:

`git range-diff --creation-factor=200` as a fallback when the default
creation factor does not find a correspondence (useful for heavily
modified patches that were upstreamed with significant changes).

`git log -L <start>,<end>:<file> REBASE_HEAD..HEAD` for each
conflicting hunk, which shows exactly how upstream modified the lines
that conflict. This is invaluable for understanding the upstream's
intent when resolving surgically: the AI can see the progression of
changes that led to the current state of those lines.

The line ranges are extracted from the diff hunk headers of each
conflicting file by the new `generate_log_l_commands()` function.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
When the merge-tree approach fails for a sibling or previous
correspondence (the three-way merge still has conflicts), pass the
OIDs of those tried commits to the AI as context. The AI can then
examine them with `git show` to understand how a related rebase
resolved the same commit, which often provides strong hints for the
surgical resolution even when the merge-tree could not apply cleanly.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
After the AI resolves a conflict surgically and before letting `git
rebase --continue` commit the result, run `make` to catch
compilation errors introduced by the resolution. If the build fails,
give the AI one more chance by feeding it the last 15 lines of the
build log and asking it to fix the error. If the retry also fails to
produce a clean build, abort with exit code 2.

This catches a class of errors where the AI's resolution is
semantically reasonable but syntactically broken (e.g. a missing
comma, a mismatched brace, or a reference to a renamed symbol).

The ideal case would be, of course, to build on Windows. But I had so
many troubles, hitting so many bugs, trying to run GitHub Copilot CLI
on a Windows runner, that I gave up on that. For now.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
The Copilot CLI appends a statistics trailer to its output (lines like
`Model: claude-opus-4.6`, `Total turns: 5`, etc.) after blank
lines. The simple `sed -n | tail -1` parser would miss the decision
if it appeared before these trailing stats lines.

Replace it with a sed state machine that remembers the last line
matching `continue`, `skip <oid>`, or `fail`, then validates
that everything after it until EOF consists only of blank lines and
stats-like `key: value` lines (including indented continuation
lines). This ensures the decision is extracted correctly regardless
of what Copilot appends after the agent's final output.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Track how each conflict was resolved and produce a structured report.
Each resolution path (trivial upstream skip, merge-tree correspondence,
AI skip, AI surgical) now appends a section to the conflict report
documenting what happened. The report concludes with a statistics
summary counting skipped vs. surgically resolved conflicts.

Additionally, maintain a skipped-commits map file that records which
original commit OIDs were skipped and their upstream equivalents. After
the rebase completes, this map is used to annotate the range-diff so
that skipped commits show their upstream OID inline, making it easy to
verify that the right commits were skipped.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Downstream patches can become obsolete when upstream removes or rewrites
the code they were fixing, without there being a specific upstream commit
that corresponds to the downstream one. In this case, the AI should be
able to skip the patch and explain why. The new `skip -- <reason>`
decision format allows exactly that: the reason is surfaced in the
conflict report and in the GitHub Actions log annotations, making it
easy to audit why a commit was dropped during a rebase.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
While the logs contain all the information about a rebase run, they
are too verbose to quickly judge whether an AI conflict resolution
was correct. The step summary exists precisely to give the reviewer
that quick overview — but until now, `continue` verdicts produced no
summary entry at all, forcing the reviewer back into the logs to find
out what happened.

The AI now provides a brief rationale alongside its verdict, and a
range-diff between the original patch and the resolved one gives the
reviewer the most direct view of how the conflict resolution altered
the commit.

A conflict can legitimately resolve to nothing — for example when a
patch adapted code that was removed upstream. This is a real outcome
that deserves its own report entry ("Dropped"), not a silent empty
commit.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Add an 'all' option (the new default) that iterates over seen, next,
main, and maint in sequence. Each branch is rebased independently so
a failure in one does not prevent the others from proceeding. The
bundle/archive and push steps are updated to handle outputs from both
the single-branch and all-branches rebase steps.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Generate an installation access token from the GitHub App credentials
only right before pushing and only when a push is actually needed.
The token is passed via GIT_CONFIG_PARAMETERS setting an
http.<url>.extraHeader Authorization header, avoiding the need to
persist credentials from the checkout step.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Run the rebase workflow every six hours via cron. To avoid wasting
runner time when nothing has changed, each run records the ref tips
it observed (git-for-windows/git main and the upstream seen, next,
master and maint branches) in its check run output via the Checks
API. On schedule triggers, a lightweight pre-check step compares
current refs against the previous run's recorded state and skips the
expensive clone and rebase entirely if nothing moved.

The ref state flows through a REF_STATE environment variable: the
pre-check sets it from the previous run's check run summary, and
the fetch step overwrites it with the actual cloned refs when the
rebase proceeds. The "Record ref state" step writes whichever value
survived to the current run's check run output, ensuring the next
scheduled run can compare against it.

The push trigger was only used for debugging and is removed now that
the workflow has a proper schedule.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
When several consecutive scheduled runs find nothing to do, each one
skips the expensive rebase and re-records the same ref state it
inherited from the previous run. Without any further information, the
skip notice can only point at the immediately preceding run, which
itself may have been a no-op skip. This makes it hard to find the
run that actually performed the last real rebase.

To fix this, append a "run <url>" line to the ref state recorded in
the check run output. The "Record ref state" step adds this line
when writing fresh state from a real rebase (i.e. when no "run" line
is present yet). When a subsequent skip inherits the state, the
"run" line passes through unchanged, always pointing back to the
original run that produced it. The pre-check notice then uses this
URL instead of the previous run's URL, giving a direct link to the
run that matters.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
The Azure Pipelines that previously maintained the shears branches
created check runs on git-for-windows/git's main branch, giving
maintainers a visible record of each rebase's outcome directly in
the target repository. Without an equivalent in the GitHub Actions
replacement, the only way to discover whether a rebase succeeded or
failed would be to navigate to the automation repository and find
the right workflow run — an unnecessary detour.

This creates a check run per branch (named `shears-seen`,
`shears-next`, etc.) on the `origin/main` commit, mirroring what
Azure Pipelines did. The check run's `details_url` links back to
the workflow run, and its summary contains the full step summary
report so that reviewers can inspect the range-diff and conflict
resolutions without leaving the git-for-windows/git commit page.

The installation access token is now obtained whenever `PUSH` is
true and the precheck did not skip, rather than only when there are
branches to push. This is necessary both for the check runs (which
must be created even when a rebase fails) and for the push step
(which should push successful branches even when other branches
failed in the same run).

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
The rebase automation needs to capture the full state of a failed
rebase — including unmerged index entries with conflict markers in
the worktree — so that it can be pushed to a builds repository for
inspection. Unfortunately, `git stash` cannot handle unmerged index
entries at all; it simply refuses to operate when there are
conflicts in the index. This is a known limitation that has never
been addressed.

These two scripts work around that limitation while staying as close
as possible to how `git stash` and `git stash apply` work:

`stash-with-conflicts.sh` creates a fake octopus merge commit with
exactly four parents: HEAD (the base, like the first parent of a
regular stash commit), plus one commit per index stage (1 = common
ancestor, 2 = ours, 3 = theirs). Each stage tree contains all
resolved (stage 0) files alongside the stage-specific blobs for
conflicted paths, built using temporary index files so that the real
index is never modified. The commit's own tree captures the worktree
as-is, conflict markers and all. After creating the commit, the
script runs `git reset --hard` just like `git stash` would.

`apply-stash-with-conflicts.sh` reverses the process: it verifies
the index is clean and that HEAD's tree matches the stash's base
tree, then reconstructs the unmerged index entries and overlays the
worktree with the stashed conflict markers, all without moving
HEAD — just like `git stash apply`. Unlike `git stash apply`, it
deliberately requires the current HEAD tree to match the stash's
base tree exactly, because this allows the implementation to simply
replay the recorded index and worktree state without any merging.

This limitation could be lifted in the future by using `git
merge-tree` to perform a virtual cherry-pick of the difference
between the stash's base and the current HEAD onto each of the four
parent trees (the worktree tree and the three stage trees). If all
four merge-tree operations succeed without conflicts, the adjusted
trees can be used in place of the originals. This is analogous to
how the original shell script version of `git stash apply` used
`merge-recursive` to apply the stashed changes onto a potentially
different HEAD.

The fixed four-parent layout means the unstash side does not need to
parse commit messages or guess which parent corresponds to which
stage; the positions are defined by convention.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
To give maintainers a durable, reviewable record of each rebase
outcome — including the exact commits produced and the full conflict
report — the workflow now creates pull requests in the dedicated
git-for-windows/shears-builds fork.

For each successfully rebased branch, a regular PR is opened whose
head ref is the rebase result and whose base ref is the marker
commit that anchors the merging-rebase. The PR description starts
with a link back to the originating workflow run, followed by the
full step summary (range-diff, conflict resolutions, build results).

For failed rebases, a draft PR is opened instead. Because `git
stash` cannot handle unmerged index entries, the failed worktree
state is captured using the new `stash-with-conflicts.sh` script
(introduced in the previous commit). The bundle and worktree
archive are uploaded as release assets on a pre-release tag pointing
at the PR's head commit. GitHub's API does not offer a way to
attach binary files directly to pull request comments or
descriptions, so a release is the natural alternative — release
assets have no retention limit (unlike workflow artifacts, which
expire after 90 days) and the pre-release tag on the PR's head
commit creates an implicit link back to the PR. A comment on the
draft PR links to those release assets so that the conflict state
can be inspected even after the ephemeral runner is gone.

The installation access token is now scoped to both `git` and
`shears-builds` so that the workflow can push refs and create PRs
and releases in the builds repository. Each step that calls `gh
api`, `push_to_builds`, or `gh release` has explicit error handling:
failures are reported as `::error::` annotations and tracked via an
`errors` counter, but processing continues for the remaining
branches so that a single failure does not prevent the other
branches from being mirrored.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
@dscho

dscho commented Feb 20, 2026

Copy link
Copy Markdown
Member Author

One thought.. you mentioned that now the cwd is not longer in the the workdir, and that also it's failing to use git -C $DIR because of permissions, we should update the prompts/agents instructions to avoid examples using -C.

E.g,

- See the patch: git -C \"$WORKTREE_DIR\" show REBASE_HEAD
+ See the patch: cd \"$WORKTREE_DIR\" && git show REBASE_HEAD

@mjcheetham your analysis is correct. I was mistaken to believe AI when it suggested that this method would require to pass --allow-tool(cd) (which would then allow all directories, I believed). That latter form was what AI had suggested in the first place... I changed it back.

Also, I fixed an infinite loop where git rebase --skip would finish the rebase, but fail to exit the loop and AI would try, and then retry ad infinitum, to resolve the same merge conflict.

With these fixes, I kicked off another run, which correctly resolved the merge conflict (I added a fixup??? to fix the CI builds, notified the patch author of the required adjustment, and the updated patch series was integrated into seen, so my fixup??? is now obsolete), and it successfully opened git-for-windows/shears-builds#3.

Range-diff
  • 1: 9c37f62 ! 1: 85b7bfa ci(rebase-branch): add upstream correspondence map for trivial skips

    @@ rebase-branch.sh: usage () {
     +		return
     +	fi
     +
    -+	while git rev-parse --verify REBASE_HEAD >/dev/null 2>&1; do
    ++	REBASE_MERGE_DIR=$(git rev-parse --git-path rebase-merge)
    ++	while test -d "$REBASE_MERGE_DIR"; do
    ++		git rev-parse --verify REBASE_HEAD >/dev/null 2>&1 ||
    ++			die "rebase metadata exists but REBASE_HEAD is missing"
     +		rebase_head_oid=$(git rev-parse REBASE_HEAD)
     +		rebase_head_oneline=$(git show --no-patch --format='%h %s' REBASE_HEAD)
     +
    @@ rebase-branch.sh: usage () {
     +		if corresponding_oid=$(find_correspondence "$rebase_head_oid" "$UPSTREAM_MAP") &&
     +		   test "$CORRESPONDENCE_TYPE" = "="; then
     +			echo "::notice::Trivial skip (upstream: $corresponding_oid): $rebase_head_oneline"
    -+			git rebase --skip ||:
    ++			if GIT_EDITOR=: git rebase --skip; then
    ++				break
    ++			fi
     +			continue
     +		fi
     +
  • 2: f9bb5c0 ! 2: cbd66ef ci(rebase-branch): add sibling correspondence map

    @@ rebase-branch.sh: run_rebase () {
     +			   git read-tree --reset -u "$result_tree" &&
     +			   git commit -C REBASE_HEAD; then
     +				echo "::notice::Used sibling resolution from: $corresponding_oid"
    -+				GIT_EDITOR=: git rebase --continue ||:
    ++				if GIT_EDITOR=: git rebase --continue; then
    ++					break
    ++				fi
     +				continue
     +			fi
     +		fi
  • 3: bdab7f4 = 3: 2b20be0 ci(rebase-shears): fetch all shears branches

  • 4: 311e718 ! 4: 4c72bb1 ci(rebase-branch): add previous correspondence map for adoption path

    @@ rebase-branch.sh: run_rebase () {
      			   git commit -C REBASE_HEAD; then
     -				echo "::notice::Used sibling resolution from: $corresponding_oid"
     +				echo "::notice::Used resolution from: $corresponding_oid"
    - 				GIT_EDITOR=: git rebase --continue ||:
    + 				if GIT_EDITOR=: git rebase --continue; then
    +-					break
    ++					break 2
    + 				fi
     -				continue
     +				continue 2
      			fi
  • 5: bb9b37d = 5: b0873f8 ci: add conflict-resolver agent instructions

  • 6: 349d151 ! 6: 4556c5e ci(rebase-branch): add minimal AI conflict resolution

    @@ Commit message
         content. Running from automation and exposing the worktree only via
         `--add-dir` keeps discovery and trust boundaries explicit.
     
    +    For that reason, Copilot must be told explicitly to run every command
    +    prefixed by `cd $WORKTREE_DIR` (and we need to pass that environment
    +    variable to Copilot, too).
    +
         Assisted-by: Claude Opus 4.6
         Signed-off-by: Johannes Schindelin <[email protected]>
     
    @@ rebase-branch.sh: run_rebase () {
      
     -		die "Conflict requires manual resolution: $rebase_head_oneline"
     +		# Non-trivial conflict — invoke AI
    -+		resolve_conflict_with_ai
    ++		if resolve_conflict_with_ai; then
    ++			break
    ++		fi
      	done
      }
      
    @@ rebase-branch.sh: run_rebase () {
     +# (not any .github/agents/ in the worktree, which is untrusted remote code).
     +run_copilot () {
     +	(cd "$SCRIPTS_DIR" &&
    ++	  export WORKTREE_DIR &&
     +	  copilot -p "$1" \
     +		--add-dir "$WORKTREE_DIR" \
     +		${COPILOT_MODEL:+--model "$COPILOT_MODEL"} \
    @@ rebase-branch.sh: run_rebase () {
     +IMPORTANT:
     +- The target repository/worktree is: $WORKTREE_DIR
     +- You are launched from a different directory only to load the custom agent.
    -+- Run every git command as: git -C \"$WORKTREE_DIR\" <command>
    ++- For each shell command, start with: cd \"$WORKTREE_DIR\" &&
     +- Read and edit files only inside: $WORKTREE_DIR
     +
     +Conflicting files: $conflicting_files
     +
     +Investigation commands:
    -+- See the patch: git -C \"$WORKTREE_DIR\" show REBASE_HEAD
    ++- See the patch: cd \"$WORKTREE_DIR\" && git show REBASE_HEAD
     +- See conflict markers: view \"$WORKTREE_DIR/<file>\"
    -+- Check if upstreamed: git -C \"$WORKTREE_DIR\" range-diff REBASE_HEAD^! REBASE_HEAD..
    ++- Check if upstreamed: cd \"$WORKTREE_DIR\" && git range-diff REBASE_HEAD^! REBASE_HEAD..
     +
     +Decision rules:
     +1. If range-diff shows correspondence (e.g. '1: abc = 1: def'), output: skip <upstream-oid>
    -+2. If patch needs surgical resolution, edit files, stage with 'git -C \"$WORKTREE_DIR\" add', output: continue
    ++2. If patch needs surgical resolution, edit files, stage with 'cd \"$WORKTREE_DIR\" && git add', output: continue
     +3. If unresolvable, output: fail
     +
     +Your FINAL line must be exactly: skip <oid>, continue, or fail"
    @@ rebase-branch.sh: run_rebase () {
     +		else
     +			echo "::notice::Skipping commit (obsolete): $rebase_head_oneline"
     +		fi
    -+		git rebase --skip ||:
    ++		if GIT_EDITOR=: git rebase --skip; then
    ++			return 0
    ++		fi
    ++		return 1
     +		;;
     +	continue)
     +		echo "::notice::Resolved conflict surgically: $rebase_head_oneline"
    -+		GIT_EDITOR=: git rebase --continue ||:
    ++		if GIT_EDITOR=: git rebase --continue; then
    ++			return 0
    ++		fi
    ++		return 1
     +		;;
     +	fail)
     +		echo "::error::AI could not resolve conflict: $rebase_head_oneline"
  • 7: 8930766 ! 7: b222809 ci(rebase-branch): add log-L investigation commands to AI prompt

    @@ rebase-branch.sh: run_rebase () {
     +			count=${count:-1}
     +			end=$((start + count - 1))
     +			if test "$end" -lt "$start"; then end=$start; fi
    -+			echo "git -C \"$WORKTREE_DIR\" log -L $start,$end:$1 REBASE_HEAD..HEAD"
    ++			echo "cd \"$WORKTREE_DIR\" && git log -L $start,$end:$1 REBASE_HEAD..HEAD"
     +		fi
     +	done
     +}
    @@ rebase-branch.sh: resolve_conflict_with_ai () {
      
      IMPORTANT:
     @@ rebase-branch.sh: Investigation commands:
    - - See the patch: git -C \"$WORKTREE_DIR\" show REBASE_HEAD
    + - See the patch: cd \"$WORKTREE_DIR\" && git show REBASE_HEAD
      - See conflict markers: view \"$WORKTREE_DIR/<file>\"
    - - Check if upstreamed: git -C \"$WORKTREE_DIR\" range-diff REBASE_HEAD^! REBASE_HEAD..
    + - Check if upstreamed: cd \"$WORKTREE_DIR\" && git range-diff REBASE_HEAD^! REBASE_HEAD..
     -
    -+- Try higher creation factor: git -C \"$WORKTREE_DIR\" range-diff --creation-factor=200 REBASE_HEAD^! REBASE_HEAD..
    ++- Try higher creation factor: cd \"$WORKTREE_DIR\" && git range-diff --creation-factor=200 REBASE_HEAD^! REBASE_HEAD..
     +- See upstream changes to conflicting lines:
     +${log_l_commands}
      Decision rules:
      1. If range-diff shows correspondence (e.g. '1: abc = 1: def'), output: skip <upstream-oid>
    - 2. If patch needs surgical resolution, edit files, stage with 'git -C \"$WORKTREE_DIR\" add', output: continue
    + 2. If patch needs surgical resolution, edit files, stage with 'cd \"$WORKTREE_DIR\" && git add', output: continue
  • 8: 7a49aa8 ! 8: 0723430 ci(rebase-branch): pass tried correspondences to AI prompt

    @@ rebase-branch.sh: run_rebase () {
      		done
      
      		# Non-trivial conflict — invoke AI
    --		resolve_conflict_with_ai
    -+		resolve_conflict_with_ai "$tried_correspondences"
    +-		if resolve_conflict_with_ai; then
    ++		if resolve_conflict_with_ai "$tried_correspondences"; then
    + 			break
    + 		fi
      	done
    - }
    - 
     @@ rebase-branch.sh: run_copilot () {
      }
      
    @@ rebase-branch.sh: resolve_conflict_with_ai () {
     +		correspondence_context="
     +Note: We found corresponding commits from previous/sibling rebases but they did not apply cleanly:
     +$tried_correspondences
    -+You may want to examine these with 'git -C \"$WORKTREE_DIR\" show <oid>' for hints on how to resolve."
    ++You may want to examine these with 'cd \"$WORKTREE_DIR\" && git show <oid>' for hints on how to resolve."
     +	fi
     +
      	prompt="Resolve merge conflict during rebase of commit REBASE_HEAD.
    @@ rebase-branch.sh: IMPORTANT:
     -
     +$correspondence_context
      Investigation commands:
    - - See the patch: git -C \"$WORKTREE_DIR\" show REBASE_HEAD
    + - See the patch: cd \"$WORKTREE_DIR\" && git show REBASE_HEAD
      - See conflict markers: view \"$WORKTREE_DIR/<file>\"
  • 9: 44bab2c ! 9: 4b7d531 ci(rebase-branch): verify build after AI conflict resolution

    @@ rebase-branch.sh: Your FINAL line must be exactly: skip <oid>, continue, or fail
     +
     +IMPORTANT:
     +- The target repository/worktree is: $WORKTREE_DIR
    -+- Run every git command as: git -C \"$WORKTREE_DIR\" <command>
    ++- For each shell command, start with: cd \"$WORKTREE_DIR\" &&
     +- Read and edit files only inside: $WORKTREE_DIR
     +
     +Files with conflicts: $(git diff --name-only --diff-filter=U)
     +
     +Investigation:
     +- See full build log: view \"$WORKTREE_DIR/make.log\"
    -+- See your changes: git -C \"$WORKTREE_DIR\" diff
    -+- Edit files to fix, then: git -C \"$WORKTREE_DIR\" add <file>
    ++- See your changes: cd \"$WORKTREE_DIR\" && git diff
    ++- Edit files to fix, then: cd \"$WORKTREE_DIR\" && git add <file>
     +
     +Build errors (last 15 lines):
     +$(tail -15 make.log)
    @@ rebase-branch.sh: Your FINAL line must be exactly: skip <oid>, continue, or fail
     +			echo "::endgroup::"
     +		fi
     +		rm -f make.log
    - 		GIT_EDITOR=: git rebase --continue ||:
    - 		;;
    - 	fail)
    + 		if GIT_EDITOR=: git rebase --continue; then
    + 			return 0
    + 		fi
  • 10: cd27a69 = 10: e4b8d11 ci(rebase-branch): robust decision parsing for Copilot output

  • 11: b0e1f37 ! 11: 1ce8beb ci(rebase-branch): add detailed conflict reporting and statistics

    @@ Commit message
     
      ## rebase-branch.sh ##
     @@ rebase-branch.sh: run_rebase () {
    - 	while git rev-parse --verify REBASE_HEAD >/dev/null 2>&1; do
    + 			die "rebase metadata exists but REBASE_HEAD is missing"
      		rebase_head_oid=$(git rev-parse REBASE_HEAD)
      		rebase_head_oneline=$(git show --no-patch --format='%h %s' REBASE_HEAD)
     +		rebase_head_ref=$(git show --no-patch --format=reference REBASE_HEAD)
    @@ rebase-branch.sh: run_rebase () {
     +
     +			SKIP_EOF
     +			CONFLICTS_SKIPPED=$((CONFLICTS_SKIPPED + 1))
    - 			git rebase --skip ||:
    - 			continue
    - 		fi
    + 			if GIT_EDITOR=: git rebase --skip; then
    + 				break
    + 			fi
     @@ rebase-branch.sh: run_rebase () {
      			   git read-tree --reset -u "$result_tree" &&
      			   git commit -C REBASE_HEAD; then
    @@ rebase-branch.sh: run_rebase () {
     +
     +				CORR_EOF
     +				CONFLICTS_RESOLVED=$((CONFLICTS_RESOLVED + 1))
    - 				GIT_EDITOR=: git rebase --continue ||:
    - 				continue 2
    - 			fi
    + 				if GIT_EDITOR=: git rebase --continue; then
    + 					break 2
    + 				fi
     @@ rebase-branch.sh: Your FINAL line must be exactly: skip <oid>, continue, or fail"
      	skip)
      		upstream_oid=$(echo "$decision" | awk '{print $2}')
    @@ rebase-branch.sh: Your FINAL line must be exactly: skip <oid>, continue, or fail
      			echo "::notice::Skipping commit (obsolete): $rebase_head_oneline"
      		fi
     +		CONFLICTS_SKIPPED=$((CONFLICTS_SKIPPED + 1))
    - 		git rebase --skip ||:
    + 		if GIT_EDITOR=: git rebase --skip; then
    + 			return 0
    + 		fi
    +@@ rebase-branch.sh: Your FINAL line must be exactly: skip <oid>, continue, or fail"
      		;;
      	continue)
      		echo "::notice::Resolved conflict surgically: $rebase_head_oneline"
  • 12: 44a21a5 ! 12: 2af8021 ci(rebase-branch): support skipping obsolete patches with a reason

    @@ rebase-branch.sh: Investigation commands:
      ${log_l_commands}
      Decision rules:
      1. If range-diff shows correspondence (e.g. '1: abc = 1: def'), output: skip <upstream-oid>
    --2. If patch needs surgical resolution, edit files, stage with 'git -C \"$WORKTREE_DIR\" add', output: continue
    +-2. If patch needs surgical resolution, edit files, stage with 'cd \"$WORKTREE_DIR\" && git add', output: continue
     -3. If unresolvable, output: fail
     +2. If the patch is obsolete (e.g. fixes code removed upstream), output: skip -- <reason>
    -+3. If patch needs surgical resolution, edit files, stage with 'git -C \"$WORKTREE_DIR\" add', output: continue
    ++3. If patch needs surgical resolution, edit files, stage with 'cd \"$WORKTREE_DIR\" && git add', output: continue
     +4. If unresolvable, output: fail
      
     -Your FINAL line must be exactly: skip <oid>, continue, or fail"
  • 13: ec9c27e ! 13: f7ac707 ci(rebase-branch): report AI conflict resolutions in step summary

    @@ rebase-branch.sh: ${log_l_commands}
      Decision rules:
      1. If range-diff shows correspondence (e.g. '1: abc = 1: def'), output: skip <upstream-oid>
      2. If the patch is obsolete (e.g. fixes code removed upstream), output: skip -- <reason>
    --3. If patch needs surgical resolution, edit files, stage with 'git -C \"$WORKTREE_DIR\" add', output: continue
    -+3. If patch needs surgical resolution, edit files, stage with 'git -C \"$WORKTREE_DIR\" add', output: continue -- <brief summary of what you changed>
    +-3. If patch needs surgical resolution, edit files, stage with 'cd \"$WORKTREE_DIR\" && git add', output: continue
    ++3. If patch needs surgical resolution, edit files, stage with 'cd \"$WORKTREE_DIR\" && git add', output: continue -- <brief summary of what you changed>
      4. If unresolvable, output: fail
      
     -Your FINAL line must be exactly: skip <oid>, skip -- <reason>, continue, or fail"
    @@ rebase-branch.sh: Your FINAL line must be exactly: skip <oid>, skip -- <reason>,
      		/^skip -- /b found
      		/^skip$/b found
     @@ rebase-branch.sh: Your FINAL line must be exactly: skip <oid>, skip -- <reason>, continue, or fail
    - 		git rebase --skip ||:
    + 		return 1
      		;;
      	continue)
     +		resolution_summary=$(echo "$decision" | sed -n 's/^continue -- //p')
    @@ rebase-branch.sh: Your FINAL line must be exactly: continue or fail"
     +
     +			CONTINUE_EOF
     +		fi
    - 		GIT_EDITOR=: git rebase --continue ||:
    - 		;;
    - 	fail)
    + 		if GIT_EDITOR=: git rebase --continue; then
    + 			return 0
    + 		fi
  • 14: 5b053e2 = 14: 6664aac ci(rebase-shears): support rebasing all branches at once

  • 15: 12c541a = 15: e2f68f8 ci(rebase-shears): add push step with app token

  • 16: c45e8a7 = 16: 1afd7d3 ci(rebase-shears): run on schedule with ref-change detection

  • 17: f7d6759 = 17: 913fff8 ci(rebase-shears): track which run originally observed the ref state

  • 18: 4ee947d = 18: 293af1b ci(rebase-shears): create per-branch check runs on git-for-windows/git

  • 19: dd545cf = 19: 188b064 Add stash-with-conflicts.sh and apply-stash-with-conflicts.sh

  • 20: 0ba0804 = 20: 9c61c34 ci(rebase-shears): mirror rebase outcomes to PRs in shears-builds

@dscho
dscho merged commit 66731b5 into main Feb 22, 2026
@dscho
dscho deleted the rebase-shears branch February 22, 2026 16:37
@dscho

dscho commented Feb 27, 2026

Copy link
Copy Markdown
Member Author

Here is a nice win: git-for-windows/shears-builds#12. Essentially, the merge conflict between git-for-windows/git@f1824ba and git/git@d74aacd#diff-5709c9f0cfda9fa887afadc58746bc95fd1946ff9878e0552d3e7073f9484f76R119-R122 was resolved as git-for-windows/shears-builds@890df9c.

Personally, I would have moved the reftable_set_alloc(malloc, realloc, free); call before the new refs_compute_filesystem_location() call, but as the succeeding CI build proves, it is not strictly necessary.

@dscho

dscho commented Mar 4, 2026

Copy link
Copy Markdown
Member Author

And here's another non-trivial merge conflict successfully resolved: git-for-windows/shears-builds#19

@dscho

dscho commented Mar 5, 2026

Copy link
Copy Markdown
Member Author

I promise to stop adding these items soon, but for now I plan on adding these as a way to determine how much this automation took off of my shoulders. As a general rule, each of these merge conflicts would require between 10-30 minutes to investigate, I would guess.

pull Bot pushed a commit to frikke/git-for-windows-automation that referenced this pull request Apr 30, 2026
While reviewing the MSYS2 sync automation in PR git-for-windows#152, we discovered
that multi-line content inside `run: |` and `script: |` blocks
silently gets truncated when any line falls below the block's base
indentation level. This is particularly treacherous for heredoc bodies
and JS template literals whose content naturally starts at column 0.

Document the pitfall and the two mitigations that worked: indenting
heredoc bodies to match the YAML block's base indentation, and using
`[...].join('\n')` for multi-line JS strings.

See git-for-windows#152 (comment)
for the context that prompted this.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants