jimmy keen

on .NET, C# and unit testing

Reset a file with Git command line (Windows)

August 22, 2016 | tags: git windows powershell xargs

I use bunch of Git commands and aliases during my daily work. Although I am quite happy with my current setup, one thing has been lacking for a while – a simple command to undo changes made to file. Even though Git offers a default solution, I never really liked it. Recently, I finally managed to create a viable replacement.

To stage or to unstage

Before we begin, it is important to understand what happens when you modify a file tracked by Git. First, it is marked as unstaged which means this file has changed compared to its previous version but it is not commit ready yet. When you run git status it is mentioned explicitly:

> git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   CsharpKatas/Xml/Creation.cs

no changes added to commit (use "git add" and/or "git commit -a")

Also note that commiting at this point produces no commit as there is nothing to include in it:

> git commit -m "Fix"
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
        modified:   CsharpKatas/Xml/Creation.cs

no changes added to commit

We have been already hinted as to what to do next which is git add <file>. Most of the times you are probably using git add -A to include all unstaged files in soon-to-be commit.

Once a file is added with git add it becomes staged – or in other words – commit ready. This file will be included in the next commit you create. As expected, status command recognizes this fact:

> git add -A
> git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   CsharpKatas/Xml/Creation.cs

Quick recap:

  • when you modify tracked file it becomes unstaged, noticed by Git but not yet ready for commit
  • when you use git add unstaged file(s) become staged and will be included in next commit

Commands mentioned above constitute to vast majority of what Git shell user executes daily. My usual workflow (and I can imagine many other people’s) is:

  1. Modify files
  2. Verify: git status
  3. Commit: git add -A then git commit -m "Fixed"
  4. Push git push origin HEAD

These commands are so common that I use very short aliases for them: git s, git c "Fixed" (this also does git add -A) and git up. This makes interaction with shell very smooth.

Sins of automation

Unfortunately, this approach has one major flaw. It is a bulk-operation thanks to git add -A + git commit combo. Imagine you modified 10 files but changes in one of them need to be discarded. Documentation suggest to use checkout command:

git checkout -- <path-to-file>

This is quite some typing. However:

  • good news is, Git allows tab-cycling through unstaged files
  • bad news is, since checkout command is also used to checkout branches, tab-cycling includes all branches
  • good news is you can use wildcard/auto-completion
  • bad news is, as soon as you hide checkout -- behind alias, tab-cycling is gone

What I would like instead is simple undo command:

git undo <file-name>

Putting together all the pieces

There is a bunch of Git commands and tools to help us here. What needs to be done is:

  1. With git diff we can get unstaged files paths (using --name-only switch)
  2. Filter them to the one matching pattern using unix grep
  3. Feed this path to git checkout -- using unix xargs

Last two commands don’t occur naturally in Windows environment but luckily Windows Git distribution includes them. Just make sure you have Git bin directory in PATH.

Alright, assume that our working directory contains two modifed files, like this:

> git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   CsharpKatas/Xml/Creation.cs
        modified:   CsharpKatas/Xml/Querying.cs

no changes added to commit (use "git add" and/or "git commit -a")

Let’s try and match Quotation.cs with git diff and grep:

> git diff --name-only
CsharpKatas/Xml/Creation.cs
CsharpKatas/Xml/Querying.cs
> git diff --name-only | grep Query
CsharpKatas/Xml/Querying.cs

Almost there. All we need to do left is somehow pass this to git checkout --. Regular piping does not work and we need a special command, the xargs. What it does is simply pass its arguments to a command of choice. The final command looks like this:

git diff --name-only | grep Query | xargs git checkout --

Running this we got:

> git diff --name-only | grep Query | xargs git checkout --
' did not match any file(s) known to git..cs

Come again?

Story behind xargs

Judging by the strange output, could it be that Windows’ carriage return and line feed (aka new line) is causing problems? Let’s investigate what xargs does with --verbose switch:

> git diff --name-only | grep Query | xargs --verbose git checkout --
 it checkout -- CsharpKatas/Xml/Querying.cs
' did not match any file(s) known to git..cs

Yep, it seems we are on the right track with \r symbol obfuscating output. New line confusion is a common issue with xargs which uses whitespaces and newlines to separate arguments. Unix commands deal with this problem with -print0 switch which changes default argument separator to \0, end-of-string character. On Windows we do not have such option.

To mitigate this problem we could simply say that both \r\n are argument separator for xargs. Let’s see where this takes us:

> git diff --name-only | grep Query | xargs -d \r\n --verbose git checkout --
git checkout -- CsharpKatas/Xml/Querying.cs

error: pathspec '
' did not match any file(s) known to git.

Closer. It looks like xargs seems to think we have two arguments, path and the newline. But we only want to pass one argument to git checkout invocation and xargs has a switch to do just that:

> git diff --name-only | grep Query | xargs --verbose -d \r\n -n 1 git checkout --
git checkout -- CsharpKatas/Xml/Querying.cs
git checkout --

error: pathspec '
' did not match any file(s) known to git.

Despite similar error message it is actually working. First invocation of git checkout went successful and did reset changed file. There is still extra newline argument which goes to second checkout invocation, but it simply does nothing.

Keep in mind this solution works with Powershell 5. You can check your version with $PSVersionTable.PSVersion. For earlier versions the final command will be this:

git diff --name-only | grep Query | tr -s '\r\n' '\0' | xargs -d '\0' -n 1 git checkout --

In brief, it is simple emulation of unix approach where end-of-string character is used to separate arguments. It was tested on Powershell 3.

Wrapping up

