0

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.

  • Possible duplicate of [How to revert a Git repository to a previous commit](https://stackoverflow.com/questions/4114095/how-to-revert-a-git-repository-to-a-previous-commit) – evolutionxbox May 28 '19 at 10:08
  • Possible duplicate of [How to sparsely checkout only one single file from a git repository?](https://stackoverflow.com/questions/2466735/how-to-sparsely-checkout-only-one-single-file-from-a-git-repository) – Ulysse BN May 28 '19 at 10:14

4 Answers4

2

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.

1

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.

Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
  • Thanks, it will work, but this solution doesn't clean commit history and 'git log 1.txt' will show, that 1.txt was modified in commits A, B, C and new one. Sorry, my fault, I had to add more details to problem description. I'll update it. – Vin-terfresh May 28 '19 at 10:26
  • If the other commits have changes to files other than `1.txt`, then if you were to remove those commits, you'd kill the other changes too. But, you generally don't want to remove commits. – Tim Biegeleisen May 28 '19 at 10:29
  • I understand it. I don't want to remove these commits at all, just modify them. So, I'm probably looking for solution with -filter-branch, but can't figure out, which arguments should I use. – Vin-terfresh May 28 '19 at 10:39
  • You also generally should _not_ modify the earlier commits, since that would mean rewriting history. This is usually bad if your branch could be shared by other people. Rather, just leave history as it happened, and change things going forward. – Tim Biegeleisen May 28 '19 at 11:09
0

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
foo

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

Ulysse BN
  • 10,116
  • 7
  • 54
  • 82
  • Thank you. Unfortunately, it's not the solution I'm looking for. I've updated problem description with new details. – Vin-terfresh May 28 '19 at 10:48
  • Then you should take a look at the `rebase -i` command to [rewrite history](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History). Here `git rebase -i :/foo` and stop for editing for each commit would do the trick. Or you could remove the file from commit history: https://stackoverflow.com/a/8741530/6320039. Or close and ask a new question I guess, since this one has already been answered – Ulysse BN May 28 '19 at 11:18
0

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.

Krantisinh
  • 1,579
  • 13
  • 16