Skip to content

fix(git): pin autocrlf=false on the uncached fallback clone and checkout - #3409

Open
Abhishek Acharya (abhishekacharya200) wants to merge 2 commits into
microsoft:mainfrom
abhishekacharya200:fix/fallback-clone-autocrlf
Open

Abhishek Acharya (abhishekacharya200) wants to merge 2 commits into
microsoft:mainfrom
abhishekacharya200:fix/fallback-clone-autocrlf

Conversation

@abhishekacharya200

Copy link
Copy Markdown
Contributor

Fixes #3368.

Problem

The uncached fallback materializes a full working-tree clone and then checks out the locked SHA. The clone inherits the host's effective git config frozen into GIT_CONFIG_KEY_n by git_clone_env, so on a host with core.autocrlf=true it writes CRLF bytes for LF-committed files. The checkout runs under the downloader's isolated env (GIT_CONFIG_NOSYSTEM=1, empty GIT_CONFIG_GLOBAL), which has no such snapshot, compares that CRLF worktree against the LF index, and refuses:

error: Your local changes to the following files would be overwritten by checkout:
        f.txt

apm install then fails with Failed to checkout commit <sha> — but only once the locked SHA falls behind the branch head, which is why it looks intermittent. #2982 pinned core.autocrlf=false on the GitCache and bare-cache paths; the uncached fallback never got the pin.

Fix

Added git_autocrlf_pin_args() and applied it to both clone_git_worktree and checkout_git_worktree, so the working tree the fallback writes and the checkout that follows agree on line-ending handling and the materialized bytes are the committed LF bytes regardless of host config. -c outranks the frozen GIT_CONFIG_KEY_n snapshots, which is the same precedence argument #2971 relies on. Both call sites of checkout_git_worktree are the fallback commit path, so the blast radius is that path.

Tests

TestUncachedFallbackLineEndings:

  • test_clone_and_checkout_pin_autocrlf_false — asserts the pin is present, adjacent to -c, and precedes clone / checkout.
  • test_locked_sha_checks_out_from_a_crlf_host — builds a two-commit repo, sets a host gitconfig with core.autocrlf=true for the clone snapshot, clones, then checks out the older SHA with the isolated env.

Red/green on this Windows host: without the change the clone writes a\r\nb\r\nc\r\n and the checkout raises the exact error above; with it the clone writes a\nb\nc\n and the checkout lands on the locked SHA.

Verification

  • tests/unit/cache/test_git_env.py: 103 passed, 2 failed — the two test_real_lookup_rejects_symlink_into_project cases, which fail identically on main here (no Windows symlink privilege).
  • tests/unit/cache tests/unit/deps/test_clone_connect_retry.py tests/integration/test_git_hook_env_isolation.py: 10 failures, byte-for-byte the same set as main in this environment (symlink privilege and Windows case-sensitivity), with the two new tests passing.
  • ruff check and ruff format --check clean on both touched files.

The fallback working-tree clone freezes the host's effective git config
into GIT_CONFIG_KEY_n, so a host with core.autocrlf=true wrote CRLF bytes
while the follow-up `git checkout <locked sha>` ran without that snapshot
and saw every text file as modified, failing the install after the
producer gained a commit. Pin core.autocrlf=false on both commands, as
the GitCache and bare-cache paths already do (microsoft#2982), so the materialized
bytes are the committed bytes and the two commands agree.

Closes microsoft#3368

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The fixture commits retain CRLF bytes on Windows when the host’s core.autocrlf is false or unset, causing the LF assertions and Windows compatibility gate to fail.

1 open issue
What changed in this PR

Fixes #3368 by making the uncached Git fallback clone and checkout use consistent line-ending settings, preventing locked-commit installs from failing on hosts with core.autocrlf=true.

Changes:

  • Adds a shared core.autocrlf=false argument helper to both working-tree operations.
  • Adds argument checks and a real-Git regression test for checking out an older commit.
  • Documents the fallback fix in the changelog.
File Description
tests/​unit/​cache/​test_git_env.py Adds pin assertions and a CRLF-host regression scenario.
src/​apm_cli/​utils/​git_env.py Pins autocrlf off for working-tree clone and checkout.
CHANGELOG.md Adds an Unreleased entry describing the install fix.

Review effort: Balanced


def _commit(message: str) -> None:
subprocess.run(
[git, "-C", str(src), "-c", "user.name=t", "-c", "user.email=t@t", "add", "f.txt"],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 8c332e5, slightly differently from the suggestion. The fixture now writes LF bytes with write_bytes, so the commits hold LF whatever the host's autocrlf setting is, without pinning anything on git add. I could not run this on Windows, so the Windows gate is the real check.

write_text produces CRLF on Windows, and with core.autocrlf off those
bytes would reach the fixture commits and fail the LF assertions.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants