8337

I put a file that was previously being tracked by Git onto the .gitignore list. However, the file still shows up in git status after it is edited. How do I force Git to completely forget the file?

9
  • 31
    git clean -X sounds similar, but it doesn't apply in this situation (when the files are still being tracked by Git). I'm writing this for anyone looking for a solution not to follow the wrong route. Commented Feb 27, 2015 at 12:14
  • 54
    The only real answer to this is down below, see git update-index --assume-unchanged. This solution 1) keeps the file on server (index), 2) lets you modify it freely locally.
    – Qwerty
    Commented Jan 4, 2016 at 14:15
  • 17
    You need to use --skip-worktree, see: stackoverflow.com/questions/13630849/… Commented Aug 5, 2016 at 20:33
  • 160
    An important question is: should the file remain in the repository or not? Eg if someone new clones the repo, should they get the file or not? If YES then git update-index --assume-unchanged <file> is correct and the file will remain in the repository and changes will not be added with git add. If NO (for example it was some cache file, generated file etc), then git rm --cached <file> will remove it from repository.
    – Martin
    Commented Nov 22, 2016 at 10:45
  • 18
    @Martin @Qwerty Everyon should stop to advise for --assume-unchanged which is for performance to prevent git to check status of big tracked files but prefer --skip-worktree which is for modified tracked files that the user don't want to commit anymore. See stackoverflow.com/questions/13630849/…
    – Philippe
    Commented Feb 15, 2018 at 11:16

35 Answers 35

8967

.gitignore will prevent untracked files from being added (without an add -f) to the set of files tracked by Git. However, Git will continue to track any files that are already being tracked.

To stop tracking a file, we must remove it from the index:

git rm --cached <file>

To remove a folder and all files in the folder recursively:

git rm -r --cached <folder>

The removal of the file from the head revision will happen on the next commit.

WARNING: While this will not remove the physical file from your local machine, it will remove the files from other developers' machines on their next git pull.

43
  • 81
    the process that workd for me was 1. commit pending changes first 2. git rm --cached <file> and commit again 3. add the file to .gitignore, check with git status and commit again
    – mataal
    Commented Aug 13, 2009 at 21:07
  • 154
    Very important adding. If file that is ignored would be modified (but in spite of this should be not committed), after modifying and executing git add . it would be added to index. And next commit would commit it to repository. To avoid this execute right after all that mataal said one more command: git update-index --assume-unchanged <path&filename>
    – Dao
    Commented Aug 24, 2011 at 16:39
  • 40
    @AkiraYamamoto 's method worked well for me as well. In my case I suppressed the output since my repository had thousands of files: git rm -r -q --cached . Commented Oct 11, 2013 at 15:21
  • 122
    This will delete the file on git pull though. Commented May 22, 2014 at 16:04
  • 36
    git rm --cached <file> just remove file from repository, git update-index --assume-unchanged <file> not shown file in unstaged changes and does not make pull a new changes. But i want GIT JUST IGNORE CONTENT OF FILE PLEEEEEASE
    – Igor Semin
    Commented Apr 7, 2015 at 9:03
3493

The series of commands below will remove all of the items from the Git index (not from the working directory or local repository), and then will update the Git index, while respecting Git ignores. PS. Index = Cache

First:

git rm -r --cached .
git add .

Then:

git commit -am "Remove ignored files"

Or as a one-liner:

git rm -r --cached . && git add . && git commit -am "Remove ignored files"
19
  • 275
    To highlight the difference between this answer and the accepted one: Using this commands you don't need to actually know the affected files. (Imagine a temporary dir with lots of random files that should be cleared off the index). Commented Mar 27, 2014 at 10:17
  • 86
    Same as the accepted answer. Files will get deleted on git pull. Commented May 22, 2014 at 16:07
  • 101
    It would be nice to have this as a standard git command. Something like git rmignored.
    – Berik
    Commented Dec 20, 2014 at 11:59
  • 15
    @gudthing -r stands for "recursive"
    – Mark
    Commented Jul 15, 2015 at 11:24
  • 19
    With this you may end up adding other useless files that are not currently in .gitignore. Which may be difficult to find out if depending on how noise your git status is after this command. A command that only removes newly ignored files would be better. That's why I prefer thSoft's answer Commented Sep 22, 2015 at 14:27
