786

I have changed several things over the last hour and committed them step by step, but I just realized I've forgot to add a changed file some commits ago (for example, the commit marked as a0865... below).

The Log looks like this:

GIT TidyUpRequests u:1 d:0> git log 
commit fc6734b6351f6c36a587dba6dbd9d5efa30c09ce 
Author: David Klein <> 
Date: Tue Apr 27 09:43:55 2010 +0200
 The Main program now tests both Webservices at once
commit 8a2c6014c2b035e37aebd310a6393a1ecb39f463 
Author: David Klein <>
Date: Tue Apr 27 09:43:27 2010 +0200
 ISBNDBQueryHandler now uses the XPath functions from XPath.fs too
commit 06a504e277fd98d97eed4dad22dfa5933d81451f 
Author: David Klein <> 
Date: Tue Apr 27 09:30:34 2010 +0200
 AmazonQueryHandler now uses the XPath Helper functions defined in XPath.fs
commit a0865e28be35a3011d0b6091819ec32922dd2dd8 <--- changed file should go here
Author: David Klein <> 
Date: Tue Apr 27 09:29:53 2010 +0200
 Factored out some common XPath Operations

Any ideas?

Guy Avraham
3,7503 gold badges42 silver badges54 bronze badges
asked Apr 27, 2010 at 7:59
1

8 Answers 8

1223

Use git rebase. Specifically:

  1. Use git stash to store the changes you want to add.
  2. Use git rebase -i HEAD~10 (or however many commits back you want to see). Alternatively, use git rebase -i commitHash^. Replace commitHash with the hash of the commit you want to edit, which you can get by git log
  3. Mark the commit in question (a0865...) for edit by changing the word pick at the start of the line into edit. Don't delete the other lines as that would delete the commits.[^vimnote]
  4. Save the rebase file, and git will drop back to the shell and wait for you to fix that commit.
  5. Pop the stash by using git stash pop.
  6. Add your file with git add <file>.
  7. Amend the commit with git commit --amend --no-edit.
  8. Do a git rebase --continue which will rewrite the rest of your commits against the new one.
  9. Repeat from step 2 onwards if you have marked more than one commit for edit.
  10. If you have previously pushed the modified commits anywhere else, then you will have to push --force again to update them on the remote. However, the usual warnings about using --force apply, and you can easily lose other people's work if you are not careful and coordinate with them beforehand.

[^vimnote]: If you are using vim then you will have to hit the Insert key to edit, then Esc and type in :wq to save the file, quit the editor, and apply the changes. Alternatively, you can configure a user-friendly git commit editor with git config --global core.editor "nano".

answered Apr 27, 2010 at 8:10
13
  • 24
    What if you have unstaged changes which you want to add to the edit? If I stash them, I couldn't git add. Commented Jun 5, 2013 at 1:35
  • 20
    Sam you can simply unstash the changes while on the commit in question, it will work fine. Commented Sep 12, 2013 at 12:13
  • 21
    Note: When you mark the commit with edit, DO NOT DELETE the other commits listed in the file. If you do, the commits will be deleted and you'll have to follow these steps to get them back. Commented Dec 4, 2013 at 17:21
  • 7
    Regarding what @DavidTuite said, a habit of mine when doing stuff on git that I'm not sure how will turn out is creating a "branchname-ref" branch to save the current state of the timeline in case I screw things up. When I'm done, I delete it. Commented Apr 29, 2016 at 22:13
  • 5
    Note that the --no-edit is optional and will keep your existing commit message. If you intend to update the original commit message, leave that parameter out and you'll be dropped into another editor view to change it. Also, your working directory must be clean before you can git rebase --continue. So if you're only adding one portion of code from your stash, you'll want to re-stash before continuing the rebase (i.e. between steps 7 & 8). Commented Dec 13, 2019 at 15:55
719
+50

To "fix" an old commit with a small change, without changing the commit message of the old commit, where OLDCOMMIT is something like 091b73a:

git add <my fixed files>
git commit --fixup=OLDCOMMIT
git rebase --interactive --autosquash OLDCOMMIT^

You can also use git commit --squash=OLDCOMMIT to edit the old commit message during rebase.

See documentation for git commit and git rebase. As always, when rewriting git history, you should only fixup or squash commits you have not yet published to anyone else (including random internet users and build servers).


Detailed explanation

  • git commit --fixup=OLDCOMMIT copies the OLDCOMMIT commit message and automatically prefixes fixup! so it can be put in the correct order during interactive rebase. (--squash=OLDCOMMIT does the same but prefixes squash!.)
  • git rebase --interactive will bring up a text editor (which can be configured) to confirm (or edit) the rebase instruction sequence. There is info for rebase instruction changes in the file; just save and quit the editor (:wq in vim) to continue with the rebase.
  • --autosquash will automatically put any --fixup=OLDCOMMIT commits in the correct order. Note that --autosquash is only valid when the --interactive option is used.
  • The ^ in OLDCOMMIT^ means it's a reference to the commit just before OLDCOMMIT. (OLDCOMMIT^ is the first parent of OLDCOMMIT.)

Optional automation

The above steps are good for verification and/or modifying the rebase instruction sequence, but it's also possible to skip/automate the interactive rebase text editor by:

answered Dec 31, 2014 at 12:17
17
  • 3
    Terrific answer. When I executed the final rebase command, it opened vim to allow me to edit the commit message, which i didn't need to do. Is there a way to prevent that from happening? Commented Oct 21, 2016 at 4:58
  • 13
    @Jonah: the editor isn't opened to edit the commit message, but to confirm (or edit) the rebase steps. It cannot be avoided; --autosquash is only valid when the --interactive option is used. Commented Oct 21, 2016 at 11:01
  • 8
    Using this solution I am stuck git showing up VIM. My problem is, I don't know how to use VIM. How the heck do I get out of it, how can I control this thing, this so confusing. Commented Oct 30, 2016 at 13:30
  • 3
    @NeonWarge: the editor of choice is configurable using for example git config --global core.editor "pico". There are several other ways to configure git and/or change your system's default editor etcetera. Commented Oct 30, 2016 at 17:12
  • 3
    one line nice alias here Commented Apr 27, 2018 at 13:06
72

with git 1.7, there's a really easy way using git rebase:

stage your files:

git add $files

create a new commit and re-use commit message of your "broken" commit

git commit -c master~4

prepend fixup! in the subject line (or squash! if you want to edit commit (message)):

fixup! Factored out some common XPath Operations

use git rebase -i --autosquash to fixup your commit

answered Apr 27, 2010 at 8:13
8
  • 3
    +1. Nice use of the new fixup directive (1.7+): stackoverflow.com/questions/2302736/trimming-git-checkins/… Commented Apr 27, 2010 at 8:15
  • @knittl I was trying out your method to add another file to an old commit of mine (not pushed) but when rebasing I get You asked me to rebase without telling me which branch you want to rebase against, and 'branch.master.merge' and if I then use git rebase -i --autosquash I just get a noop subject line, rebasing the commit onto itself. Any idea what I do wrong? Commented Mar 1, 2012 at 16:24
  • 9
    @oschrenk: You need to provide a commit to which you want to rebase, e.g. git rebase -i --autosquash HEAD~10 Commented Mar 1, 2012 at 16:42
  • 1
    Good answer but I also needed to add a commit against which to rebase. Would be great if you could update it. Commented Mar 8, 2013 at 9:44
  • @PaulOdeon: I don't understand your question. What are you trying to do, and where are you having problems? Commented Mar 8, 2013 at 16:23
16

You can try a rebase --interactive session to amend your old commit (provided you did not already push those commits to another repo).

Sometimes the thing fixed in b.2. cannot be amended to the not-quite perfect commit it fixes, because that commit is buried deeply in a patch series.
That is exactly what interactive rebase is for: use it after plenty of "a"s and "b"s, by rearranging and editing commits, and squashing multiple commits into one.

Start it with the last commit you want to retain as-is:

git rebase -i <after-this-commit>

An editor will be fired up with all the commits in your current branch (ignoring merge commits), which come after the given commit.
You can reorder the commits in this list to your heart's content, and you can remove them. The list looks more or less like this:

pick deadbee The oneline of this commit
pick fa1afe1 The oneline of the next commit
...

The oneline descriptions are purely for your pleasure; git rebase will not look at them but at the commit names ("deadbee" and "fa1afe1" in this example), so do not delete or edit the names.

By replacing the command "pick" with the command "edit", you can tell git rebase to stop after applying that commit, so that you can edit the files and/or the commit message, amend the commit, and continue rebasing.

answered Apr 27, 2010 at 8:09
2

Here's a function that implements @Greg's answer:

function gitamendoldcommit() {
 printf "\n\nPress any key if you have no changes other than those you want to add to 1ドル."
 printf "(You can commit your unwanted changes and undo later.):\n\n"
 read foo
 printf "\n\nChange 'pick' to 'edit' in front of 1ドル (top one), save, close the editor & press any key..."
 printf "Resolve any possible conflicts, if any. then: git add .; git rebase --continue\n\n"
 git rebase -i 1ドル^
 git stash pop
 git add .
 git commit --amend --no-edit
 git rebase --continue
}

Usage: (while having only the intended changes in the staging) gitamendoldcommit $OLDCOMMIT

answered Apr 20, 2021 at 5:17
2

Aditionally, if you use git rebase -i and want to go to the first commit of your current branch you can use git rebase -i --root. Now you could easily modify your first commit.

answered Apr 28, 2021 at 7:36
1
  • Great, It returns a list of commits and the developer can edit the one hi/she wants to modify. Thanks. Commented Sep 26, 2024 at 9:41
0

Based on the marked answer the best way to do file changes on an old commit, not the previous one, is git rebase interactive. But instead of using HEAD~NUMBER I prefer to use hash commits. So my answer is like the following example, I want to change the getRandomArbitrary.ts which I wrote many commits ago:

  1. git log: I run this command and then find the commit which I want to edit, and get the previous hash commit:

    git log

  2. git rebase -i COPIED_HASH_COMMIT: By running this command, Git will show me a list of commit messages, I modify the one I want to edit, only change the pick word to edit and save the list:

    git rebase interactive

  3. Now it's time to edit. The git history goes back to when the getRandomArbitrary.ts file was generated (it's like a time machine - change the future 😁)

  4. After the modification, I should save the git history and turn it back to the latest, by running git add <file> and then git rebase --continue it does exactly what I want.

HINT: I changed the git history so that up-stream will reject git push. I must run git push -f to do a force push (only this time).

answered Sep 26, 2024 at 9:37
0
-1

In 2023 there are tools that do this: specifically git absorb. It is a great tool with the potential to change a lot of dusty git workflows.

answered Jan 3, 2023 at 16:53

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.