Git for Windows tip: opening Sublime Text from bash

I wrote a post about how to open NotePad++ from the bash prompt a few years ago but I recently made the switch to using Sublime Text 2 as my standard text editor and had to figure out how to do the same thing with Sublime. It was surprisingly difficult to find it on Google.

1. Create a text file called subl (with no extension) with the following content:

#!/bin/sh
"C:\Program Files\Sublime Text 2\sublime_text.exe" $1 &

2.  copy it into the C:\Program Files (x86)\Git\bin folder.

The first line indicates that this is a shell script. The first part of the second line is the path to the Sublime exe. The $1 parameter passes in any parameters so that you can use it like this from the bash prompt (command line) to open a file:

subl text.txt

or

subl .

to open the current folder.

The last parameter & indicates that it should open Sublime in the background so that you can continue using the command prompt.

Build Your Open Source .NET Project On Travis CI

Travis CI is a continuous integration service that lives in the cloud and is free for public Github repositories. When you push a change to your Github repo, Travis CI will automatically detect it and run a build script. It works for all branches and pull requests as well and has some really nice integration with Github. In the screenshots of the Github Pull Requests below you can see the first Pull Request has a Good to merge label and the second one failed.

SafeToMergePRTravis

FailedPRTravis

Travis CI supports loads of languages but not C# and the reason is that Travis CI only supports Linux servers. Although Windows support seems to be on the way.

