6290

There is a file that was being tracked by git, but now the file is on the .gitignore list.

However, that file keeps showing up in git status after it's edited. How do you force git to completely forget about it?

Akshay Sood
  • 3,867
  • 10
  • 28
  • 50
Ivan
  • 77,768
  • 15
  • 47
  • 57
  • 22
    [`git clean -X`](http://makandracards.com/makandra/17529-git-how-to-remove-ignored-files-from-your-repository-s-directory) 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. – imz -- Ivan Zakharyaschev Feb 27 '15 at 12:14
  • 39
    The only real answer to this is down below, see [`git update-index --assume-unchanged`](http://stackoverflow.com/a/20241145/985454). This solution 1) keeps the file on server (index), 2) lets you modify it freely locally. – Qwerty Jan 04 '16 at 14:15
  • 10
    You need to use `--skip-worktree`, see: http://stackoverflow.com/questions/13630849/git-difference-between-assume-unchanged-and-skip-worktree/13631525#13631525 – Doppelganger Aug 05 '16 at 20:33
  • 96
    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 ` 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 ` will remove it from repository. – Martin Nov 22 '16 at 10:45
  • 11
    @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 http://stackoverflow.com/questions/13630849/git-difference-between-assume-unchanged-and-skip-worktree/13631525#13631525 – Philippe Feb 15 '18 at 11:16
  • Possible duplicate of [How to stop tracking and ignore changes to a file in Git?](https://stackoverflow.com/questions/936249/how-to-stop-tracking-and-ignore-changes-to-a-file-in-git) – melpomene Jun 17 '19 at 05:15
  • 1
    `git update-index --assume-unchanged ` has a serious problem I just ran into: running `git stash` after making local changes to this "ignored" file, all local changes will be lost (i.e. `git stash pop` will not bring them back) – jannikb Jul 27 '19 at 09:14
  • 1
    I tried but it seems when i try to pull it is not working and giving error like '`Your local changes to the following files would be overwritten by merge` – NeverGiveUp161 Sep 12 '19 at 09:05

29 Answers29

6757

.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 you need to remove it from the index. This can be achieved with this command.

git rm --cached <file>

If you want to remove a whole folder, you need to remove all files in it 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, it will remove the files from other developers machines on next git pull.

Robin Wieruch
  • 10,245
  • 6
  • 68
  • 90
CB Bailey
  • 648,528
  • 94
  • 608
  • 638
  • 64
    the process that workd for me was 1. commit pending changes first 2. git rm --cached and commit again 3. add the file to .gitignore, check with git status and commit again – mataal Aug 13 '09 at 21:07
  • 126
    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 ` – Dao Aug 24 '11 at 16:39
  • 5
    mataal's comment is very important. Commit pending changes first, THEN git rm --cached and commit again. If the rm is part of another commit it doesn't work as expected. – Andiih Jul 31 '12 at 09:02
  • I find @AkiraYamamoto 's solution better. – qed Sep 28 '13 at 17:28
  • 34
    @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 .` – Aaron Blenkush Oct 11 '13 at 15:21
  • 4
    You should make reference to Seth's answer below as a danger of this approach. The exact thing Seth mentioned happened to me but I'd followed your answer due to the number of up-votes. – Mark Oct 30 '13 at 14:58
  • @Mark: there's no danger because git will only remove the file on checkout (merge, etc.) if it is unchanged in the working tree. In this case, if the developer decides that he really does want the unchanged version kept he can easily retrieve the version from the commit before you removed it. – CB Bailey Oct 30 '13 at 15:09
  • 6
    @CharlesBailey: By your own comment you've shown there is a case where the file can be removed which you might not want. Just because you can retrieve it doesn't mean that you want it to happen. I 'stopped tracking' a file by this method and when I pulled to a qa server it deleted the important (but environment specific) shell script rather than just not tracking it. This slowed down the deployment process and had it been a live roll out there would have been downtime while I retrieved the file. I'm not saying this is the wrong method, I'm saying you should make reference to Seth's answer too. – Mark Oct 30 '13 at 15:34
  • 2
    @CharlesBailey: Surely you can see there's a reason why Seth has 16 upvotes for his answer. Probably 15 other people making the same mistake I did. – Mark Oct 30 '13 at 15:36
  • 2
    @Mark: You probably don't want to use a source control tool as the only element in a deployment strategy but if you do then you certainly aren't in the same situation as the question asker who wanted to force git "to completely forget about" the file. I did explicitly say in my answer: "The removal of the file from the head revision will happen on the next commit." – CB Bailey Oct 30 '13 at 15:48
  • 3
    @CharlesBailey: Firstly, let's not get petty, I didn't set up this deployment strategy it's a project which I've inherited. Secondly, the asker said "that file keeps showing up in git st after it's edited" so clearly they wanted to keep the file, just not have it tracked, hence why a warning about it's risk of deletion is warranted. – Mark Oct 30 '13 at 15:55
  • 95
    This will delete the file on `git pull` though. – Petr Peller May 22 '14 at 16:04
  • For some reason i had to do a Git push right after this solution. If i didn't and made a change in the fileit would pop back up in git status. – MacGyver Mar 18 '15 at 14:25
  • 25
    git rm --cached just remove file from repository, git update-index --assume-unchanged 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 Apr 07 '15 at 09:03
  • Also to do it recursively: git rm --cached -r lib/data/excel/* – Donato Apr 10 '15 at 22:34
  • If you're having problems with this, try running it on the root directory of your git repo, rather than a sub-folder. – Andy McKenzie May 17 '15 at 17:00
  • @CharlesBailey unfortunly, is not working in my case. Changed a lot of files. In some times I figured out that problem is in file endings (autocrlf), but after I disable it I still have 4k+ files which are assumed to be changed but actually are the same. I think there is problem with encoding, but it's the hypothesis only. – Alex Zhukovskiy Feb 08 '17 at 13:55
  • Just in case you wanted to remove only a few cached files but you removed all of them because "git rm --cached -r ." and you immediately deeply regret it, this will help - "git reset HEAD ." – lightsong Feb 15 '17 at 03:22
  • If you keep getting "Fatal: pathspec ... did not match any files", you can list them all with "git ls-files", and then copy and paste the path of the offending file. – rothschild86 Apr 04 '17 at 16:43
  • 1
    i just tired this answer with my github repository and it didnt work – Parsa Apr 10 '17 at 13:15
  • 7
    @PetrPeller, that's right .. then what is the right solution ? – ebram khalil Apr 29 '17 at 15:38
  • @CharlesBailey I have too many files and folders in `.gitignore`. How to remove them from the index in one step? not one by one with `` as you said. – Dr.jacky Sep 15 '17 at 06:34
  • A hanging point for me was that the Git app (as of May 2018) does NOT handle this properly. Even if these changes are in a single commit (no other changes), committing with the Git app results in "Commit failed - exit code 1 received". Committing and pushing via command line is what finally resolved this for me using this answer. – ryanm May 17 '18 at 17:38
  • 3
    This supports wildcards, for example: `git rm --cached *.pyc` will recursively remove all files with `.pyc` extensions. – alex May 31 '18 at 18:57
  • A common use for this is for node_modules: `sudo git rm --cached -r ./node_modules/` – dylanh724 Nov 08 '18 at 03:08
  • 3
    The complete answer is `git rm --cached -r .; git add .; git status` – youhans Nov 19 '18 at 11:18
  • 1
    @youhans: But I seem to have to run git commit to complete it. – John Nov 29 '18 at 23:24
  • I'm still using this answer as it's proven safer, more conservative than others, with which I've experienced undesirable effects. The full flow is `git rm --cached `, `git add .`, `git commit`, `git push`. – hBrent Jul 26 '19 at 21:05
  • 2
    It would help if the answer also specified how to avoid the removal of files that are now untracked and added to gitignore when someone else performs a git pull. Also, when switching branches, the files will be removed. this is highly problematic if you have tmp files that represent a state (eg in terraform) that get blown away. – openCivilisation Mar 24 '20 at 19:59
  • i've come back to this page about 1000 times in the last month. And i suspect i will come back here another 1000 times in the future. Thanks for this. – Quintonn May 14 '20 at 19:43
  • 1
    [@openCivilisation](https://stackoverflow.com/users/1692999/opencivilisation) If you want to make git "completely forget" (retroactively apply gitignore as if it had existed in the beginning), **AND/OR** want to **avoid** removing files from other developers machines on the next `git pull`, see [this](https://stackoverflow.com/a/57487519/423125) answer or [this](https://stackoverflow.com/q/57418769/423125) question/answer – goofology May 28 '20 at 08:16
  • I committed with a large file included. Then I realize it should be ignored, so I add it in .gitignore. I use `git rm --cache`, `git add` and `git commit` to commit again. But I found the .git directory is still large and when I push it to GitHub, it cost a long time and eventually failed. My question is how to remove the large file thoroughly (including the record in .git directory) so that I can push quickly. – Liang Xiao Jun 06 '20 at 06:53
  • @LiangXiao: That's a separate question to this one. Post it as a question. – Oddthinking Jul 03 '20 at 04:10
  • Even when it's added to git ignore? It will be removed from other developers on the next pull? – iamafasha Oct 31 '20 at 05:36
  • It's important to RUN that COMMAND from your GIT ROOT DIRECTORY – Martin Nov 06 '20 at 11:45
  • thank you. i kept doing git rm and it would remove the whole file! – Arthur Bowers Apr 15 '21 at 09:06
  • I get fatal: pathspec '' did not match any files – Wassadamo May 19 '21 at 01:04
2911

The series of commands below will remove all of the items from the Git Index (not from the working directory or local repo), 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 one-liner:

git rm -r --cached . && git add . && git commit -am "Remove ignored files"
Matt Frear
  • 45,587
  • 10
  • 66
  • 82
  • 1
    It could use a bit more exposition, but yes, this was the best solution to my problem. – vlasits Feb 17 '14 at 14:38
  • 226
    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). – Ludwig Mar 27 '14 at 10:17
  • 66
    Same as the accepted answer. Files will get deleted on `git pull`. – Petr Peller May 22 '14 at 16:07
  • I'm having a hard time finding an answer, but will this solution delete any files? If I run this on my local machine, push changes out, and someone pulls, will they lose anything on their machine? – jaredready Oct 21 '14 at 16:39
  • 81
    It would be nice to have this as a standard git command. Something like `git rmignored`. – Berik Dec 20 '14 at 11:59
  • what is the purpose of the `-r` flag before `--cached`? Seems to work without it – benscabbia Jun 30 '15 at 19:15
  • 12
    @gudthing -r stands for "recursive" – Mark Jul 15 '15 at 11:24
  • 2
    This solution causes all current files to show up as "new." Will they show up as new after the commit? [How to undo git rm -r . if you don't commit](http://stackoverflow.com/questions/2125710/how-to-revert-a-git-rm-r) – Josiah Yoder Aug 07 '15 at 17:29
  • 16
    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](http://stackoverflow.com/a/23839198/236871) – KurzedMetal Sep 22 '15 at 14:27
  • but what if I don't want to commit all the changes yet? – Harry May 03 '16 at 20:36
  • Thanks! There was no way was going to go through all the files listing which ones I wanted to be ignored next commit. – Charles Clayton Aug 18 '16 at 00:07
  • 4
    So, just so you guys know git rm -r --cached . will remove literally every single file in the repo from tracking. Every one. Regardless of whether its in the gitignore. I'm apparently an idiot and actually ran that command and pushed lol. And I had thousands and thousands of files. I had to open reflog, git clean -d, checkout branch before this command, create temp branch, set master to track new branch, check out master, and force push to fix it. ouch. – Josh Sanders Sep 09 '17 at 07:47
  • 4
    This is a **dangerous** command as it also wipes out **-f** switch applied to early files. For example we want to explicitly save a single DLL file then we generally do is to use **-f** switch to add that file in .git tracking explicitly. However **running the `git rm -r --cached .` command will also remove earlier forced files.**. Therefore its better to use `git rm -r --cached ` rather than adding dot. – vibs2006 Jan 23 '18 at 11:47
  • No! This makes a mess. Use Konstantin's answer below. – arnoldbird Apr 06 '18 at 17:39
  • 2
    @Berik: `git clean -X` Remove only files ignored by Git – Eugen Konkov May 09 '18 at 11:39
  • This is not working for me. Running `git push` after these instructions still keeps trying to upload files in my `.gitignore`. – Cameron Hudson Aug 14 '18 at 23:21
  • For more details about what each of the flags mean to `rm` see [this post](http://www.codeblocq.com/2016/01/Untrack-files-already-added-to-git-repository-based-on-gitignore/). – Senseful Feb 14 '19 at 20:52
  • Is there any way I can achieve to ignore my file by not removing remotely? – Kiran Jasvanee Jun 07 '19 at 06:14
  • 1
    @Berik you could add an alias https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases – Keara Jul 12 '19 at 22:48
  • This answer got the job done for me and didn't require anything but a page refresh on the Github repo to reflect changes on my local machine after pushing. – overcoded Jul 16 '19 at 13:16
  • git commit -am "Remove ignored files" was the missing step to the accepted answer (at least for me) – john ktejik Aug 12 '20 at 20:43
1334

git update-index does the job for me:

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

Note: This solution is actually independent on .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>
rofrol
  • 12,038
  • 7
  • 62
  • 63
Konstantin
  • 13,940
  • 1
  • 11
  • 8
  • 203
    This **IS** the real answer. Awesome actually, very simple, doesn't pollute `git status` and actually very intuitive. Thanks. – Pablo Olmos de Aguilera C. Jan 12 '14 at 23:57
  • 4
    I went for the good enough `rm [...] .` solution, as at least I could grok how it worked. I found no great documentation on what `update-index` & `--assume-unchanged` do. Can anyone add how this compares to the other, in that I would like to remove all files that would have been ignored? (Or a link to clear explanation?) – Brady Trainor Mar 01 '14 at 00:12
  • 27
    `git update-index --assume-unchanged …` 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 …` 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 Jun 25 '14 at 02:02
  • `hg forget` is mostly `rm --cached`. This trick is really cool (for forget forever). – weakish Nov 28 '14 at 08:23
  • If you already added the file using `git add`, use `git reset [file]` to undo your add and use this command to forget the file. – Halil Kaskavalci Jun 08 '15 at 16:02
  • 3
    This is very very useful (I wish I could upvote more than once) but technically it's not the answer to the question. Because it will do it independent of the file being in .gitignore or not, that's what I mean it's not 100 percent answer to the OP question (it solved my problem though). – Nader Ghanbari Nov 11 '15 at 09:54
  • 32
    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. – Dave Cooper Dec 02 '15 at 22:46
  • 16
    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 Aug 19 '16 at 15:39
  • 2
    E.g. if I use this option, will other repos that pull from the remote know to stop tracking the file? (Maybe only if I add the file to .gitignore?) – LarsH Aug 19 '16 at 17:43
  • For the files in submodule, we need execute the command in submodule folder. – liwp_Stephen Sep 02 '16 at 08:56
  • 2
    if it's a directory, make sure to add a trailing slash at the end of the directory name. e.g. `git update-index --assume-unchanged .history/` – Alex Cory Mar 04 '17 at 01:18
  • 2
    This solution is what I was looking for. `git rm --cached` will delete the removed file upon pull (which means, you don't want to just ignore the file, you want to delete it). `git rm --cached ` with `.gitignore` is a misleading combination. The actual solution that will not delete your files on your colleagues who run `git pull` after your changes is this answer. Thank you ! Run `git ls-files -v | grep '^[[:lower:]]'` to find files ignored this way. – George Dimitriadis Mar 23 '17 at 09:37
  • 7
    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 Jul 30 '18 at 06:37
  • Yes, this command will only stop tracking it locally. To stop tracking file completely just remove if from the repository and commit/push the changes. – Konstantin Jul 31 '18 at 08:15
  • This won't help me solve, my case is files was staged and pushed to remote. So this solution is temporary remove the files from my view, but if files were changed again, it will come back to my unstaged view. – Natta Wang Aug 31 '18 at 01:14
  • In Eclipse/EGit: right click on file > Team > Advanced > Assume Unchanged – golimar Oct 22 '18 at 09:07
  • 4
    So this is only done locally, while `git rm --cached` will delete it upon pull for others. What if one simply wants to ignore future changes to the file, both on your machine and others (that already have the file), and not have it auto-deleted as it currently is on any machines? – miyalys Apr 15 '19 at 10:08
  • 1
    I tried but it seems when i try to pull it is not working and giving error like `Your local changes to the following files would be overwritten by merge` – NeverGiveUp161 Sep 12 '19 at 09:03
  • There are instances in which you want to push clean files to a repo, then disable change tracking so that users can edit them on their machine. One example would be appsettings files for ASP.NET applications. – KappaG3 Jan 30 '20 at 09:56
  • So I had a slightly different use case; I was testing build changes to a project with 400 sub projects, and I wanted to isolate just one sub project, so I deleted the 399 I didn't want to work on atm, and ran `git ls-files --deleted -z | git update-index --assume-unchanged -z --stdin` once I was ready I undid the delete and restored tracking with `for f in $(git ls-files -v | grep '^[[:lower:]]' | awk '{print $2}'); do git update-index --no-assume-unchanged "$f"; done` – Calvin Taylor Jul 23 '20 at 15:47
  • This option still showing all tracked files as modified which one does not want to see and simply get rid of just seeing them without deleting – Shashank Bhatt Sep 11 '20 at 10:33
  • 1
    After setting `--skip-worktree` for one file, I cannot checkout another branch: `git checkout` says `Please commit your changes or stash them before you switch branches.` although `git status` claims `Your branch is up to date`, `nothing to commit, working tree clean`. So without additional research/explanation this answer might break stuff. – Theo Dec 10 '20 at 09:55
300
git ls-files --ignored --exclude-standard -z | xargs -0 git rm --cached
git commit -am "Remove ignored files"

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

thSoft
  • 19,314
  • 5
  • 82
  • 97
  • 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. – imz -- Ivan Zakharyaschev Feb 27 '15 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 Hernandez Jun 19 '15 at 15:29
  • 8
    git ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm --cached – David Hernandez Jun 19 '15 at 15:36
  • `git ls-files --ignored --exclude-standard | xargs -d"\n" git rm --cached` – KurzedMetal Sep 04 '15 at 18:16
  • Be aware, fails on filenames with certain "nasty" characters in them, e.g. `\n`. I have posted my solution to cater for this. – JonBrave Dec 29 '15 at 13:02
  • 3
    `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 Jan 06 '16 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 Feb 02 '16 at 10:05
  • I get Argument list too long. How the hell do I untrack all of these ignored files?? – David P Apr 21 '16 at 02:44
  • 2
    Can you explain the motivation of adding the option `-a` to `git commit`? For me it unnecessary... – Jean Paul Nov 08 '18 at 11:40
  • TBH, this is the best answer for me, the `git rm -r --cached . && git add .` does a mess in my repo. – KurzedMetal Feb 27 '20 at 16:13
95

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, replace every output line with a quoted line instead to handle paths with spaces inside, and pass everything to git rm -r --cached to remove the paths/files/dirs from the index.

David Hernandez
  • 2,632
  • 21
  • 18
  • 3
    Great solution! Worked perfectly and feels more correct that removing all files then adding them back in. – Jon Catmull Sep 09 '15 at 08:59
  • 6
    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 Dec 29 '15 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 Dec 29 '15 at 13:01
  • 3
    Another caveat: on pull, this will cause the file to be deleted in others' working directories, right? – LarsH Aug 19 '16 at 15:46
  • tried, but didn't work for me: `sed: 1: "s/.*/": unterminated substitute in regular expression` in a filter-branch command on a repo with spaces. (Seemed to work outside the filter-branch though). I used `git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cached` from [@JonBrave's](https://stackoverflow.com/users/489865/jonbrave) [answer](https://stackoverflow.com/a/34511442/423125) instead. – goofology Aug 12 '19 at 02:50
82

move it out, commit, then move it back in. This has worked for me in the past. There is probably a 'gittier' way to accomplish this.

Joel Hooks
  • 6,059
  • 3
  • 31
  • 37
  • 2
    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. – Oskar Persson May 28 '13 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 Sep 23 '16 at 12:04
  • This is the easiest way to get rid of wrongly committed folders. – Martlark Jan 14 '17 at 08:01
  • 2
    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 Sep 24 '17 at 09:09
  • This worked after I had them added, and then after the fact added them to .gitignore – hanzolo Jul 29 '19 at 22:00
  • This works the best for me, even after having tried the other "gittier" ways. Sometimes simple is best. – Patrick Chu Apr 25 '20 at 16:00
70

Use this when:

1. You want to untrack a lot of files, or

2. You updated your gitignore file

Source link: http://www.codeblocq.com/2016/01/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; 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 repo, 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: Re add 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.

Dheeraj Bhaskar
  • 17,151
  • 9
  • 58
  • 65
  • 2
    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 Mar 19 '19 at 15:35
  • 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 – Dheeraj Bhaskar Mar 19 '19 at 20:00
68

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).
Seth Robertson
  • 27,856
  • 5
  • 55
  • 52
  • 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 -- ` - but then it would be checked out and ignored. – amenthes Jul 24 '14 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. – RomainValeri Nov 22 '18 at 09:17
56

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.

bolov
  • 58,757
  • 13
  • 108
  • 182
drrlvn
  • 7,534
  • 2
  • 39
  • 56
  • 11
    This will change all commit IDs, thus breaking merges from branches outside of your copy of the repository. – bdonlan Aug 13 '09 at 19:56
  • 24
    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 Mar 13 '13 at 05:49
51

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.

Community
  • 1
  • 1
JonBrave
  • 3,109
  • 1
  • 28
  • 87
  • 1
    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 Feb 02 '16 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 Oct 06 '17 at 00: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 Nov 08 '18 at 11:32
26
  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.

Chris Nolet
  • 7,969
  • 7
  • 59
  • 90
Chen_Wayne
  • 483
  • 5
  • 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) – Aran Mulholland Jul 28 '16 at 01: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 Sep 15 '17 at 17:33
  • Tks for the reference to my comment. I find it weird that my original comment was deleted from https://stackoverflow.com/a/1274447/475876 – Akira Yamamoto May 24 '21 at 01:46
22

Do the following steps serially,you will be fine.

1.remove the mistakenly added files from the directory/storage. You can use "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 gitignore file now and save it.

3.now remove them from git cache by using these commands (if there are 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, use these 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
Shamsul Arefin Sajib
  • 1,435
  • 15
  • 19
18

I think, that maybe git can't totally forget about 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 2 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 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 file is changed on remote, conflict will occure, when git pull. Stash won't work. To resolve it, copy file content to the safe place and follow these steps:

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

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

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

If everyone, who works with 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 file has been changed on remote, but can protect it from overwriting by remote content.

Boolean_Type
  • 953
  • 2
  • 12
  • 32
14

The copy/paste answer is git rm --cached -r .; git add .; git status

This command will ignore the files that have already been committed to a Git repository but now we have added them to .gitignore.

youhans
  • 3,623
  • 2
  • 22
  • 33
12

Do the following steps for file/folder:

Remove 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 test.txt file. I accidentally pushed to GitHub want to remove commands will be followed as:

1st add test.txt in .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 .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/dir. I accidentally pushed to GitHub want to remove commands will be followed as:

1st add .idea in .gitignore

git rm -r --cached .idea
git add .
git commit -m ".idea removed"
git push
Arslan Ahmad khan
  • 3,648
  • 1
  • 21
  • 31
10

The answer from Matt Fear was the most effective IMHO. The following is just a PowerShell script for those in windows to only remove files from their git repo 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 .
Ameer Deen
  • 668
  • 8
  • 19
10

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.

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

Skeets
  • 3,311
  • 31
  • 52
9

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.

Josh Lee
  • 149,877
  • 34
  • 253
  • 263
Apreche
  • 23,946
  • 8
  • 37
  • 46
  • 34
    `git rm --cached` will remove the file from the index without deleting it from disk, so no need to move/copy it away – bdonlan Aug 13 '09 at 19:56
5

The BFG is specifically designed for removing unwanted data like big files or passwords from Git repos, 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 generally much easier to use - check the full usage instructions and examples for more details.

Source: https://confluence.atlassian.com/bitbucket/reduce-repository-size-321848262.html

Meir Gerenstadt
  • 3,499
  • 1
  • 20
  • 20
5

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.

Peter T.
  • 2,143
  • 5
  • 24
  • 36
5

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 index
  • stage .gitignore and the files you just removed
  • commit
Jay Irvine
  • 175
  • 2
  • 10
4

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

The .gitignore 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 empty repo
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
Lloyd
  • 7,498
  • 2
  • 31
  • 52
  • 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 Mar 14 '19 at 15:26
3

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 does not delete anything from working directory (even when re-pulled from remote).

This method requires usage of /.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 re-write history and thus have significant ramifications for any public/shared/collaborative repos that might be pulled after this process. 2

General advice: start with a clean repo - 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 current index, not 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 devs that pull from now-modified remote repo 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 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 exists in the commit history, and clearly re-writing 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 standalone 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. Re-writing git history also changes commit hashes, which will wreak havoc on future pulls from public/shared/collaborative repos. Please understand the ramifications fully before doing this to such a repo. 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 repo are git update-index --assume-unchanged </path/file> or git update-index --skip-worktree <file>, examples of which can be found here.

Community
  • 1
  • 1
goofology
  • 625
  • 2
  • 8
  • 19
2

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!

2

Especially for the IDE based files, I use this:

For instance the slnx.sqlite, I just got rid off it completely like 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 stores 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 there are uncommitted changes.

curiousBoy
  • 4,733
  • 4
  • 39
  • 43
0

If anyone having hard time on Windows and you wanna ignore entire folder, 'cd' to desired the 'folder' and do 'Git Bash Here'.

git ls-files -z | xargs -0 git update-index --assume-unchanged
RajVimalC
  • 71
  • 5
0

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.

Hope this helps others!

DC.Skells
  • 530
  • 4
  • 14
0

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 info, you can refer to the man page of filter-branch here

source: https://docs.github.com/en/github/authenticating-to-github/removing-sensitive-data-from-a-repository#using-filter-branch

source: https://thomas-cokelaer.info/blog/2018/02/git-how-to-remove-a-big-file-wrongly-committed/

Khushhalm
  • 344
  • 5
  • 10
0

In my case I needed to put the .envrc file on the .gitignore file. and then I used the:

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

and the file was removed.

Then I committed again telling that file was removed.

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

Then I used this command:

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

And I don't see again the content.

rld
  • 1,519
  • 1
  • 18
  • 29