11

I have been caught out by what appears to be a common enough problem for git newbies.

I forgot to .gitignore a particular file, and adding it to .gitignore after having committed makes no difference.

I found this page on gitready which explains how to remove a file from the repository without removing it from the working tree (by using the command git rm --cached <file>, which works ok, except that if I then try to merge that back into another branch, the files in the working tree get deleted.

Steps to reproduce, on an empty folder:

git init
touch want.txt
touch wantnot.txt
git add .
git commit -m "Initial"
git checkout -b BranchForIgnore
git rm --cached wantnot.txt
cat > .gitignore
wantnot.txt  [Ctrl-D Ctrl-D here to exit cat]
git add .
git commit -m "Ignoring file"

Up to here, everything is fine

git checkout master
git merge BranchForIgnore

At this point, my wantnot.txt files are no longer in my master, and obviously, checking out BranchForIgnore won't help either.

What to do?

Benjol
  • 63,995
  • 54
  • 186
  • 268

2 Answers2

8

I forgot to .gitignore a particular file, and adding it to .gitignore after having committed makes no difference.

Well, of course it doesn't. Ignoring is about untracked files (which means files not under version control).

I found a page on gitready which explains how to remove a file from the repository without removing it from the working tree (by using the command git rm --cached <file>, which works ok, except that if I then try to merge that back into another branch, the files in the working tree get deleted.

Git deletes file because you can recover it, as it was tracked in one branch.

The solution would be to commit "untracking" a file (removing file from version control) in all branches, using git cherry-pick, or making git rm --cached <file> && git commit -a on a separate commit branch, then merging this topic branch in all branches (and then recovering file from a tracked version).

Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
  • Thanks for this. Given that files with the same name exist in many locations, how would I do 'recovering file from a tracked version'? – Benjol Dec 15 '09 at 08:33
  • 1
    When you switch to a branch that lack given file, you can recover a file using e.g. `git checkout HEAD^ -- `, or in more generic situation use `git log -2 --full-history -- ` to get the commit which last had the file and then recover file using `git checkout -- ` or `git show : > `. – Jakub Narębski Dec 15 '09 at 15:35
  • Wow, that's some serious git-fu! – Benjol Dec 16 '09 at 06:58
  • The git-fu is required only if you need to find a commit which last had the file. The core is `git checkout -- `. – Jakub Narębski Dec 16 '09 at 10:29
  • Apparently this only works if I type in the exact location of the file(s), I was hoping for something that would work with wildcards (the way the rm did) – Benjol Dec 17 '09 at 10:16
  • It doesn't work? Doesn't e.g. `git checkout -- '*.xml'` work (don't forget '--' separator, don't forget using quotes to protect from shell expansion; also if I remember correctly paths are from top tree)? It might not work, because not all commands support wildcards. – Jakub Narębski Dec 17 '09 at 11:47
  • In the end, I went the back root - copied my entire working tree to another folder, deleted all the files by hand, modified gitignore, committed, then pasted working tree back... Did it on master. – Benjol Mar 08 '10 at 10:07
6

Rename the file to a temporary name (not using git), commit the deletion plus the .gitignore file, then rename the file back to its original name.

mv wantnot.txt wantnot.txt.tmp
git rm wantnot.txt
echo wantnot.txt >.gitignore
git add .gitignore
git commit -m "remove mistakenly committed wantnot.txt"
mv wantnot.txt.tmp wantnot.txt

Your use of a separate branch for this may be unnecessarily confusing the issue.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • Thanks. Actually my problem is that I have already created several branches for other reasons, and I didn't want to have to perform this operation in all my branches (and even if I did, I wasn't too clear on what would happen when I start merging those changes). – Benjol Dec 11 '09 at 10:16
  • I see. You may need to perform a similar operation on all your branches (a `git cherry-pick` may be useful for this). As you move between branches that track and don't track `wantnot.txt`, it may appear and disappear until you have everything sorted for all branches. Make sure you save a copy if this file is important. – Greg Hewgill Dec 11 '09 at 10:19
  • Oh, and the actual file is AssemblyInfo.cs, so one in every project in my working tree, not sure if mv works for that (I'm not a unix command line expert, but amazingly git is seducing me) – Benjol Dec 11 '09 at 10:19
  • 1
    At the worse, there's always filter-branch, this is a standalone, one developer repository, after all. – Benjol Dec 11 '09 at 10:20
  • `mv` renames any file, even C# source files. :) – Greg Hewgill Dec 11 '09 at 10:20
  • Oh really?! :) No, I meant whether it could rename recursively, my google-foo says no. – Benjol Dec 11 '09 at 10:28
  • This worked really easily. – Caleb Jan 08 '23 at 08:11