Being a core committer for FluentMigrator, an OSS .NET project, this is actually just what I was looking for. We have a Teamcity server (thank you Jetbrains and Codebetter) but we don’t have any testing for Mono and Linux. I had seen that the git-tfs project (also .NET and C#) was using Travis CI and thought I’d try and copy their build script. But it was not as simple as that! Here is my guide to getting a .NET project to build on Travis CI.

Sign up for Travis CI

The first step is to sign up on the Travis CI website. You can only sign up for Travis CI via your Github login which makes sense as the service focuses on CI for Github projects only. After signing in you should see your name in the top right corner so click on that to open your profile.

TravisEnableRepo

Enable the Github hook for Travis CI by selecting the appropriate repository (daniellee/FluentMigrator in my case). And that is all you need to do. If the repository contains a file called .travis.yml then it will try and build it. This is triggered after every push to the repo.

XBuild

The second step is to create an MSBuild Xml file that can be run with XBuild. XBuild is the Mono version of MSBuild and uses the same file format. The simplest build script describes which .NET version to build the project in, the platform (x86 or AnyCPU) and the name of the solution file. Here is the MSBuild file for FluentMigrator:

<?xml version="1.0"?>
<Project ToolsVersion="4.0" DefaultTargets="CI" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <Target Name="CI" DependsOnTargets="Build" />

  <Target Name="Build">
    <MSBuild Projects="FluentMigrator (2010).sln" Properties="Configuration=Debug;Platform=x86" />
  </Target>

</Project>

Create A Travis YAML File

The next step is to create a .travis.yml file. A file with the extension .yml is a YAML file, a file format sort of like JSON but without all the curly braces. Check out the documentation for all the options that can be defined in the YAML file. Here is the YAML file for FluentMigrator:

language: c

install:
  - sudo apt-get install mono-devel mono-gmcs nunit-console

script:
  - xbuild CI.proj
  - nunit-console ./src/FluentMigrator.Tests/bin/Debug/FluentMigrator.Tests.dll -exclude Integration,NotWorkingOnMono

This needs a bit of explanation. Travis CI runs on Ubuntu Linux so before running this script .NET/Mono is not installed so we have to install everything before trying to build and test the project.

As C# and .NET are not supported by Travis we set the language to C.

The install part of the script uses Ubuntu’s package manager, apt-get (Chocolatey is the Windows version) to fetch Mono, the Mono C# Compiler (MCS) and the NUnit console runner. We need MCS to be able to compile our C# code. We need the Mono Development Tools (mono-devel) as it contains Mono 4.0, a CLR implementation that works on Linux. And the NUnit console runner is needed to run tests from the command line.

The first task in the script step is to use xbuild to build the MSBuild file. For FluentMigrator this builds the main solution file that contains all the projects.

The second task is to run all the NUnit tests from one of the test projects. I did try and run NUnit via the MSBuild script but gave up as it was much easier to do this way. There is one very important gotcha to note here. All the examples of using the NUnit console runner use a forward slash (/) for the switches even the man pages (Linux help pages) for nunit-console. It took me a while but I eventually noticed that in the NUnit documentation that it mentions that on Linux you should use a hyphen (-) instead and then either a space or colon after the switch. E.g.  I use the exclude switch to exclude some categories of tests like this: –exclude Integration,NotWorkingOnMono

Deploy It!

Now it is time to cross your fingers and do a git push to your repo on Github. I’m sorry but there is no way it is going to work first time unless you have already spent some time building it on Linux. It took me seven attempts to get it to build successfully and another five attempts before I got all the tests to pass.

Potential Pitfalls

Here is a list of all the problems I ran into. Though I’m sure that there are loads of other problems that could occur in your project.

Linux is case sensitive

There is a class named SqliteQuoter in FluentMigrator (with exactly that casing) and I got the following slightly baffling error on my first run:

error CS2001: Source file `Generators/SQLite/SqliteQuoter.cs’ could not be found

When I looked at the actual filename (it looked perfectly correct in Visual Studio) I saw that it had this casing: SQLiteQuoter. As just changing the casing of the filename is not a change in Git you have to explicitly rename the file.

git mv -f SQLiteQuoter.cs SqliteQuoter.cs

The next casing problem was just as baffling. It turned out that in the csproj file a reference to System.Data.SQLite.dll looked like this: System.Data.SQLite.DLL. And that meant that Linux could not match the filenames. I fixed that by manually editing the csproj file and manually changing the case.

I got stuck on another issue for a while but it turned out that it was nothing to do with Travis CI or Linux. After that was fixed I got a green build but the test runner did not run. The problem was the one I talked about before with the NUnit console runner and using a hyphen instead of a forward slash.

NewLine

Travis CI now ran all of FluentMigrators’ tests (except for the integration tests) and had 21 failing tests out of 1254. Most of the failing tests were due to the new line problem. FluentMigrator is (was) littered with \r\n which is the Windows new line character (Carriage Return and Line Feed). In Linux a new line is \n (Line Feed) only. The rather tedious solution to this was to go through the code and replace all of instances of \r\n with Environment.NewLine.

File Paths

A much harder to solve issue is file paths. They are fundamentally different on Windows and Linux. For some tests I could use Path.Combine and Path.DirectorySeparatorChar to get them to pass but others had to be ignored (for now).

And at attempt number twelve I had FluentMigrator being built and all the tests run successfully on Mono and Ubuntu Linux. And there are not a lot of open source .NET projects that can say that.

Next Step

Travis CI has support for MySql, Postgres and Sqlite and it would be fantastic if I could get all the integration tests running against them. Next week maybe.

Hope this helps out some of the other OSS .NET projects.

Git merge commit with no fast forward

When working in open source, it is quite common that I want to either manually merge in a pull request or refer to a Github issue when merging in one of my own branches. If you do a merge and there are no merge conflicts then Git will not create a commit. It does a fast-forward merge as this is the desired behaviour most of the time.

To create a merge commit when merge in a branch, I use this command:

git merge branchname --no-ff -m "Merge pull request #999 from fork/branch"

The –no-ff switch means no fast-forward while merging. There is some Github functionality in the commit message. Using # and the issue or pull request number will create an automatic link to this commit in the issue or pull request.

Github PR
Commit reference as seen in a Github Pull Request

A few tips on git reset

I’ve learned a couple of handy tricks with git reset during the last few weeks (mostly by following Github employees on Twitter!).

This first tip is really handy when you have messed up your master branch by forgetting to create a new branch or when you want to redo your commits. For example, if you want to submit a beautiful, handcrafted pull request but your recent commit history looks really nasty then run this command:

git reset --soft origin/master

This will rollback your branch to the same commit as origin/master but it keeps all the changes you have done. So the commits are removed but all the files you have changed are moved to staging. You can then unstage and stage files and handcraft logical commits with fantastic commit messages. Don’t forget to do a git fetch origin/master first and a git rebase afterwards!

The 3 modes

There are three modes for git reset: soft, mixed and hard. See this post on ProGit for an in-depth explanation. If I had used –mixed instead of –soft then I would also have unstaged all my changes. While –hard would have thrown away all my changes and my local repo would look exactly like origin/master. So be careful with hard resets as the only way you might be able to get your changes back is using something like git reflog.

The Last Trick

git reset will take any git reference (SHA) or treeish.  Examples of treeishes are:

  • origin/master – which is a remote branch name
  • HEAD~ – the previous commit of wherever HEAD is pointing
  • master~2, master^^ – two commits back from the current master HEAD
  • master@{5} – 5 commits back from current master HEAD
  • 981c1cc775845200e6cb5ad6e9d8d10f2d3e1cf8 – a complete SHA
  • 981c1cc775845200e – a partial SHA

To quickly reset your branch HEAD to a previous commit, choose an appropriate treeish for a previous commit and pass it into git reset. Here is an example using a complete SHA which I found by using git log to see my recent commits:

git reset --hard eff383a443e8bfe1859063079bd15f712509e2c5

Git for Windows tip: How to copy and paste into Bash

Per default the only way to copy and paste into the Git Bash is to click on the git icon in the top left corner and select Edit->Mark/Copy/Paste. This is actually not a property of Git Bash. It is just how the Windows console runner works i.e. the standard command prompt.

There are two solutions to be able to copy/paste with the keyboard and directly with the mouse. The first is to use a different console, see Scott Hanselman’s post on Console2. I haven’t tried this yet so let me know in the comments if you use Console2 and can recommend it.

The other solution is to enable QuickEdit Mode under Options->Edit Options in the properties for the console. To open the Properties dialog, click the Git icon in the top left corner of the console and choose Properties in the menu.

msysgitPropertiesCapture

Now you can select text with the mouse and right click to copy. Pasting is done by either right clicking with the mouse or pressing the Insert key. Still more awkward than using Ctrl-C and Crtl-V so maybe it is time to check out Console2.

Git for Windows tip: Setting $HOME and the startup directory

Git for Windows opens bash in the the user profile directory per default and I wanted to change it to the directory with my Github projects instead. I had to try a couple of approaches before finding the solution.

Setting $HOME

The first approach I tried was setting the $HOME environment variable. There are a couple different ways of doing this (like messing around with the etc/profile file) but easiest for me was using Windows’ environment variables as Git for Windows/msysgit has access to them. The Home directory for msysgit is set to the Windows environment variable %USERPROFILE%  if no $HOME variable exists. So just create a $HOME environment variable in Windows (see screenshot below)  and msysgit bash will use that as the default. Now you can use the command cd $HOME to go directly to your new home directory.

EVCapture

But msysgit still opens up in my user profile directory…

Unfortunately, this does not help with the startup directory problem. The solution is actually really simple. Right-click on the msysgit shortcut and in the Start in field, enter your desired startup directory. Easy if you know how.

msysgitCapture

Git for Windows tip: Use P4Merge as mergetool

I recently found P4Merge (thank you Twitter and Git Immersion) and instantly dropped WinMerge as my standard diff/merge tool. I really like the way it visualises the differences and the 3-way merge is really nicely done. P4Merge is the merge tool for Perforce (which I have never used) and is both free and can be downloaded separately from the rest of Perforce.

The download can be found here. Choose Browse by component->Clients->Visual Merge Tool as you do not want to download the whole Perforce client package. In the installer for P4Merge you can choose which components you wish to install, you only need the Visual Merge Tool (P4Merge).

Install p4merge and then set it as your merge tool for git by running the following two config commands:

git config --global merge.tool p4merge
git config --global mergetool.p4merge.cmd 'p4merge.exe \&quot;$BASE\&quot; \&quot;$LOCAL\&quot; \&quot;$REMOTE\&quot; \&quot;$MERGED\&quot;'

EDIT: As pointed out by barik in the comments, in newer versions of git this works as well and is slightly simpler:

git config --global merge.tool p4merge
git config --global mergetool.p4merge.path &quot;C:/Program Files/Perforce/p4merge.exe&quot;

P4Merge can be used for doing diffs and merges. If using P4Merge for diffs then call:

git difftool

If the file you want to compare is already staged then use the –cached switch after that command.

When Git tells you that there has been conflict, to resolve it type:

git mergetool

This will open P4Merge and show three different versions of the file; your local version, the version you are trying to merge in (probably the master branch) and the base version. The base version is the common ancestor of the local version and the remote version. Choose which version wins or edit the merge manually and then save and quit P4Merge. Finally commit the merge and then remove any .orig files that may be left over. It should be possible to have git remove the .orig file automatically by setting mergetool.keepBackup to false in git config but I have not succeeded in getting that to work for me yet.

An example of using P4Merge

And this is what a 3-way merge looks like. The local version on the left, the base version in the middle and the remote version (the master branch) on the right. In the merge window at the bottom all three versions are currently selected. To select the local version, for example, I would click the blue diamond icon and then save and quit.

p4mergeCapture