1910
+50

git update-index does the job for me:

git update-index --assume-unchanged <file>

Note: This solution is actually independent of .gitignore as gitignore is only for untracked files.


Update, a better option

Since this answer was posted, a new option has been created and that should be preferred. You should use --skip-worktree which is for modified tracked files that the user don't want to commit anymore and keep --assume-unchanged for performance to prevent git to check status of big tracked files. See https://stackoverflow.com/a/13631525/717372 for more details...

git update-index --skip-worktree <file>

To cancel

git update-index --no-skip-worktree <file>
31
  • 259
    This IS the real answer. Awesome actually, very simple, doesn't pollute git status and actually very intuitive. Thanks. Commented Jan 12, 2014 at 23:57
  • 33
    git update-index --assume-unchanged <path> … will cause git to ignore changes in the specified path(s), regardless of .gitignore. If you pull from a remote and that remote has changes to this path, git will fail the merge with a conflict and you will need to merge manually. git rm --cached <path> … will cause git to stop tracking that path. If you do not add the path to .gitignore you will see the path in future git status. The first option has less noise in the git commit history and allows changes to the "ignored" file to be distributed in the future.
    – ManicDee
    Commented Jun 25, 2014 at 2:02
  • 40
    I'm quite confused as to how this isn't the accepted answer. The accepted answer here clearly isn't answering the actual question being asked. This answer ignores changes to the file that is in the repository whilst not removing it from the repository. Commented Dec 2, 2015 at 22:46
  • 27
    This answer would be a lot more useful if it explained exactly what the given command does, e.g. how it's different from the other suggested solutions.
    – LarsH
    Commented Aug 19, 2016 at 15:39
  • 13
    this command will only be effective on your machine right? what if i want to stop tracking the file on all machines ? also on the machines that will clone the rep in the future?
    – George
    Commented Jul 30, 2018 at 6:37
387
git ls-files -c --ignored --exclude-standard -z | xargs -0 git rm --cached
git commit -am "Remove ignored files"

This takes the list of the ignored files, removes them from the index, and commits the changes.

9
  • 7
    If you need to remove them from the working directory, too, then simply run git ls-files --ignored --exclude-standard | xargs git rm . I believe this answer is the best! Because it's very clear, Unix-way, and does the wanted thing in a direct manner, without composing the side-effects of other, more complex commands. Commented Feb 27, 2015 at 12:23
  • 6
    Great answer; however, the command will fail if you have paths with spaces on the middle, e.g.: "My dir/my_ignored_file.txt"
    – David H.
    Commented Jun 19, 2015 at 15:29
  • 8
    git ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm --cached
    – David H.
    Commented Jun 19, 2015 at 15:36
  • 5
    git rm will complain if ls-files didn't match anything. Use xargs -r git rm ... to tell xargs not to run git rm if no files matched.
    – Wolfgang
    Commented Jan 6, 2016 at 19:15
  • 11
    It would be better to use \0 as separator: git ls-files --ignored --exclude-standard -z|xargs -0 git rm --cached
    – Nils-o-mat
    Commented Feb 2, 2016 at 10:05
165

The copy/paste (one-liner) answer is:

git rm --cached -r .; git add .; git status; git commit -m "Ignore unwanted files"

This command will NOT change the content of the .gitignore file. It will ignore the files already committed to a Git repository, but now we have added them to .gitignore.

The command git status; is to review the changes and could be dropped.

Ultimately, it will immediately commit the changes with the message "Ignore unwanted files".

If you don't want to commit the changes, drop the last part of the command (git commit -m "Ignore unwanted files")

2
  • 2
    It can be read as if the command lines will change the content of file .gitignore. Can you make it clear that is not the case (without "Edit:", "Update:", or similar - the answer should appear as if it was written today). That is, not a direct clarification, but by changing the way it is written. Commented Jul 21, 2021 at 7:37
  • the first command just set all the files to be deleted, maybe you should explain the first command and in what state should the files be Commented Jan 4 at 9:28
124

Move it out, commit, and then move it back in.

This has worked for me in the past, but there is probably a 'gittier' way to accomplish this.

