Skip to content

Commit

Permalink
Merge pull request #120 from ns-rse/ns-rse/git-amend-fixup
Browse files Browse the repository at this point in the history
  • Loading branch information
ns-rse committed Mar 7, 2024
2 parents c2dac53 + c2c9821 commit 9e70887
Showing 1 changed file with 219 additions and 0 deletions.
219 changes: 219 additions & 0 deletions posts/git-amend-fixup/index.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
---
title: "Git - Making Amends and Fixing things up"
date: "2024-03-08"
categories: [git, amend, fixup]
image: https://live.staticflickr.com/65535/53448512934_c045c232c9_k.jpg
from: markdown+emoji
toc: true
toc-depth: 4
toc-location: right
execute:
code_fold: true
code_link: true
code_tools: true
fig-cap-location: top
tbl-cap-location: top
warning: false
---

[Git][git] is the world's most popular version control software out there at the moment and if you write code and
version control it the chances are you are using it. It's a complex tool though and there are a bewildering array of
options. In this short post we will look at some options for changing commits that have been made.

![[Crown Shy Trees by Me](https://www.flickr.com/photos/slackline/53448512934/in/datetaken/)](https://live.staticflickr.com/65535/53448512934_c045c232c9_k.jpg)

## Python Examples

We'll use my [pytest-examples](https://github.com/ns-rse/pytest-examples) as an example for this work but you can do
this in any repository you have going. We'll clone the repository and make a new branch called `amend-fixup-tutorial`.

``` bash
git clone git@github.com:ns-rse/pytest-examples.git
cd pytest-examples
git switch -c amend-fixup-tutorial
Switched to a new branch 'amend-fixup-tutorial'
```

## The Git Dance

A typical Git work flow involves making some changes to one or more files (or adding a new one into the
repository). These are staged with `git add <files>`, or you can use a shortcut `git add -u/--update` to add all
currently tracked files that have been modified if you are not adding anything new, before committing them with a
message using `git commit -m "<meaningful message about content being added>"`.

Lets add a simple `CONTRIBUTING.md` file to the repository.

``` bash
echo "# Contributing\n\nContributions via pull requests are welcome." > CONTRIBUTING.md
git add CONTRIBUTING.md
git commit -m "Adding CONTRIBUTING.md"
```

``` bash
git log --oneline
01191a2 (HEAD -> amend-fixup-tutorial) Adding CONTRIBUTING.md
```

This should be familiar to users of [Git][git] whether you use the command line interface (CLI), [Emacs'][emacs] amazing
Git porcelain [magit][magit] or any other tool such as [GitKraken][gitkraken] or the support in you IDE such as
[RStudio][rstudio] or VSCode.

## Making Amends

Sometimes you will have made a commit and you realise that you want to add more to it or perhaps you forgot to run your
test suite and find that on running it your tests fail so you need to make a correction. In this example we want to be
more explicit about how to make contributions and let people know they should fork the branch.

``` bash
echo "\n Please make a fork of this repository, make your changes and open a Pull Request." >> CONTRIBUTING.md
```

Now you could make a second commit...

``` bash
git add -u
git commit -m "Ask for PRs via fork in CONTRIBUTING.md"
```

``` bash
git log --oneline
9f0655b (HEAD -> amend-fixup-tutorial) Ask for PRs via fork in CONTRIBUTING.md
01191a2 Adding CONTRIBUTING.md
```
...and there is nothing wrong with that. However, Git history can get long and complicated when there are lots of small
commits, because these two changes to `CONTRIBUTING.md` are essentially the same piece of work and if we'd been thinking
clearly we would have written about making forks in the first place and made a single commit.
Fortunately Git can help here as there is the `git commit --amend` option which adds the staged changes to the last
commit and allows you to edit the last commit message (if nothing is currently staged then you will be prompted to edit
the last commit message). We can undo the last commit using `git reset HEAD~1` and instead amend the first commit that
added the `CONTRIBUTING.md`
``` bash
git add -u
git commit --amend
```
``` bash
git log --oneline
4fda15f (HEAD -> amend-fixup-tutorial) Adding CONTRIBUTING.md
cat CONTRIBUTING.md
# Contributing
Contributions via pull requests are welcome.
Please make a fork of this repository, make your changes and open a Pull Request.
```
We now have one commit which contains the new `CONTRIBUTING.md` file that contains all the changes we wished to have
in the file in the first place and our Git history is slightly more compact.
## Fixing things up
Amending commits is great providing the commit you want to change is the last commit you made (i.e. `HEAD`). But
sometimes you might wish to correct a commit further back in your history and `git commit --amend` is of no use
here. Git can however help here with the `git commit --fixup` command which allows you to mark a commit as being a "fix
up" of an older commit. These can then be autosquashed via an interactive Git rebase.
Let's add a few empty commits to our `amend-fixup-tutorial` branch to so we can do this.
``` bash
git commit --allow-empty -m "Empty commit for demonstration purposes"
git commit --allow-empty -m "Another empty commit for demonstration purposes"
```
```bash
git log --oneline
8061221 (HEAD -> amend-fixup-tutorial) Another empty commit for demonstration purposes
65587ce Empty commit for demonstration purposes
4fda15f Adding CONTRIBUTING.md
```
And let's expand our `CONTRIBUTING.md` file further.
``` bash
echo "\nPlease note this repository uses [pre-commit](https://pre-commit.com) to lint the Python code and Markdown files." >> CONTRIBUTING.md
```
We want to merge this commit with the first one we made in this tutorial using `git commit --fixup`. To do this we need
to know the hash (`4fda15f` see output from above `git log`) or the relative reference of the commit we want which in
this case is `HEAD~2` as it is three commits back from the current `HEAD` (which is commit `0`, most indexing in
computing starts at `0` rather than `1`). Use _one_ for the following `git commit --fixup` commands (adjusting the hash
to yours if you are using that option, you can find this using `git log --oneline`).
``` bash
git add -u
git commit --fixup 4fda15f
git commit --fixup HEAD~2
```
We see the commit we have just made starts with `fixup!` and is then followed by the commit message that it is fixing.
```bash
git log --oneline
97711a4 (HEAD -> amend-fixup-tutorial) fixup! Adding CONTRIBUTING.md
8061221 Another empty commit for demonstration purposes
65587ce Empty commit for demonstration purposes
4fda15f Adding CONTRIBUTING.md
```
The final step is to perform the automatic squashing via an interactive rebase, again you can either use the hash or the
relative reference.
``` bash
git rebase -i --autosquash 4fda15f
git rebase -i --autosquash HEAD~2
```
This will open the default editor and because the `--autosquash` option has been used it will already have marked the
commits that need combining with `fixup`. All you have to do is save the file and exit and we can check the history and
look at the contents of the file.
**NB** If you find that the necessary commit _isn't_ already marked navigate to that line and delete `pick`. The lines
below the file you have open give instructions on how you can mark commits for different actions, in this case you can
replace `pick` with either `f` or `fixup`. Save and exit and the commits are squashed.
```bash
git log --oneline
0fda21e (HEAD -> amend-fixup-tutorial) Another empty commit for demonstration purposes
65587ce Empty commit for demonstration purposes
4fda15f Adding CONTRIBUTING.md
cat CONTRIBUTING.md
# Contributing
Contributions via pull requests are welcome.
Please make a fork of this repository, make your changes and open a Pull Request.
Please note this repository uses [pre-commit](https://pre-commit.com) to lint the Python code and Markdown files.
```
And you're all done! If you were doing this for real on a repository you could now `git push` or continue your work. As
this was just an example we can switch branches back to `main` and force deletion of the branch we created.
``` bash
git switch main
git branch -D amend-fixup-tutorial
```
## Conclusion
Git has lots of commands to help you maintain a clean history by using `--amend` and `--fixup` flags to `git commit` and
in the later case then performing an interactive `git rebase -i`. This takes a little discipline to get into the
practice of but once in the habit of doing so it greatly improves the readability of the Git history and avoids
including commit messages such as `Fixing typo` / `Linting code` / `Fixing tests` / `I've gone mad!`.
If all of this sounds completely unfamiliar to you but you would like to learn more about Git I can highly recommend the
introductory course developed by [Dr Anna Krystalli][anna] [Git and GitHub through GitKraken : From Zero to
Hero][gitzerohero]. This course is run regularly by myself and colleagues in [Research Software Engineering][rseshef]
for post-graduate researchers and staff at the University of Sheffield.
[anna]: https://www.r-rse.eu/
[emacs]: https://www.gnu.org/software/emacs/
[git]: https://git-scm.com
[gitkraken]: https://www.gitkraken.com/
[gitzerohero]: https://srse-git-github-zero2hero.netlify.app/
[magit]: https://magit.vc/
[rseshef]: https://rse.shef.ac.uk
[rstudio]: https://posit.co/products/open-source/rstudio/

0 comments on commit 9e70887

Please sign in to comment.