How to amend changes to your pull request?
Tags: git
Published: 2023-03-23 21:52:53

So you have just received feedback on your PR. You forgot a semi-colon at the end of a line. After you fixed it, your commit history looks like this:

```
5 (HEAD -> feature-branch) fix: add semi-colon (my bad)
4 add docs
3 write tests
2 implement feature
1 (master) base commit
```

_I know commit IDs are not integers. For the purpose of this article, you can assume they are. The base commit of your branch starts from 1, and continues to increase sequentially._

_Also, notice that commit history always starts from the most recent commit down to the oldest commit._

It is a best practice to maintain a neat commit history. But yours is cluttered by a small fix commit now.

To remove this clutter, you should amend the 4th commit into the 3rd. There are multiple ways of doing it. Rebasing the branch is one of them.

## WARNING: The dark side of rebasing

Before you go to rebasing a branch without reading the whole article, I must stop you. Rebasing modifies the commit history. When you push the rebased branch, your modified history (i.e. local copy) will replace the branch's current history residing in the upstream repository (i.e. GitHub). This is something you ABSOLUTELY don't want to do. Because next time anyone else pushes or pulls from the upstream repository, git will find mismatch in history and cause unexpected issues.

A general rule of thumb is that do not modify a branch if more than one person is working on it. Ideally, your pull request is coming from a branch from a forked repo owned by you. And, you are the only person working on that branch most of the cases. In this situation, rebasing that branch is not a problem.

## What rebasing does under the hood

When you rebase a branch, git will repeat the branch's history from the beginning (right after the base commit). It will remove all commits after 1, which are 2 to 5, from the commit history (not from the memory). Then, it will re-apply the commits from 2 to 5 sequentially. Before it starts re-applying, it will give you an opportunity to specify any modification you would like to make in the history. In your case, you want to specify that you want to squash the 5th commit into the 4th commit.

Let's start the rebasing process. If you are afraid of playing with rebasing in your work repository, you may init a sample test repository and recreate the commit history we are dealing with now.

## How to perform rebasing

Run the following command.
```
git rebase -i HEAD~2
```

Why `HEAD~2`? Simply put, the command will repeat the last two commits, instead of the entire branch. Because you know you are modifying the last two commits in this case. If you really want to know what `HEAD~2` means, it is a reference to the 3rd last commit (which is 3). The command is saying 'rebase all commits after the 3rd one into the 3rd one'. 

After you hit the command, git will open a text editor with the following content to give you a chance to specify the changes you want to make to the history of the last two commits. 
```
pick 4 add docs
pick 5 fix: add semi-color (my bad)
```
_Notice, this time git is showing the history from oldest to recent_

Modify the content by replacing `pick` by `squash` for the 5th commit. Save and close the text editor.
```
pick 4 add docs
squash 5 fix: add semi-color (my bad)
```

You will be given another prompt to specify new commit message for the 4th commit. It will show you both commit messages from 4th and 5th. Simply comment out the commit message of the 5th commit. Save and close the editor.

Git starts processing this commands from line 1. It re-applies the 4th commit. When it is time to re-apply the 5th commit, git sees the `squash` command. So, it skips creating the 5th commit. Instead it ammends to contents of the 5th commit into the 4th. Basically, the 5th commit is squashed but its contents are retained by amending to the previous commit.

Your job here is done!

## Effects of rebasing

If you now see the commit history, you will see that all rebased commits received a new commit ID.
```
4' (HEAD -> feature-branch) add docs
3  write tests
2  implement feature
1  (master) base commit
```

This change in commit ID is what causes mismatch with upstream commit history.

## Pushing the updated history

Now in your local commit history, the 'fix' commit is gone. And, it is time to update the upstream branch. 

Because your history is modified, you need to push with the `--force-with-lease` option.
```
git push --force-with-lease
```

As we have already discussed, don't force push if your branch has multiple people working on it.

If you know go to the pull request page, you will see that updated history is reflected there. Also, you will see the any reference to the rebased commits made in the comments became invalid. But, they are not very important.

I intentionally left out some details in this article. If you want me to expand, let me know in the comments.