Category Archives: Git

Git – What to do when things go wrong

I thought I’d detail some things that you can do when you get issues with git. Specifically, how you can deal with issues without having to resort to manually copying files around.

For this purpose of this post, I’ll assume that you have a git repository, and that some code has been pushed to it. I’ll be using git bash for the commands in this post.

Status and Log

Before we start working out how we can revert or fix issues that go wrong, let’s first see how we can reliably, and sensibly see what state we’re in.

Let’s start with having a look at the story so far. For this we can use the git log command:

git log --oneline

Our history has some commits in already – these have been pushed to the remote repository.

To demonstrate these commands, firstly, we’ll change a file:

$ echo "some test" >> test.txt

This file hasn’t been staged yet; we can prove this by using the git status command:

$ git status -s

This gives us a nice display of the state of our pending changes:

What git status -s gives you is a report of which files are staged, and which are committed in the following manner:

XY filename

Where X is a staged change, and Y is an unstaged, but tracked change. For example, if we stage the change to test.txt:

Of course, a file can have both staged, and unstaged changes:

Finally, let’s commit and push this change, and then view the log again:

git log --oneline

When things go wrong

Okay, so what we’ll cover here is how to revert changes that are in various states. The states that a file can exist in in git are:

1. Untracked
2. Tracked and unchanged
3. Tracked and changed

Let’s start with (1) – untracked. We’ll create a new file:

$ echo "new file 2" >> newfile2.txt
$ git status -s

This shows up like so:

The question mark here is telling us that git doesn’t know about this file – it’s untracked. In fact, if you just call git status it will tell you that the file is untracked.

Remove Untracked files

Okay, so by default, git will not remove newfile2.txt for you – you’re working away and you type the wrong command, you don’t really want git to go and wipe your files; however, sometimes, you do.

Because we aren’t tracking the new file, we’ll need the command:

git clean

However, if you run this, you’ll get the error:

fatal: clean.requireForce defaults to true and neither -i, -n, nor -f given; refusing to clean

The reason here is that, by default, git tries to prevent you from breaking things – and removing all the files in a directory that git doesn’t know about it quite drastic. We’ll therefore need the -f parameter to force the issue:

git clean -f

This will remove any untracked files that it finds; however, it will leave directories, and also anything that you’ve explicitly told it to ignore. To remove absolutely everything, we’ll include two extra parameters:

git clean -f -d -x

That will remove anything that git doesn’t know about, whether or not you’ve told it to ignore the files. Obviously, use this with caution – it WILL delete files and you WILL NOT be able to restore them – however, it will ONLY remove local files – any remote repository will be unaffected.

Let’s now consider a situation where you have made a change to a file, but you wish to revert to the unchanged state.

Reverting a tracked change

There is more than one category of tracked change; they are:

1. Changes that are unstaged
2. Changes that are staged
3. Changes that are committed
4. Changes are committed and pushed to a remote repository

All four of these can represent different changes in the same file. Let’s change a file so that it’s different in all four stages:

echo "change type 4" >> test.txt
git add .
git commit -m "change type 4"
git push
echo "change type 3" >> test.txt
git add .
git commit -m "change type 3"
echo "change type 2" >> test.txt
git add .
echo "change type 1" >> test.txt

We can see the status of the local repository by issuing a status command:

git status -s

This will show that the file has both stages and unstaged changes:

And of the remote by viewing the tree:

git log --all --graph --decorate --oneline

So, we can see that each category of change is represented. Let’s start by reverting the unstaged change.


This is perhaps the easiest to revert, as there’s a command designed specifically for it:

git restore .

This will leave staged changes, but will discard any unstaged changes to the state of HEAD. If you wish the keep these changes for later, then stash might be a better answer.


One way of unstaging changes is to use the git restore command with the parameter staged:

git restore --staged .

Doing this will unstage the change that you’ve made; for example:

Similarly, you can issue a reset command for the same purpose:

git reset HEAD

Which has the same effect.

However, if you wish to completely discard the staged change, you can issue a reset with the hard parameter; for example:

None of these commands will affect any of the commits made:

Let’s look at how you can revert a local commit.

Undoing a local commit

If we look at the result of git log above, we get a clue as to how this may be possible, commit 335cf90 points to the local master branch, but origin/master points to a remote master branch. So we can just point our local HEAD back to the remote. There’s a few ways that this can be done; they are quite subtly different.

git revert

To move to the state of a previous commit, you can issue a git revert. However, this does not actually revert the commit, it simply creates a new commit which is the reverse of the one that’s already there.

