0

I would like to modify a git commit which I pushed long time ago. Is there any simple and straightforward way to do that?

I have made a lot of changes since then, and it is very confusing and risky for me to do it with git rebase.

I can still use git rebase but I am very new for git usage.

The commit involves the first file of my repository, and I am not allowed to create a new commit for changing a file which I pushed with my first commit.

j08691
  • 204,283
  • 31
  • 260
  • 272
yusuf
  • 3,591
  • 8
  • 45
  • 86
  • 1
    `git commit --amend` [Changing the Last Commit](https://www.atlassian.com/git/tutorials/rewriting-history) – Alexan Mar 02 '20 at 01:47
  • Hi @matt, basically, I have pushed a file with my first commit. After that I have done a lot of changes in the repository. Now I would like to go back to my first commit, and modify the file in that specific commit, but not making a new commit for changing that file. – yusuf Mar 02 '20 at 01:54
  • 1
    https://stackoverflow.com/q/2246208/240564 or https://stackoverflow.com/a/2119656/240564 – Alexan Mar 02 '20 at 01:55
  • Hi @matt, I must do this way. There must be a way to do it. – yusuf Mar 02 '20 at 02:09
  • @matt, would it be possible to create another commit, and replace with that commit? – yusuf Mar 02 '20 at 02:11
  • Forgive me for misclarification. What I want to do is, changing a specific file content, and replace with that commit. At the end, when you look at the history chain, the previous commit will disappear somehow, and this new commit with changes will appear there. – yusuf Mar 02 '20 at 02:14
  • 1
    Okay, so do this https://stackoverflow.com/questions/32315156/how-to-inject-a-commit-between-some-two-arbitrary-commits-in-the-past to insert the change commit after the problem commit. Then interactive rebase to squash the inserted commit to the problem commit. – matt Mar 02 '20 at 05:02
  • Please don't try to learn Git by trial and error. Don't try to compare it to other SCMs (like SVN) and to apply the same workflows. Read chapters 2 and 3 of "Pro Git" book. Just these 2 chapters and you'll have much better understanding of how to (and not to) work with Git. That will also help you to avoid XY-Problem that may be present here. You are focused on modifying some old commit, but you did not explain why you need to do that, it's possible that your actual goal is achieved easier by other means. – Paul Mar 02 '20 at 05:48
  • Note that, technically speaking, you cannot change *any* commit. What you are going to do, however you do it—see VonC's answer for git filter-repo for instance—is make a *whole new series of commits* starting from the first one that has the file that you want to pretend was different a year ago. Every commit after that must also be copied to a new-and-improved version. Then you simply *stop using* the original commits, switching to these new-and-improved commits instead. – torek Mar 02 '20 at 08:34
  • I say "simply", and if you're the only person with this repository, it really is that simple—but if there are multiple copies of the repository, the other ones will keep wanting to re-introduce the old commits. – torek Mar 02 '20 at 08:35

2 Answers2

1

What I want to do is, changing a specific file content,

Instead of an interactive rebase, use git filter-repo, which replaces BFG or filter-branch.

Use content filtering

At the end, you can (if you are the only one working on that repository) do a git push --force

If you want to modify file contents, you can do so based on a list of expressions in a file, one per line.
For example, with a file named expressions.txt containing:

p455w0rd
foo==>bar
glob:*666*==>
regex:\bdriver\b==>pilot
literal:MM/DD/YYYY=>YYYY-MM-DD
regex:([0-9]{2})/([0-9]{2})/([0-9]{4})==>\3-\1-\2

then running

git filter-repo --replace-text expressions.txt

will go through and replace:

  • p455w0rd with ***REMOVED***,
  • foo with bar,
  • any line containing 666 with a blank line,
  • the word driver with pilot (but not if it has letters before or after; e.g. drivers will be unmodified),
  • the exact text MM/DD/YYYY with YYYY-MM-DD and
  • date strings of the form MM/DD/YYYY with ones of the form YYYY-MM-DD.
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
0

Now that you've granted that we are allowed to make new commits to achieve the desired effect, the easy way to do this is: Do what it says at How to inject a commit between some two arbitrary commits in the past? to insert the change commit after the problem commit. Then interactive rebase to squash the inserted commit to the problem commit.

I'll demonstrate:

humlet:gittest matt$ git init
humlet:gittest matt$ echo test > dummy.txt
humlet:gittest matt$ git add .
humlet:gittest matt$ git commit -m "start"
humlet:gittest matt$ echo crucial > crucial.txt
humlet:gittest matt$ git add .
humlet:gittest matt$ git commit -m "created crucial file"
humlet:gittest matt$ echo test2 > dummy.txt
humlet:gittest matt$ git commit -am "changed dummy"
humlet:gittest matt$ echo test3 > dummy.txt
humlet:gittest matt$ git commit -am "changed dummy again"

git log shows that the commits are

    changed dummy again
    changed dummy
    created crucial file
    start

Okay, so the goal is to change what is in crucial.txt and put that commit in place of the "created crucial file" commit. git log shows that that commit is 785f75. Here we go:

humlet:gittest matt$ git branch temp 785f75
humlet:gittest matt$ git checkout temp
humlet:gittest matt$ echo whoa > crucial.txt
humlet:gittest matt$ git commit -am "teehee"
humlet:gittest matt$ git rebase temp master

We've now inserted a commit ("teehee") containing the revised crucial.txt, but we have one too many commits:

    changed dummy again
    changed dummy
    teehee
    created crucial file
    start

So now we go back and squash "teehee" onto "created crucial file". To do so, we do an interactive rebase starting at the commit preceding both of them, which is "start"; git log shows that that is e01e0c5:

humlet:gittest matt$ git rebase -i e01e0c5

In the interactive rebase, we pick all the commits we are shown, except that we squash "teehee". When we are offered a chance to revise the commit message, we change it to "created crucial file". git log shows that we now have

    changed dummy again
    changed dummy
    created crucial file
    start

Okay, now let's check what's in the "created crucial file" commit:

humlet:gittest matt$ git diff 65c198 e01e0c
diff --git a/crucial.txt b/crucial.txt
deleted file mode 100644
index 0a6a129..0000000
--- a/crucial.txt
+++ /dev/null
@@ -1 +0,0 @@
-whoa

So, you see, it looks like "created crucial file" added crucial.txt and it consisted of "whoa". That is the desired result.

Disclaimer: Changing history that you have pushed is wrong. Don't do it.

matt
  • 515,959
  • 87
  • 875
  • 1,141