9
  • 3
    This worked great if you want to ignore a bunch of files that weren't previously ignored. Though like you said, there is probably a better way for this. Commented May 28, 2013 at 16:19
  • This is exactly what I did. Simply move the files to a folder outside of git, then do "git add .", "git commit". (This removed the files) then add the gitignore, referencing the files/folders, commit again to add the gitignore file to git, then copy/move back in the folders, and they should be ignored. NB: it will appear that the files were deleted from GIT, so would probably remove them from other checkouts/pulls, as mentioned in above solutions, but since you are making copies of them initially, this isnt as much of an issue IMHO. just let the rest of the team know...
    – Del
    Commented Sep 23, 2016 at 12:04
  • This is the easiest way to get rid of wrongly committed folders.
    – Martlark
    Commented Jan 14, 2017 at 8:01
  • 7
    Seems to be the only way, that I can see. It's a massive bug (not 'feature') in git that as soon as you add a file/folder to .gitignore, it doesn't just ignore that file from that point on - forever - everywhere.
    – JosephK
    Commented Sep 24, 2017 at 9:09
  • 5
    Please note that, as most (all?) other answers, this removes other people's file upon git pull. Commented Jun 23, 2021 at 11:32
110

I always use this command to remove those untracked files. One-line, Unix-style, clean output:

git ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm -r --cached

It lists all your ignored files, replaces every output line with a quoted line instead to handle paths with spaces inside, and passes everything to git rm -r --cached to remove the paths/files/directories from the index.

6
  • 3
    Great solution! Worked perfectly and feels more correct that removing all files then adding them back in. Commented Sep 9, 2015 at 8:59
  • 8
    I too found this "cleanest". It might be obvious, but just running the first part, git ls-files --ignored --exclude-standard, on its own lets you first understand/verify what files your new .gitignore is going to exclude/remove, before you go ahead and execute the final git rm.
    – JonBrave
    Commented Dec 29, 2015 at 11:56
  • Be aware, fails on filenames with certain "nasty" characters in them, e.g. \n. I have posted my solution to cater for this.
    – JonBrave
    Commented Dec 29, 2015 at 13:01
  • 3
    Another caveat: on pull, this will cause the file to be deleted in others' working directories, right?
    – LarsH
    Commented Aug 19, 2016 at 15:46
  • 3
    Git 2.32 broke the behaviour of ls-files - you now need to add -c.
    – wortwart
    Commented Oct 19, 2021 at 10:10
96

Use this when:

  1. You want to untrack a lot of files, or
  2. You updated your .gitignore file

Source: Untrack files already added to Git repository based on .gitignore

Let’s say you have already added/committed some files to your Git repository and you then add them to your .gitignore file; these files will still be present in your repository index. This article we will see how to get rid of them.

Step 1: Commit all your changes

Before proceeding, make sure all your changes are committed, including your .gitignore file.

Step 2: Remove everything from the repository

To clear your repository, use:

git rm -r --cached .
  • rm is the remove command
  • -r will allow recursive removal
  • –cached will only remove files from the index. Your files will still be there.

The rm command can be unforgiving. If you wish to try what it does beforehand, add the -n or --dry-run flag to test things out.

Step 3: Readd everything

git add .

Step 4: Commit

git commit -m ".gitignore fix"

Your repository is clean :)

Push the changes to your remote to see the changes effective there as well.

