Git: How to remove file from index without deleting files from any repository

Fletcher Moore picture Fletcher Moore · Apr 9, 2010 · Viewed 102.1k times · Source

When you use

git rm --cached myfile

it doesn't delete from the local filesystem, which is the goal. But if you've already versioned and committed the file, pushed it to a central repository, and pulled it into yet another repository before using the command, it will delete the file from that system.

Is there a way to just remove the file from versioning without deleting it from any filesystem?

Edit: Clarified, I hope.

Answer

Chris Johnsen picture Chris Johnsen · Apr 10, 2010

I do not think a Git commit can record an intention like “stop tracking this file, but do not delete it”.

Enacting such an intention will require intervention outside Git in any repositories that merge (or rebase onto) a commit that deletes the file.


Save a Copy, Apply Deletion, Restore

Probably the easiest thing to do is to tell your downstream users to save a copy of the file, pull your deletion, then restore the file. If they are pulling via rebase and are ‘carrying’ modifications to the file, they will get conflicts. To resolve such conflicts, use git rm foo.conf && git rebase --continue (if the conflicting commit has changes besides those to the removed file) or git rebase --skip (if the conflicting commit has only changed to the removed file).

Restore File as Untracked After Pulling a Commit That Deletes It

If they have already pulled your deletion commit, they can still recover the previous version of the file with git show:

git show @{1}:foo.conf >foo.conf

Or with git checkout (per comment by William Pursell; but remember to re-remove it from the index!):

git checkout @{1} -- foo.conf && git rm --cached foo.conf

If they have taken other actions since pulling your deletion (or they are pulling with rebase into a detached HEAD), they may need something other than @{1}. They could use git log -g to find the commit just before they pulled your deletion.


In a comment, you mention that the file you want to “untrack, but keep” is some kind of configuration file that is required for running the software (directly out of a repository).

Keep File as a ‘Default’ and Manually/Automatically Activate It

If it is not completely unacceptable to continue to maintain the configuration file's content in the repository, you might be able to rename the tracked file from (e.g.) foo.conf to foo.conf.default and then instruct your users to cp foo.conf.default foo.conf after applying the rename commit. Or, if the users already use some existing part of the repository (e.g. a script or some other program configured by content in the repository (e.g. Makefile or similar)) to launch/deploy your software, you could incorporate a defaulting mechanism into the launch/deploy process:

test -f foo.conf || test -f foo.conf.default &&
    cp foo.conf.default foo.conf

With such a defaulting mechanism in place, users should be able to pull a commit that renames foo.conf to foo.conf.default without having to do any extra work. Also, you avoid having to manually copy a configuration file if you make additional installations/repositories in the future.

Rewriting History Requires Manual Intervention Anyway…

If it is unacceptable to maintain the content in the repository then you will likely want to completely eradicate it from history with something like git filter-branch --index-filter …. This amounts to rewriting history, which will require manual intervention for each branch/repository (see “Recovering From Upstream Rebase” section in the git rebase manpage). The special treatment required for your configuration file would be just another step that one must perform while recovering from the rewrite:

  1. Save a copy of the configuration file.
  2. Recover from the rewrite.
  3. Restore the configuration file.

Ignore It to Prevent Recurrence

Whatever method you use, you will probably want to include the configuration filename in a .gitignore file in the repository so that no one can inadvertently git add foo.conf again (it is possible, but requires -f/--force). If you have more than one configuration file, you might consider ‘moving’ them all into a single directory and ignoring the whole thing (by ‘moving’ I mean changing where the program expects to find its configuration files, and getting the users (or the launch/deploy mechanism) to copy/move the files to to their new location; you obviously would not want to git mv a file into a directory that you will be ignoring).