2

I have a config file I have excluded locally. I did not use .gitignore because other team members update the file, but my local changes only apply to my machine. I used this question for reference on how to exclude the file: How do I configure git to ignore some files locally?

A team member has updated the file, and I'm now unable to do a git pull. I get the error:

error: Your local changes to the following files would be overwritten by merge:
    config/td_config.php
Please commit your changes or stash them before you merge.

But my local shows no changes to td_config.php because it is excluded. I cannot commit or stash it.

My questions are:

  1. Why does the remote see local changes to a file I've excluded when my expectation is that the remote will not be aware of changes to an excluded file?

  2. How do I get around this problem?

I tried git update-index --no-assume-unchanged config/td_config.php to no avail.

ChrisGPT was on strike
  • 127,765
  • 105
  • 273
  • 257
Tony Chapman
  • 88
  • 1
  • 9
  • Looks like I need additional clarification on this: It's been a while since I set up the local ignore so I don't recall the exact process I used. But I do currently have that file in my .git/info/exclude. You're right that I'm unclear on the differences between assume-unchanged, skip-worktree, and the exclude file, but it still looks to me like the conflict I'm seeing isn't the behavior I expect from having something excluded. I'm using Tower as a gui for this, partially so I can avoid commands I don't understand. – Tony Chapman Apr 09 '19 at 20:40
  • 1
    `.git/info/exclude` can't be used to ignore changes to tracked files, either. It's exactly like `.gitignore`, except it isn't tracked or shared. It's for local ignores, but just like `.gitignore` it only prevents files from being tracked in the first place, having no effect on tracked files. – ChrisGPT was on strike Apr 09 '19 at 20:46

2 Answers2

3

Why does the remote see local changes to a file I've excluded when my expectation is that the remote will not be aware of changes to an excluded file?

Because that's not what assume-unchanged does; in fact, it's for the very opposite purpose (bold added):

Assume-unchanged should not be abused for an ignore mechanism. It is "I know my filesystem operations are slow. I'll promise Git that I won't change these paths by making them with that bit---that way, Git does not have to check if I changed things in there every time I ask for 'git status' output". It does not mean anything other than that. Especially, it is not a promise by Git that Git will always consider these paths are unmodified---if Git can determine a path that is marked as assume-unchanged has changed without incurring extra lstat(2) cost, it reserves the right to report that the path has been modified (as a result, "git commit -a" is free to commit that change).

Yes, there are lots of resources recommending assume-unchanged to ignore local changes to a tracked file. They're almost all wrong, and the issue you're asking about is one of the ways that this can fail.

Unfortunately, assume-unchanged often looks like it's ignoring changes to files so people continue to use it and to recommend it.

How do I get around this problem?

skip-worktree is a better option, but it's not a perfect fit either. A much better solution is to coordinate with your team to rename the tracked file to td_config.template.php or similar and properly ignore td_config.php.

Don't fight Git; use it the way it's meant to be used. It doesn't provide a mechanism for ignoring a tracked file, and all of the current workarounds are flawed in one way or another.

(If you really want to ignore this advice, you should be able to git update-index --no-assume-unchanged the file, then stash your changes, then pull, then apply your changes, then re-apply the bit. But do so knowing that it doesn't do what you want it to.)

Edit:

OP commented that

When I originally set this up, I tried update-index, skip-worktree, and the exclude file… so at this point it's pretty hard to understand how exactly git is or isn't tracking the file.

Running git ls-files -v config/td_config.php helps us figure out which of these settings is active on that file. In this case we got

S config/td_config.php

The capital S shows us that skip-worktree is set, and that assume-unchanged is unset on the file. Running git update-index --no-skip-worktree config/td_config.php should return it to "regular status", at which point any of the preceding suggestions can be used.

ChrisGPT was on strike
  • 127,765
  • 105
  • 273
  • 257
  • As my question mentions, I tried the update-index --no-assume-unchanged command but I am still unable to pull. When I originally set this up, I tried update-index, skip-worktree, and the exclude file (with little understanding of what they actually did, clearly), so at this point it's pretty hard to understand how exactly git is or isn't tracking the file. Pretty sure my team doesn't want to take the time to rework our workflow, so I have to find a workaround myself. – Tony Chapman Apr 09 '19 at 21:12
  • @TonyChapman, yes, I saw that. Based on what you've shared so far I'm not sure what the issue is; I had hoped I could convince you to use a more appropriate workflow. I still don't recommend abusing `assume-unchanged` or `skip-worktree` this way, and I really think you should propose this change to your team. In any case, I'd like to help you solve your immediate issue if possible. What does `git ls-files -v config/td_config.php` give? – ChrisGPT was on strike Apr 09 '19 at 21:23
  • Thanks, I really appreciate that. I've learned so far that git has no way to natively do _exactly_ what I want, which is to pull changes from the remote but ignore changes I make locally. I can raise the workflow issue with the team at a later point (as this issue affects other files I work with also) but need to move past it now. My local changes are trivial, so I can easily delete the file, pull and reproduce my configuration. Not sure what is best after that. The output you requested is: ```S config/td_config.php``` – Tony Chapman Apr 09 '19 at 21:38
  • 1
    Okay, that capital `S` means that [`skip-worktree` is set](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt-S) and [`assume-unchanged` is not](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt--v). Try running `git update-index --no-skip-worktree config/td_config.php`, then `git stash save "Stash local config changes"`, pull your changes, then `git stash pop`. Then, if you must, `git update-index --skip-worktree config/td_config.php` to mark it with the `skip-worktree` bit again. – ChrisGPT was on strike Apr 09 '19 at 21:42
  • That did it. Thanks! – Tony Chapman Apr 09 '19 at 21:47
  • 1
    Glad to help. I'll update my answer with the `ls-files -v` bit for completeness. – ChrisGPT was on strike Apr 09 '19 at 21:48
  • 1
    Incidentally, I wrote a program, `git-flagged`, to identify files in the index with either or both of skip-worktree or assume-unchanged set. It's in my general purpose scripts repository at https://github.com/chris3torek/scripts/blob/master/git-flagged (it also has an option to clear such flags). – torek Apr 09 '19 at 21:58
0

Stash the file then pull
git stash
git pull
git stash pop

EncryptedWatermelon
  • 4,788
  • 1
  • 12
  • 28