Skip to content

Workflow: Editing an old commit's contents

kain88-de edited this page May 4, 2024 · 67 revisions

Description

Sometimes, you may want to edit the contents of an existing commit. For example, if you're working on a commit stack, and you received code review feedback on a previous commit, then you may want to update it.

Suppose for this article that we're working on C in the following commit graph, and we want to update A:

$ git sl
⋮
◇ e6adfe90 19s (main) Main branch commit
┃
◯ 2d070a71 14s A
┃
◯ 7ebf9885 14s B
┃
● 32d90336 14s (feature) C

Approaches

Amend the commit directly

The easiest approach is to check out the commit directly, and then amend it:

$ git checkout 2d070a71
# do some work...

$ git commit --amend
...
branchless: This operation abandoned 1 commit!
branchless: Consider running one of the following:
branchless:   - git restack: re-apply the abandoned commits/branches
branchless:     (this is most likely what you want to do)
branchless:   - git smartlog: assess the situation
branchless:   - git hide [<commit>...]: hide the commits from the smartlog
branchless:   - git undo: undo the operation
branchless:   - git config branchless.restack.warnAbandoned false: suppress this message
...

Since you've updated a commit earlier in the stack, its descendant commits were abandoned:

$ git sl
⋮
◇ e6adfe90 2m (main) Main branch commit
┣━┓
┃ ✕ 2d070a71 2m (rewritten as dc95606b) A
┃ ┃
┃ ◯ 7ebf9885 2m B
┃ ┃
┃ ◯ 32d90336 2m (feature) C
┃
● dc95606b 42s A

To fix this, run git restack:

$ git restack
$ git sl
⋮
◇ e6adfe90 3m (main) Main branch commit
┃
● dc95606b 1m A
┃
◯ 14722c98 3s B
┃
◯ 1c5eba43 3s (feature) C

Make additional child commits

If you don't want to immediately amend the original commit (for example, if you're not immediately sure that your change addresses the feedback), then you may want to make an additional child commit instead:

$ git checkout 2d070a71
# do some work...

$ git commit

Now your graph will look like this:

$ git sl
⋮
◇ e6adfe90 6m (main) Main branch commit
┃
◯ 2d070a71 6m A
┣━┓
┃ ◯ 7ebf9885 6m B
┃ ┃
┃ ◯ 32d90336 6m (feature) C
┃
● ca7323c7 3s temp: address feedback

Once you've confirmed that this commit is suitable, you can combine it into its parent commit with git rebase -i:

$ git rebase -i main

The default rebase plan will look like this:

pick 2d070a7 A
pick ca7323c temp: address feedback

Change the second pick to fixup:

pick 2d070a7 A
fixup ca7323c temp: address feedback

Then execute the rebase:

...
branchless: This operation abandoned 1 commit!
branchless: Consider running one of the following:
branchless:   - git restack: re-apply the abandoned commits/branches
branchless:     (this is most likely what you want to do)
branchless:   - git smartlog: assess the situation
branchless:   - git hide [<commit>...]: hide the commits from the smartlog
branchless:   - git undo: undo the operation
branchless:   - git config branchless.restack.warnAbandoned false: suppress this message
Successfully rebased and updated detached HEAD.
...

Like the previous case, this results in the descendant commits being abandoned:

⋮
◇ e6adfe90 8m (main) Main branch commit
┣━┓
┃ ✕ 2d070a71 8m (rewritten as 8b8b6ee3) A
┃ ┃
┃ ◯ 7ebf9885 8m B
┃ ┃
┃ ◯ 32d90336 8m (feature) C
┃
● 8b8b6ee3 40s A

So we can fix it with git restack:

$ git restack
$ git sl
⋮
◇ e6adfe90 9m (main) Main branch commit
┃
● 8b8b6ee3 1m A
┃
◯ 6ddb67d6 1s B
┃
◯ 8ffc6f49 1s (feature) C

Commute a patch upward

If you don't want to check out commit A, and you know that you can make a patch on top of your current commit C without it conflicting with either of B or C, then you can make the patch and commute (change the order of) the patch upwards in the stack. You might want to do this e.g. if you have build artifacts which you don't want to invalidate.

Detach from the branch you have checked out (if any) and then make your commit:

$ git checkout --detach
$ git commit
$ git sl
⋮
◇ e6adfe90 14m (main) Main branch commit
┃
◯ 2d070a71 14m A
┃
◯ 7ebf9885 14m B
┃
◯ 32d90336 14m (feature) C
┃
● 21753de2 1s temp: address feedback for A

Next, run git rebase -i to see this rebase plan:

$ git rebase -i main
...
pick 2d070a7 A
pick 7ebf988 B
pick 32d9033 C
pick 21753de temp: address feedback for A

Move the last line to immediately after the first line, and change it to fixup:

pick 2d070a7 A
fixup 21753de temp: address feedback for A
pick 7ebf988 B
pick 32d9033 C

Then execute the rebase plan. Note that if your patch conflicts with B or C, then you'll have to resolve merge conflicts as a part of this step.

...
branchless: This operation abandoned 1 branch (feature)!
branchless: Consider running one of the following:
branchless:   - git restack: re-apply the abandoned commits/branches
branchless:     (this is most likely what you want to do)
branchless:   - git smartlog: assess the situation
branchless:   - git hide [<commit>...]: hide the commits from the smartlog
branchless:   - git undo: undo the operation
branchless:   - git config branchless.restack.warnAbandoned false: suppress this message
Successfully rebased and updated detached HEAD.

The branch feature was abandoned:

$ git sl
⋮
◇ e6adfe90 16m (main) Main branch commit
┣━┓
┃ ✕ 2d070a71 16m (rewritten as d4919de2) A
┃ ┃
┃ ✕ 7ebf9885 16m (rewritten as 902205ea) B
┃ ┃
┃ ✕ 32d90336 16m (rewritten as 394cdfb2) (feature) C
┃
◯ d4919de2 34s A
┃
◯ 902205ea 34s B
┃
● 394cdfb2 34s C

So run git restack to fix it:

$ git sl
⋮
◇ e6adfe90 16m (main) Main branch commit
┃
◯ d4919de2 49s A
┃
◯ 902205ea 49s B
┃
● 394cdfb2 49s (feature) C
Clone this wiki locally