48

When I started my git repo I commited a few files as initial commit to it. Now, many commits later, I noticed that I included in those files a line with information which I do not want to publish (unlike the rest of the code). So I want to remove/change this one line and keep the rest of the code.

Searching around I found this solution: Insert an empty commit as initial commit (described here: Insert a commit before the root commit in Git?), do a rebase on it and then edit the old first commit via amend. Unfortunately, many cruel merge conflicts arise during rebase (as described here: git: solving conflicts caused by rebase).

Is there a different way to solve my problem or do have to rebase and edit all conflicts by hand?

Thanks in advance :)

tbolender
  • 1,172
  • 3
  • 14
  • 19

4 Answers4

57

Here's a command that will remove an offending line from a file's history in all your branches:

git filter-branch --tree-filter 'sed -i "/sensitive information/ d" filename' -- --all

This checks out every revision, runs the sed command on it, then commits that revision back.

The sed command in this case matches any lines containing the pattern sensitive information in the file named filename and deletes those lines.

Note: it's good if you have a backup, and try the sed script on its own first to make sure it's doing what you want, because it can take quite a while to run on a long history.

Karl Bielefeldt
  • 47,314
  • 10
  • 60
  • 94
  • Is there any possibility to ignore a sed error (e.g. when a file does not exist in one revision)? – tbolender Aug 27 '11 at 16:19
  • 3
    I don't know if the `-q` option does that or not. You might have to wrap it in a shell if-then, like `if [ -e file ]; then sed...; fi` – Karl Bielefeldt Aug 27 '11 at 17:52
  • 4
    If you know the commit the mistake was introduced, you can `git filter-branch --tree-filter 'sed -i "/sensitive information/ d" filename' ..HEAD` – exclsr Sep 17 '14 at 18:44
  • 2
    On windows I kept getting an error 'No such file or directory'. I eventually found out that using the full file path for the filename (eg: c:\filename.txt) and putting it in double quotes make it work. – Sevin7 Mar 28 '16 at 13:45
  • 7
    And with a slightly modified `sed` command+args I used on macOS: `git filter-branch -f --tree-filter 'test -f yourfile.json && sed -i "" "s/oldText/newText/g" yourfile.json || echo "skipping file"' -- --all` – Alon Amir May 17 '17 at 15:16
  • 1
    I had to use `--force` on the `filter-branch` command, otherwise the changes didn't appear to apply. GitHub [recommends](https://help.github.com/articles/removing-sensitive-data-from-a-repository/) it too. – dimo414 Jan 05 '18 at 21:27
8

I made this command that doesn't error out when a file doesn't exist:

 filename=./path/to/your/filename
 filter=your_regex_here

 git filter-branch --tree-filter 'test -f $filename && sed -i.bak "/$filter/d" $filename  || echo “skipping file“' -- --all
Ted Yavuzkurt
  • 517
  • 4
  • 10
  • Worked great for me. Thought I'd note in case anyone else runs into this -- don't remove the echo "skipping file" at the end. Otherwise you might get the error "tree filter failed"; since the last return code is non-zero git thinks there was a failure. – moltenform Jan 14 '19 at 00:08
5

My answer combines the best of everything. It replaces the just the given filter string instead of the whole line and it nicely skips if the file is not found in a commit. There are a lot of escaped quotes but the other answer had really weird quotes that didn't work for me.

 git filter-branch --tree-filter "test -f \"$filename\" && sed -i \"s/$filter//g\" \"$filename\" || echo \"skipping $filename\"" -- --all
Jacob Kern
  • 165
  • 3
  • 12
2

You can have a look at git filter-branch

The examples in the link should get you going: http://git-scm.com/docs/git-filter-branch

manojlds
  • 290,304
  • 63
  • 469
  • 417
  • 2
    I one found a solution for removing a complete file, but not for changing one line in it. Can give an example? – tbolender Aug 25 '11 at 19:09