I have a repo with three files in it - 1.txt, 2.txt and 3.txt. I created a branch 'new_branch' and made commits (let's call them A, B and C), every of these commits contains changes in several files from my repo. Then I understood, that 1.txt was updated mistakenly. Is it possible to remove changes, made in 1.txt, from commits A, B and C but keep changes in 2.txt and 3.txt?

UPD. I want also to remove changes, made it 1.txt, from commit history. As result 'git log -p' should show, that only 2.txt and 3.txt were modified in commits A, B and C.

Many thanks.

4 Answers

Piotr Broda On Best Solutions

Assuming that your revision tree looks like this:

-- S <s.sha> -- A -- B -- C (HEAD)

You can use git filter-branch to remove the changes from commits A..C made to a single file.

git filter-branch --tree-filter 'git checkout <s.sha> -- 1.txt' HEAD~3..

This will:

  • check out each of the last three commits,
  • run the 'git checkout' for the 1.txt file bringing it to the version you want,
  • put the modified commits back on the tree.

This obviously rewrites history, so the same considerations are in place as for pushing a rebase.

Ulysse BN On

git checkout <commit reference> <file> is what you are looking for.

> git init
Initialized empty Git repository in /Users/ody/temp/git-co/.git/
> touch 1 2 3
> echo foo > 1 > 2 > 3
> git add 1 2 3
> git commit -m "foo"
[master (root-commit) 470f4e8] foo
 3 files changed, 3 insertions(+)
 create mode 100644 1
 create mode 100644 2
 create mode 100644 3
> echo bar > 1 > 2 > 3
> git add 1 2 3
> git commit -m "bar"  
[master 916819d] bar
 3 files changed, 3 insertions(+), 3 deletions(-)
> git checkout :/foo 1
> cat 1

NOTE: The :/ notation lets you reference a commit by a part of its name instead of a commit hash.

Tim Biegeleisen On

You may try checking out 1.txt at the commit before commit A, the first commit where you started making unwanted changes:

git checkout abc123 -- 1.txt

This assumes your branch structure looks like this (with commit S having SHA-1 hash abc123):

S -- A -- B -- C

Then, just add the change and commit.

Krantisinh On

Here's another not so neat way [Solution provided by Tim best suits the situation though]-

Assuming your top 3 commits are A,B,C, You can do git reset --soft HEAD~3. All your committed changes will come in your staging area. You can unstage 1.txt and commit rest of the files.

This will lead to change in history with commits A, B and C to be replaced by a new commit D that will have all changes in A, B and C except for the file 1.txt.