There is one more thing we need to do - enclose this command as git alias. And this causes yet another issues. The behavior regarding \r\n changes once again with inline function. Powershell 3 version of the command seems to be the most robust and this is the one I use. Finally:

> git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   CsharpKatas/Xml/Querying.cs

> git config --global alias.undo "!f() { git diff --name-only | grep `$1 | tr -s '\r\n' '\0' | xargs -d '\0' -n 1 git checkout -- ; }; f"
>
> git undo Query
> git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

We have an alias. In a similar fashion we can create unstage command to degrade once staged files (using diff with --staged switch and git reset HEAD --).

Introduction to F# unit testing for C# developers

July 16, 2016 | tags: f# c# unit-testing

I recently picked up F# as a tool for one of my pet projects. After getting familiar with syntax and tools, natural next step was to write few unit tests. How did it go?

C# background

When writing tests in C# I would use the following libraries:

This is more or less state of the art in modern C# development. Of course you can replace any library with its alternative. However, it will not be a breakthrough but rather a cosmetic change. The big picture remains the same.

It is possible to have nearly identical setup in F# with a single library – FsUnit (save from mocking framework but we will get to that later).

F# unit test

Currently I use Visual Studio 2015 Community, which is just enough for F# development on Windows box. Assuming we have our IDE sorted:

  1. Create F# Library project (no, you don’t need any script file in there)
  2. Add FsUnit package (this will also install NUnit 3 dependency)
  3. Add NUnit3TestAdapter package (it is important to have this otherwise VS will not detect your tests)

And write simple test:

module JimmyKeen.Paterns.UnitTests

open FsUnit
open JimmyKeen.Patterns.Candles
open NUnit.Framework

[<Test>]
let ``Candles overlap when parts of their bodies occupy same price range``() =
    let candle1 = Candle(20161010, 10.0M, 12.0M, 9.0M, 11.0M, 1000, 0)
    let candle2 = Candle(20161011, 10.5M, 11.5M, 10.0M, 11.5M, 1000, 0)
    overlapRatio candle1 candle2 |> should equal 0.5M

Notice the double-backtick identifier which allows us to be very verbose with test names. Other than that, this is very similar to what our C# test would look like.

Wait, did you forget about FakeItEasy?

You might have noticed that F# setup is missing mocking framework. Why is that?

Let’s ask different question – why do you need mocking framework in first place? Because you have some dependency that should behave different during production and test run. And existing language features do not make changing this behavior easy. It is possible, but requires lot of boilerplate code. Mocking framework does this mundane work for us.

Now in C#, primarily object-oriented language, dependencies are other objects which in most cases will be a class instance. In F# we want to deal with functions. Functions, which will depend on other functions. Think about C# class which has a dependency to a Func<T>, rather than other class:

public class RiskCalculator
{
    private Func<int, int> ageCategoryComputer;

    public RiskCalculator(Func<int, int> ageCategoryComputer)
    {
        this.ageCategoryComputer = ageCategoryComputer;
    }

    // ...
}

You do not need any special framework to mock such dependency. In test you would simply create lambda expression.

In F# it is always like that. As a result, usage of mocking frameworks is very limited (but not completely gone). You can, and perhaps should design your code so that mocks are not required. If you want to hear more about this approach I recommend watching “Look, no mocks!” presentation by Mark Seemann.

Where to go from here

I believe that starting with FsUnit and Visual Studio is the easy-transition path for C# developer. Once you get familiar with that approach, there are other ways to develop with F#. You don’t need Visual Studio. There are few more, novel options for tooling libraries. We’ll take a look at that in next blog post.

Using Git with Powershell on Windows

November 10, 2015 | tags: git windows powershell posh-git

This will be a quick guide on how to set up Powershell with Git on Windows.

Essentials

  1. Powershell
  2. Git for Windows
  3. Git credentials store

Download and install files above. Make sure Git bin directory is added to your PATH environmental variable. Credentials store are not really required but I’m pretty sure you do not want to type your login details with every push or pull, right?

Aliases

Just as with any console application, Git offers a way to streamline your work with aliases. Rather than typing

> git add -A

> git commit -m "Added config file"

You can simply type

> git c "Added config file"

This of course goes further than that considering we have Powershell at our disposal. For example, you can easily create alias like this:

> git config --global alias.logby "!f() { git log --author='`$1'; }; f"

> git logby Jimmy

We create f function and execute it straight away. You can read more about this approach at “Git alias with parameters”. When setting such alias from powershell itself, make sure to escape $-variables with ` (grave accent symbol), otherwise powershell will try to insert variables which are not there.

You can see some more aliases here.

The Posh

A neat little extension offering git commands completion and making your Powershell look like this:

posh-git powershell highlighting

Posh-git can be installed from powershell:

> (new-object Net.WebClient).DownloadString("http://psget.net/GetPsGet.ps1") | iex

> Install-Module posh-git

If your powershell compains that your execution policy is restricted, make sure to un-restrict it with:

> Set-ExecutionPolicy RemoteSigned

It’s great we can see current branch name. It’s also great that we can see whether there were any modifications. But I’m not a huge fan of having all the details of changed files, untracked, stashed and so forth. I would rather have this:

posh-git powershell simplified highlighting

A simple indicator telling me what branch I am on and what’s the status (* - pending changes, - synced with origin, - ahead of origin).

Behavior described above can be achieved if we modify posh-git code, namely GitPrompt.ps1 file. You can find this file location in one of your powershell modules directories (check $env:PSModulePath variable). You can use the files from my repository:

And that’s it. Enjoy your brand new Git + Powershell experience!