This can result in merge conflicts, depending on the state of the local and remote branch.

Once you’ve started a git revert, you can issue a git revert --abort to abandon the revert.

git reset

A quick way to discard a local commit is to simply reset HEAD to the origin/master:

$ git reset --hard origin/master
HEAD is now at 00f3c8e change type 4

Undoing a remote change

Because you’re now manipulating the remote repository, this carries a very real risk of causing some serious damage.

The first step here is to set your HEAD, as before, to a previous commit; for example:

$ git reset --hard origin/master~1

This will set your HEAD to the commit 1 before the current remote master (to go back two, you would use master~2

If you now look at your log, you’ll see that it now looks a little strange:

There’s a couple of things to note here – the first is that you now have a detached HEAD – this means that your HEAD no longer refers to a branch. Secondly, your branch and the remote branch are now different. To complete this process, you can push the change; however, if you just call git push you’ll get something like the following error:

$ git push
fatal: You are not currently on a branch.
To push the history leading to the current (detached HEAD)
state now, use

    git push origin HEAD:<name-of-remote-branch>

What’s needed here is a bit of force:

$ git push -f origin master

Looking at the history now, we can see that the latest commit is gone:

It’s worth bearing in mind that, one of the great things about git is that, nothing is ever really gone. If we use git reflog we’ll see that the “lost” commit is, in fact, safe and well – just orphaned:

git reflog

You can even see this in the tree structure:

$ git log --graph --all --oneline --reflog

If we now add a new commit, we can use this command to see where we went back to and forked:

Git Bisect with Automated Tests

Some time ago, I saw a talk at DDD North about git bisect (it may well have been this one). I blogged about it here. I can honestly say that it’s one of, if not the, most useful thing I’ve ever learnt in 10 minutes!

However, the problem with it is that you, essentially, have to tell it what’s good and what’s bad. In this post, I’ll be detailing how you can write automated tests to determine this, and then link them in.

Using existing tests to determine where something broke

In this example, I’ll be using this repository (feel free to do the same). The code in the repository is broken, but it hasn’t always been, and there are some tests within the repository that clearly weren’t run before check-in, and are now broke (I know this, because I purposely broke the code – although this does happen in real life, and often with good intentions – or at least not bad).

We’re using xUnit here; but I’m confident that any test framework would do the same. The trick is the dotnet test command; from the docs on that command:

If all tests are successful, the test runner returns 0 as an exit code; otherwise if any test fails, it returns 1.

As with the previous post, we need to start with a good and bad commit; for the purpose of this post, we’ll assume the current commit is bad, and the first ever commit was good.

git log

Will give a list of commits:

We need the first, which, for this repo, is:


Now we need to go through the process of setting up git bisect; the process is: you tell git that you wish to start a bisect:

git bisect start

Next, you tell git which commit is bad. In our case, that’s the current one:

git bisect bad

Finally, you tell it which was the last known good one – in our case, the first:

git bisect good 3cbd757dd4e92d8ab2424c6a1e46a73bef23e056

Now that we’re in a bisect, you could just tell git each time which is good and which bad (see the previous post on how you might do that), but here you can simply tell it to run the test:

git bisect run dotnet test GitBisectDemo/

This will then iterate through the commits and come back with the breaking commit:

That’s great, but in most cases you didn’t actually have a breaking test – something has stopped working, and you don’t know why or when. In these cases, you can write a new breaking test, and then give that to git bisect for it to tell you the last time that test passed.

Create a new test to determine where something broke

Firstly, the new test must not be checked in to source control, as this works by checking out code from previous releases. Then create your new test; for example:

namespace GitBisectDemo.Tests
    public class CalculationTests2
        public void DoCalculation_ReturnsCorrectValue()
            // Arrange
            var calculationEngine = new CalculationEngine();

            // Act
            float result = calculationEngine.DoCalculation(2, 3);

            // Assert
            Assert.True(result > 4);


This is a new class, and it’s not checked into source control.

Executing a specific test from the command line

We now want to execute just one test, and you can do that using dotnet test like so:

dotnet test GitBisectDemo/ --filter "FullyQualifiedName=GitBisectDemo.Tests.CalculationTests2.DoCalculation_ReturnsCorrectValue"

You need to give it the full namespace and class name; we can now incorporate that into our git bisect:

git bisect start
git bisect bad
git bisect good 3cbd757dd4e92d8ab2424c6a1e46a73bef23e056

These are the same as before.

Note: if, at any time, you wish to cancel the bisect, it’s git bisect reset

Now, we feed the filtered test run into git bisect:

git bisect run dotnet test GitBisectDemo/ --filter  "FullyQualifiedName=GitBisectDemo.Tests.CalculationTests2.DoCalculation_ReturnsCorrectValue"

And we get a result when the new test would have broken.

That’s two cases covered. The final case is the situation whereby the thing that has broken cannot be determined by an automated test; say, for example, that an API call isn’t working correctly, or a particular process has slowed down. In this situation, we can have git bisect call out to an external executable.

Custom Console App

The first step here is to return a value (exit code) from the console app. In fact, this is deceptively simple:

static int Main(string[] args)
    var calculationEngine = new CalculationEngine();
    float result = calculationEngine.DoCalculation(3, 1);

    return (result == 4) ? 0 : -1;

Notice that all we’ve done here is change the Main signature to return an int. This console app could now be calling an external API, running a performance test, or anything that has a verifiable result.

Publish the console app

Because we’re calling this from another location, we’ll need to publish this test as a self-contained console app:

dotnet publish -r win-x64

Run the test

Again, the same set-up:

git bisect start
git bisect bad
git bisect good 3cbd757dd4e92d8ab2424c6a1e46a73bef23e056

Finally, we call the console app to run the test:

git bisect run GitBisectDemo/GitBisectDemo.ConsoleTest/bin/Debug/netcoreapp3.1/win-x64/GitBisectDemo.ConsoleTest.exe


GitHub Secrets

I’ve recently been playing around with GitHub Actions. The reason being that I created this little NuGet package, because I needed such a utility for a web site that I’m working on.

I’ll go into how I set-up a basic CI/CD pipeline in a later post; but after playing with GitHub actions for a short period of time, it because clear that the first thing you need to understand is how secrets work. Otherwise you may, for example, generate a GitHub API key, and check that into your source control; and then get a rather harshly worded e-mail from GitHub telling you to stop doing that.

It turns out that storing secrets in GitHub is very easy. First, visit your repository; and, under the Settings tab, you’ll find Secrets:

Here, you just select Add Secret. Give the secret a name and tell it what the secret is:

Finally, to use your secret inside a workflow, you use the following format:



Working out when something broke using Git Bisect and Git Log

On 5th Feb, I attended DDD North. There were many excellent talks on the day, but in this post, I’m delving into something that I saw in one of the grok talks (I gave one myself). It concerns a feature of git that I’d never heard about until then, and it’s proved to be enormously useful. This post is, as all of my posts are, really for myself.

Something has broken

You’re giving the app a quick test, and suddenly you realise that something in the application has stopped working (or maybe it’s started, but it shouldn’t have). You remember that two days ago, this particular thing was working fine. This post is going to cover manually using git bisect to determine when your code was broken.

Get the good commit

To use git bisect, you need a good commit and a bad commit. Obviously, git doesn’t know what’s good or bad, so you could use it to determine when something was changed, added, or removed. Providing the behaviour is different, you can identify it.

If you know the date of the ‘good’ commit, you can ask git for the logs after that date. To list commits after a given date:

git log --after="2019-03-19T00:00:00-00:00"

This will give you all the commits (you can add a ‘–before’, too if you’re dealing with a long time ago. When you get the git Commit Id, make a note of it; for example:


Let’s do some bisecting

You start the bisect process by:

git bisect start

At all times through this process, git does precisely nothing to help you along, so if you even get an acknowledgement of your command you should consider yourself lucky!

The next step is to tell git that the current release is bad:

git bisect bad

You can give it a commit here that isn’t the current release, if you so choose.

The final step to start the process is to tell it where to start – i.e. what was the last known good release. This is your commit Id from earlier:

git bisect good Bc1afa359dc98b899cc4194ec8130a6229643172

What git does here is a binary chop. So the first code it will check out will be halfway between the two commits. It then tells you nothing (as usual). Now that it’s checked out the code for you, run your application with the code and see whether it is ‘good’ or ‘bad’. Once you’ve determined that, go back to git and tell it:

git bisect good


git bisect bad

Each time you tell it it’s good or bad, it will check out new code, and you need to test again:

(apologies for all the redacting)

Finally, it will determine which commit was the first bad one, and report back:

$ git bisect bad
Eea8e3d72fcb26cdebb2dbf2c13fdd88d7f3782a is the first bad commit
commit Eea8e3d72fcb26cdebb2dbf2c13fdd88d7f3782a
Author: Ian Kilmister <[email protected]>
Date:   Wed Mar 20 11:49:18 2019 +0100

    New lyrics

Once you’re done, you need to reset – this will book out the code you had out before you started:

git bisect reset