Git switch branches with changed --skip-worktree file

933 views Asked by At

I have an file abc.log that is (for some reason) tracked in git, but that I (perhaps foolishly) set as --skip-worktree so changes to it would not show up in my commits to the repository. I would like to change branches:

~ % git checkout master                                                
error: Your local changes to the following files would be overwritten by checkout:
    abc.log
Please commit your changes or stash them before you switch branches.
Aborting

I then try to refresh the index and force-checkout:

~ % git update-index --really-refresh 
~ % git checkout master -f
error: Entry 'abc.log' not uptodate. Cannot merge.

However, abc.log does not have any changes on local:

~ % git status            
On branch xyz
Your branch is ahead of 'origin/xyz' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

Following Undo git update-index --assume-unchanged <file>, I try to set it as no-skip-worktree again.

~ % git update-index --no-skip-worktree --no-assume-unchanged abc.log
fatal: Unable to mark file dump.rbd

Following the advice of something like https://stackoverflow.com/a/42363882 I see that it is tracked by git, and currently set to skip worktree

~ % git ls-files -v | grep "S "                                        
S abc.log

However, simply looking for it doesn't show it

~ % git ls-files --error-unmatch abc.log                           
error: pathspec 'abc.log' did not match any file(s) known to git
Did you forget to 'git add'?

And I am unable to add it as follows

~ % git add dump.rbd
fatal: pathspec 'abc.log' did not match any files
2

There are 2 answers

0
torek On BEST ANSWER

bk2204's answer is the right one for this case, but more generally, it is possible to carry around some uncommitted change using --skip-worktree for a while. When you run into this problem, the trick to use is:

  1. Clear the --skip-worktree flag (with git update-index --no-skip-worktree path.)
  2. Add and commit the change, as a change to just the one file (or set of files). Give this a good commit message, so that you can find this commit easily, including after it gets rebased.
  3. Obtain the upstream commits.
  4. Rebase or merge as appropriate.
  5. Use git rebase -i to "move" the commit made in step 2 to be a separate, final commit on your branch. Make sure everything still works and you still want to carry this around as an uncommitted change with --skip-worktree set, i.e., that you don't want to have committed changes to this file (or these files, plural). If you do want to have committed changes, make these changes, commit, and repeat as needed so that the "extra" change that you want to carry around is still the final commit.
  6. Use git reset (with the default --mixed) to remove the final commit, that has this change that you want to carry around as an uncommitted change.
  7. Use git update-index to set the --skip-worktree flag again.

This simple 7-step process lets you easily carry your change forward. If multiple files are affected, you may wish to write a small tool to let you add and clear the --skip-worktree flag appropriately. (I have one that lets you view and remove the flags easily, but not one that lets you add them back—that's just a matter of typing in for i in <list>; do git update-index --skip-worktree $i; done anyway and I didn't want to go that far in terms of automation when I was doing this.)

0
bk2204 On

You've discovered the reason that the Git FAQ states there's no way to ignore changes to tracked files. From the FAQ:

Git doesn’t provide a way to do this. The reason is that if Git needs to overwrite this file, such as during a checkout, it doesn’t know whether the changes to the file are precious and should be kept, or whether they are irrelevant and can safely be destroyed. Therefore, it has to take the safe route and always preserve them.

It’s tempting to try to use certain features of git update-index, namely the assume-unchanged and skip-worktree bits, but these don’t work properly for this purpose and shouldn’t be used this way.

This just isn't going to work at all, and there's no way to make it work. You just have to not do that. Since that file looks like a log file, it probably should not be checked in at all, which will solve your problems. You can remove it with git rm.