2
  • 3
    It won't delete the files from the remote repository? What if I want to keep the files both in local repo and remote repo but make git "forget" about them?
    – Avishay28
    Commented Mar 19, 2019 at 15:35
  • 1
    AFAIK this won't delete files from history because we're not using any history changing commands (correct me if I"m wrong). This only adds a new commit by deleting files ignored in gitignore from git. Those files will be there in the historical commits Commented Mar 19, 2019 at 20:00
74

If you cannot git rm a tracked file because other people might need it (warning, even if you git rm --cached, when someone else gets this change, their files will be deleted in their filesystem). These are often done due to config file overrides, authentication credentials, etc. Please look at https://gist.github.com/1423106 for ways people have worked around the problem.

To summarize:

  • Have your application look for an ignored file config-overide.ini and use that over the committed file config.ini (or alternately, look for ~/.config/myapp.ini, or $MYCONFIGFILE)
  • Commit file config-sample.ini and ignore file config.ini, have a script or similar copy the file as necessary if necessary.
  • Try to use gitattributes clean/smudge magic to apply and remove the changes for you, for instance smudge the config file as a checkout from an alternate branch and clean the config file as a checkout from HEAD. This is tricky stuff, I don't recommend it for the novice user.
  • Keep the config file on a deploy branch dedicated to it that is never merged to master. When you want to deploy/compile/test you merge to that branch and get that file. This is essentially the smudge/clean approach except using human merge policies and extra-git modules.
  • Anti-recommentation: Don't use assume-unchanged, it will only end in tears (because having git lie to itself will cause bad things to happen, like your change being lost forever).
3
  • 7
    git wouldn't remove the file, if it were dirty at the time of deletion. And if it's not dirty, retrieving the file would be as easy as git checkout <oldref> -- <filename> - but then it would be checked out and ignored.
    – amenthes
    Commented Jul 24, 2014 at 14:05
  • Concerning your last note (about --assume-unchanged) : either this is cargo cult and should be dismissed, or you can explain why (which I'm convinced of) and it becomes useful. Commented Nov 22, 2018 at 9:17
  • 1
    @RomainValeri: “Git will fail (gracefully) in case it needs to modify this file in the index e.g. when merging in a commit; thus, in case the assumed-untracked file is changed upstream, you will need to handle the situation manually.”—git-scm.com/docs/git-update-index. You commit to: (1) backup the file out of the tree; (2) reset the a.-u. bit; (3) reset the file to its original content git checkout -- file; (4) git pull or merge, which will now succeed; (5) copy the file back and examine changes; (6) set the a.-u. bit again. That's a definition of PITA in my book, but YMMV. :) Commented Jul 13, 2021 at 23:21
66

I accomplished this by using git filter-branch. The exact command I used was taken from the man page:

WARNING: this will delete the file from your entire history

git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD

This command will recreate the entire commit history, executing git rm before each commit and so will get rid of the specified file. Don't forget to back it up before running the command as it will be lost.

2
  • 13
    This will change all commit IDs, thus breaking merges from branches outside of your copy of the repository.
    – bdonlan
    Commented Aug 13, 2009 at 19:56
  • 27
    WARNING: this will delete the file from your entire history. This was what I was looking for though, to remove a completely unnecessary and oversized file (output that should never have been committed) that was committed a long time ago in the version history.
    – zebediah49
    Commented Mar 13, 2013 at 5:49
57

What didn't work for me

(Under Linux), I wanted to use the posts here suggesting the ls-files --ignored --exclude-standard | xargs git rm -r --cached approach. However, (some of) the files to be removed had an embedded newline/LF/\n in their names. Neither of the solutions:

git ls-files --ignored --exclude-standard | xargs -d"\n" git rm --cached
git ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm -r --cached

cope with this situation (get errors about files not found).

So I offer

git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cached
git commit -am "Remove ignored files"

This uses the -z argument to ls-files, and the -0 argument to xargs to cater safely/correctly for "nasty" characters in filenames.

In the manual page git-ls-files(1), it states:

When -z option is not used, TAB, LF, and backslash characters in pathnames are represented as \t, \n, and \\, respectively.

so I think my solution is needed if filenames have any of these characters in them.

3
  • 2
    For me this is the best solution. It has much better performance than a git add .. It also contains the best improvements from some comments above.
    – Nils-o-mat
    Commented Feb 2, 2016 at 12:08
  • 1
    Can you add thSoft's git commit -am "Remove ignored files" afterward to your answer? Your answers combined got me through things : j
    – kando
    Commented Oct 6, 2017 at 0:07
  • I don't understand the purpose of git commit -a. For me git rm --cached affect exactly the index so no need to stage the files after...
    – Jean Paul
    Commented Nov 8, 2018 at 11:32
43

Do the following steps for a file/folder:

Remove a File:

  1. need to add that file to .gitignore.
  2. need to remove that file using the command (git rm --cached file name).
  3. need to run (git add .).
  4. need to (commit -m) "file removed".
  5. and finally, (git push).

For example:

I want to delete the test.txt file. I accidentally pushed to GitHub and want to remove it. Commands will be as follows:

First, add "test.txt" in file .gitignore

git rm --cached test.txt
git add .
git commit -m "test.txt removed"
git push

Remove Folder:

  1. need to add that folder to file .gitignore.
  2. need to remove that folder using the command (git rm -r --cached folder name).
  3. need to run (git add .).
  4. need to (commit -m) "folder removed".
  5. and finally, (git push).

For example:

I want to delete the .idea folder/directory. I accidentally pushed to GitHub and want to remove it. The commands will be as follows:

First, add .idea in file .gitignore

git rm -r --cached .idea
git add .
git commit -m ".idea removed"
git push
1
33
  1. Update your .gitignore file – for instance, add a folder you don't want to track to .gitignore.

  2. git rm -r --cached . – Remove all tracked files, including wanted and unwanted. Your code will be safe as long as you have saved locally.

  3. git add . – All files will be added back in, except those in .gitignore.


Hat tip to @AkiraYamamoto for pointing us in the right direction.

4
  • 1
    How about downvoted due to the fact that it won't actually work as you need a -r to run rm recursively anyway :) (Someone didn't copy correctly) Commented Jul 28, 2016 at 1:55
  • 1
    Warning: This technique doesn't actually cause git to ignore the file, instead it actually causes git to delete the file. That means if you use this solution, any time anyone else does a git pull, the file will get deleted. So it isn't actually ignored. See the solution suggesting git update-index --assume-unchanged instead for a solution to the original question.
    – orrd
    Commented Sep 15, 2017 at 17:33
  • 1
    Tks for the reference to my comment. I find it weird that my original comment was deleted from stackoverflow.com/a/1274447/475876 Commented May 24, 2021 at 1:46
  • I had to run the rm with -f but this did the job.
    – Rafa
    Commented Apr 21, 2022 at 9:35
25

Do the following steps serially, and you will be fine.

  1. Remove the mistakenly added files from the directory/storage. You can use the "rm -r" (for Linux) command or delete them by browsing the directories. Or move them to another location on your PC. (You maybe need to close the IDE if running for moving/removing.)

  2. Add the files / directories to the .gitignore file now and save it.

  3. Now remove them from the Git cache by using these commands (if there is more than one directory, remove them one by one by repeatedly issuing this command)

     git rm -r --cached path-to-those-files
    
  4. Now do a commit and push by using the following commands. This will remove those files from Git remote and make Git stop tracking those files.

     git add .
     git commit -m "removed unnecessary files from Git"
     git push origin
    
1
  • but will it remove the file from other devs machines when they git pull ?
    – rob
    Commented Apr 24, 2023 at 8:51
20

I think, that maybe Git can't totally forget about a file because of its conception (section "Snapshots, Not Differences").

This problem is absent, for example, when using CVS. CVS stores information as a list of file-based changes. Information for CVS is a set of files and the changes made to each file over time.

But in Git every time you commit, or save the state of your project, it basically takes a picture of what all your files look like at that moment and stores a reference to that snapshot. So, if you added file once, it will always be present in that snapshot.

These two articles were helpful for me:

git assume-unchanged vs skip-worktree and How to ignore changes in tracked files with Git

Basing on it I do the following, if the file is already tracked:

git update-index --skip-worktree <file>

From this moment all local changes in this file will be ignored and will not go to remote. If the file is changed on remote, conflict will occur, when git pull. Stash won't work. To resolve it, copy the file content to the safe place and follow these steps:

git update-index --no-skip-worktree <file>
git stash
git pull

The file content will be replaced by the remote content. Paste your changes from the safe place to the file and perform again:

git update-index --skip-worktree <file>

If everyone, who works with the project, will perform git update-index --skip-worktree <file>, problems with pull should be absent. This solution is OK for configurations files, when every developer has their own project configuration.

It is not very convenient to do this every time, when the file has been changed on remote, but it can protect it from overwriting by remote content.

0
14

Move or copy the file to a safe location, so you don't lose it. Then 'git rm' the file and commit.

The file will still show up if you revert to one of those earlier commits, or another branch where it has not been removed. However, in all future commits, you will not see the file again. If the file is in the Git ignore, then you can move it back into the folder, and Git won't see it.

2
  • 51
    git rm --cached will remove the file from the index without deleting it from disk, so no need to move/copy it away
    – bdonlan
    Commented Aug 13, 2009 at 19:56
  • @bdonlan This doesn't work. It throws fatal: No pathspec was given. Which files should I remove? (exit code 128) Commented Jan 27, 2023 at 0:37
14

Using the git rm --cached command does not answer the original question:

How do you force git to completely forget about [a file]?

In fact, this solution will cause the file to be deleted in every other instance of the repository when executing a git pull!

The correct way to force Git to forget about a file is documented by GitHub here.

I recommend reading the documentation, but basically:

git fetch --all
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch full/path/to/file' --prune-empty --tag-name-filter cat -- --all
git push origin --force --all
git push origin --force --tags
git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
git reflog expire --expire=now --all
git gc --prune=now

Just replace full/path/to/file with the full path of the file. Make sure you've added the file to your .gitignore file.

You'll also need to (temporarily) allow non-fast-forward pushes to your repository, since you're changing your Git history.

12

The answer from Matt Frear was the most effective IMHO. The following is just a PowerShell script for those on Windows to only remove files from their Git repository that matches their exclusion list.

# Get files matching exclusionsfrom .gitignore
# Excluding comments and empty lines
$ignoreFiles =  gc .gitignore | ?{$_ -notmatch  "#"} |  ?{$_ -match  "\S"} | % {
                    $ignore = "*" + $_ + "*"
                    (gci -r -i $ignore).FullName
                }
$ignoreFiles = $ignoreFiles| ?{$_ -match  "\S"}

# Remove each of these file from Git
$ignoreFiles | % { git rm $_}

git add .
1
  • In what situation won't this list of files be equal to the recursive --cached? Commented Jan 10, 2014 at 18:47
9

The accepted answer does not "make Git "forget" about a file..." (historically). It only makes Git ignore the file in the present/future.

This method makes Git completely forget ignored files (past/present/future), but it does not delete anything from the working directory (even when re-pulled from remote).

This method requires usage of file /.git/info/exclude (preferred) or a pre-existing .gitignore in all the commits that have files to be ignored/forgotten. 1

All methods of enforcing Git ignore behavior after-the-fact effectively rewrite history and thus have significant ramifications for any public/shared/collaborative repositories that might be pulled after this process. 2

General advice: start with a clean repository - everything committed, nothing pending in working directory or index, and make a backup!

Also, the comments/revision history of this answer (and revision history of this question) may be useful/enlightening.

#Commit up-to-date .gitignore (if not already existing)
#This command must be run on each branch

git add .gitignore
git commit -m "Create .gitignore"

#Apply standard Git ignore behavior only to the current index, not the working directory (--cached)
#If this command returns nothing, ensure /.git/info/exclude AND/OR .gitignore exist
#This command must be run on each branch

git ls-files -z --ignored --exclude-standard | xargs -0 git rm --cached

#Commit to prevent working directory data loss!
#This commit will be automatically deleted by the --prune-empty flag in the following command
#This command must be run on each branch

git commit -m "ignored index"

#Apply standard git ignore behavior RETROACTIVELY to all commits from all branches (--all)
#This step WILL delete ignored files from working directory UNLESS they have been dereferenced from the index by the commit above
#This step will also delete any "empty" commits.  If deliberate "empty" commits should be kept, remove --prune-empty and instead run git reset HEAD^ immediately after this command

git filter-branch --tree-filter 'git ls-files -z --ignored --exclude-standard | xargs -0 git rm -f --ignore-unmatch' --prune-empty --tag-name-filter cat -- --all

#List all still-existing files that are now ignored properly
#If this command returns nothing, it's time to restore from backup and start over
#This command must be run on each branch

git ls-files --other --ignored --exclude-standard

Finally, follow the rest of this GitHub guide (starting at step 6) which includes important warnings/information about the commands below.

git push origin --force --all
git push origin --force --tags
git for-each-ref --format="delete %(refname)" refs/original | git update-ref --stdin
git reflog expire --expire=now --all
git gc --prune=now

Other developers that pull from the now-modified remote repository should make a backup and then:

#fetch modified remote

git fetch --all

#"Pull" changes WITHOUT deleting newly-ignored files from working directory
#This will overwrite local tracked files with remote - ensure any local modifications are backed-up/stashed

git reset FETCH_HEAD

Footnotes

1 Because /.git/info/exclude can be applied to all historical commits using the instructions above, perhaps details about getting a .gitignore file into the historical commit(s) that need it is beyond the scope of this answer. I wanted a proper .gitignore file to be in the root commit, as if it was the first thing I did. Others may not care since /.git/info/exclude can accomplish the same thing regardless where the .gitignore file exists in the commit history, and clearly rewriting history is a very touchy subject, even when aware of the ramifications.

FWIW, potential methods may include git rebase or a git filter-branch that copies an external .gitignore into each commit, like the answers to this question.

2 Enforcing Git ignore behavior after-the-fact by committing the results of a stand-alone git rm --cached command may result in newly-ignored file deletion in future pulls from the force-pushed remote. The --prune-empty flag in the following git filter-branch command avoids this problem by automatically removing the previous "delete all ignored files" index-only commit. Rewriting Git history also changes commit hashes, which will wreak havoc on future pulls from public/shared/collaborative repositories. Please understand the ramifications fully before doing this to such a repository. This GitHub guide specifies the following:

Tell your collaborators to rebase, not merge, any branches they created off of your old (tainted) repository history. One merge commit could reintroduce some or all of the tainted history that you just went to the trouble of purging.

Alternative solutions that do not affect the remote repository are git update-index --assume-unchanged </path/file> or git update-index --skip-worktree <file>, examples of which can be found here.

1
  • +1 for git update-index --assume-unchanged </path/file> which I assume is what a good portion of devs are looking for (files that should be overwritten by neither push or pull) Commented Sep 2, 2022 at 16:05
9

In my case I needed to put ".envrc" in the .gitignore file.

And then I used:

git update-index --skip-worktree .envrc
git rm --cached .envrc

And the file was removed.

Then I committed again, telling that the file was removed.

But when I used the command git log -p, the content of the file (which was secret credentials of the Amazon S3) was showing the content which was removed and I don't want to show this content ever on the history of the Git repository.

Then I used this command:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch .envrc' HEAD

And I don't see the content again.

1
  • git update-index --skip-worktree .idea/ git rm -r --cached .idea/ did the trick after I accidentally hit add . with newly generated IDE folder Commented Jul 9, 2021 at 9:56
7

I liked JonBrave's answer, but I have messy enough working directories that commit -a scares me a bit, so here's what I've done:

git config --global alias.exclude-ignored '!git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cached &&  git ls-files -z --ignored --exclude-standard | xargs -0 git stage &&  git stage .gitignore && git commit -m "new gitignore and remove ignored files from index"'

Breaking it down:

git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cached
git ls-files -z --ignored --exclude-standard | xargs -0 git stage
git stage .gitignore
git commit -m "new gitignore and remove ignored files from index"
  • remove ignored files from the index
  • stage .gitignore and the files you just removed
  • commit
6

The BFG is specifically designed for removing unwanted data like big files or passwords from Git repositories, so it has a simple flag that will remove any large historical (not-in-your-current-commit) files: '--strip-blobs-bigger-than'

java -jar bfg.jar --strip-blobs-bigger-than 100M

If you'd like to specify files by name, you can do that too:

java -jar bfg.jar --delete-files *.mp4

The BFG is 10-1000x faster than git filter-branch and is generally much easier to use - check the full usage instructions and examples for more details.

Source: Reduce repository size

6

If you don't want to use the CLI and are working on Windows, a very simple solution is to use TortoiseGit. It has the "Delete (keep local)" Action in the menu which works fine.

5

This is no longer an issue in the latest Git (v2.17.1 at the time of writing).

The .gitignore file finally ignores tracked-but-deleted files. You can test this for yourself by running the following script. The final git status statement should report "nothing to commit".

# Create an empty repository
mkdir gitignore-test
cd gitignore-test
git init

# Create a file and commit it
echo "hello" > file
git add file
git commit -m initial

# Add the file to gitignore and commit
echo "file" > .gitignore
git add .gitignore
git commit -m gitignore

# Remove the file and commit
git rm file
git commit -m "removed file"

# Reintroduce the file and check status.
# .gitignore is now respected - status reports "nothing to commit".
echo "hello" > file
git status
1
  • 1
    I'm glad git now does this. However, the OP was asking about not tracking modifications in files present in the .gitignore, not deleted files still showing a status.
    – mrturtle
    Commented Mar 14, 2019 at 15:26
5

This is how I solved my issue:

git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD
git push

In this, we are basically trying to rewrite the history of that particular file in previous commits also.

For more information, you can refer to the man page of filter-branch here.

Source: Removing sensitive data from a repository - using filter-branch

Source: Git: How to remove a big file wrongly committed

4

Especially for the IDE-based files, I use this:

For instance, for the slnx.sqlite file, I just got rid off it completely like the following:

git rm {PATH_OF_THE_FILE}/slnx.sqlite -f
git commit -m "remove slnx.sqlite"

Just keep that in mind that some of those files store some local user settings and preferences for projects (like what files you had open). So every time you navigate or do some changes in your IDE, that file is changed and therefore it checks it out and show as uncommitted changes.

3

In case of already committed DS_Store:

find . -name .DS_Store -print0 | xargs -0 git rm --ignore-unmatch

Ignore them by:

echo ".DS_Store" >> ~/.gitignore_global
echo "._.DS_Store" >> ~/.gitignore_global
echo "**/.DS_Store" >> ~/.gitignore_global
echo "**/._.DS_Store" >> ~/.gitignore_global
git config --global core.excludesfile ~/.gitignore_global

Finally, make a commit!

1
  • What is "DS_Store" (or ".DS_Store"?)? A Mac-specific file? Commented Jul 21, 2021 at 7:14
3

In my case here, I had several .lock files in several directories that I needed to remove. I ran the following and it worked without having to go into each directory to remove them:

git rm -r --cached **/*.lock

Doing this went into each folder under the 'root' of where I was at and excluded all files that matched the pattern.

1
  • 1
    Where did the .lock files come from? What created them? Commented Jul 21, 2021 at 7:57
3

If anyone is having a hard time on Windows and you want to ignore the entire folder, go to the desired 'folder' on file explorer, right click and do 'Git Bash Here' (Git for Windows should have been installed).

Run this command:

git ls-files -z | xargs -0 git update-index --assume-unchanged
1
  • What do you mean by "do Git Bash Here"? How is it connected to the command-line? Please respond by editing (changing) your answer, not here in comments (without "Edit:", "Update:", or similar - the answer should appear as if it was written today). Commented Jul 21, 2021 at 7:55
3

I'll show a real-life scenario.

I was committing .env.development & .env.production to git because it contained constants, not environment variables as i was using sqlite database.

Both .env.development & .env.production are same so I'll just use .env.development as an example.

Previously, I was committing this:

.env.development

SQLITE_DATABASE_NAME=users.sqlite

But later on, I added sqlite database backups using litestream with cloudflare r2 so I had to update .env.development with those variables.

.env.development

SQLITE_DATABASE_NAME=users.sqlite

# Use Litestream to backup SQLite database on every insert
REPLICA_URL=https://<account_id>.r2.cloudflarestorage.com
REPLICA_BUCKET_NAME=bucket_name
CLOUDFLARE_R2_ACCESS_KEY_ID=access_key_id
CLOUDFLARE_R2_SECRET_ACCESS_KEY=secret_access_key

Assume, the above contained real environment keys.

See commits on 27th Feb 2024 on https://github.com/deadcoder0904/easypanel-nextjs-sqlite/ (the first two commits starting from bottom)

I first used the solution mentioned here:

git update-index --assume-unchanged .env.development .env.production

This kept .env.development & .env.production on its previous state. That is just containing SQLITE_DATABASE_NAME.

And it removed tracking on .env.development & .env.production. See the commit named backup database using litestreamw with cloudflare r2. I added environment variables but they weren't committed.

Then I used this solution (see the commit named stop tracking .env.development & .env.production) to stop tracking those environment files altogether.

git rm --cached .\.env.development .\.env.production

So, next time I'll just use the 2nd command & add .env.development & .env.production to .gitignore to stop tracking them altogether from the next commit.

Pro-Tip: Always check git status to make sure you are not committing real environment variables to .git or use dotenvx.

Not the answer you're looking for? Browse other questions tagged or ask your own question.