deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Moving a folder broke .gitignore anchoring and nearly committed live credentials

A developer's folder restructure silently stopped .gitignore patterns from matching a file holding live Supabase credentials. The cause is a little-known slash rule in gitignore pattern anchoring.

Moving a folder broke .gitignore anchoring and nearly committed live credentials

What happened

A developer writing on dev.to has documented a near miss in which a routine repository reorganisation silently stopped two .gitignore patterns from matching — including one covering a file with live credentials.

The repository contained a Swift app spread across several top-level directories alongside assorted scripts. When an Android app was added to the codebase, the author used git mv to gather the Swift half into a new apple/ directory so that apple/ and android/ sat side by side. Nothing in the files themselves changed, both platforms still built, and every check passed.

The trouble appeared only when the author inspected what git was proposing to commit. Two paths that had been ignored up to that point showed up as untracked: apple/Support/pooled-flight/ and apple/Support/telemetry.env — the latter holding a live Supabase URL and key. According to the post, a single git add -A run without looking would have sent those credentials to a remote.

One slash decides everything

The repo's .gitignore listed five patterns: build/, *.xcodeproj, dist/, Support/telemetry.env and Support/pooled-flight/. Three of them survived the folder move and two did not, and the difference comes down to a slash.

As the post summarises the gitignore specification: when a pattern contains a slash anywhere other than at the end, it is interpreted relative to the directory holding the .gitignore file. Patterns without an interior slash match by name at any depth.

So build/ kept matching apple/build/, and *.xcodeproj kept matching anywhere in the tree. But Support/telemetry.env contained an interior slash and was therefore anchored to the repository root, meaning exactly Support/telemetry.env at the top level. The moment the file became apple/Support/telemetry.env, the pattern no longer described it, and git started offering a secrets file as an ordinary untracked file.

The author stresses that this behaviour is documented and correct — and completely quiet. Git never warns that a pattern has stopped matching, because a pattern matching nothing is a normal state most of the time. Notably, neither the file nor the .gitignore had been edited; the tree simply grew a directory above them both.

Verifying instead of assuming

Two commands did the forensic work. git check-ignore -v reports the exact pattern and line number responsible for ignoring a path — and when it prints nothing at all, the file is not ignored. The author flags that asymmetry as worth internalising: the dangerous answer is the silent one.

The second question was whether the credentials had ever been committed on any branch at any point. Running git log --all --oneline -- '*telemetry.env' returned an empty result, confirming a near miss rather than an incident. Had it not been empty, the author's stated order of operations is fixed: rotate the credential first, then worry about rewriting history — in that order, never reversed.

The fix, and the better fix

The obvious repair is to re-point the patterns to apple/Support/telemetry.env and apple/Support/pooled-flight/. That works today and breaks the next time anything moves, which the author describes as resetting the same trap.

The better repair is to remove the anchoring altogether, switching to telemetry.env and pooled-flight/. With no interior slash, these match by name at any depth, wherever the tree ends up next. The trade-off is slightly broader matching — any file named telemetry.env anywhere is now ignored — which, for a secret, the author argues is the correct direction to be imprecise in.

Why it matters

Ignore patterns containing interior slashes are coupled to your directory structure. For build output that coupling is mostly harmless: if dist/ stops being ignored, you find out because something downstream misbehaves. For secrets it is silent — the file becomes committable at the exact moment you are busy thinking about an unrelated refactor.

The post's practical takeaways apply broadly. Prefer name-only patterns for anything sensitive: telemetry.env rather than config/telemetry.env, *.p12 rather than keys/*.p12, trading a little precision for immunity to future restructuring. After any structural change, scan git status specifically for untracked files that appear from nowhere — files you actually moved show up as renames and are safe; anything newly untracked after a move was previously covered by an anchored pattern. And run git check-ignore -v against your secrets, because it is the only command that gives a straight answer, and the dangerous result is the one that prints nothing at all.

  • #git
  • #gitignore
  • #version-control
  • #security
  • #developer-tools

Related posts