12 February 2018

Git Cheat Sheet

After moving from Subversion to Git, I was rather confused. Working with Subversion was mostly comprised of Update and then Commit. If there was branching involved, things could get messy quickly, but if everyone was working on trunk Subversion was mostly invisible.

Now Git… Git has many commands. Therefore, this list.

A word of warning: these snippets work(ed) for me, but you should try to understand what they do before applying. Some commands have a dry-run mode - use it. If you are not sure about the changes - do not push. That makes it much harder to correct.

Basics

To clone an already existing repository:

git clone <repository>

This creates a subfolder named after the repository. If you want to have two clones, or you want your folder name to be different, just add a name afterwards. Like this:

git clone <repository> <directory>

To get some details about current state, issue

git status

command. This displays info about changed files, deleted files and newly added files which are in Git language called untracked.

Small workflow example

Git is all about branching. To create a new branch from the currently active branch:

git branch <new_branch_name>

Checkout new branch

git checkout <new_branch_name>

Do some work and then

git add .

to include all changes so far, for committing.

After

git commit -m "commit message"

all changes will be committed to current branch of local repository.

To share your work, issue

git push

Since the branch is new, command will fail. So only when the new locally created branch is pushed for the first time we need to

git push --set-upstream origin <new_branch_name>

Now, to get the changes from remote repository, do a

git pull

Exploration

Display all branches

git branch -l

Display all branches, including remote ones

git branch -l -a

Find commits with log message that matches the specified pattern (regular expression):

git log --grep=<pattern>

Find a commit in which a string was added or erased from some file:

git log -S<a_string> --oneline

Show differences between two commits. In my opinion, not very useful, but included for sake of completeness:

git diff

Find all commits by someone:

git log --committer=<someone>

Show all remotes:

git remote -v

Show all branches on the remote:

git remote show <remote_name>

Useful since not all remote branches are present locally.

Branch operations

Delete a local branch:

git branch -d <branch_name>

Force delete a local branch:

git branch -D <branch_name>

Delete a branch from the remote:

git push <remote_name> :<branch_name>

Here remote_name is usually origin.

Create a branch from any specific commit:

git branch <new_branch> <commit>

Rename a branch:

git branch --move [<old_branch>] <new_branch>

Merging

Merge another branch into current one:

git merge <another_branch> 

--no-commit can be added to allow for additional changes, or inspection of merge.

To do a squash merge:

git merge --squash <another_branch>

This applies changes from another_branch into current one as one commit, and does not record merge info.

Often, for purposes of clearer history, it is useful to add --no-ff option, so that a merge commit will be created even if it will be empty:

git merge --no-ff <another_branch>

When things go wrong

If, changes have been made to a wrong branch, but nothing was committed, the following sequence of commands transports changes to a different branch:

git stash -u
(optionally) git branch <some_other_branch>
git checkout <some_other_branch>
git stash pop

Here, -u ensures that untracked files are also added to stash.

Undo the changes to a file

git checkout -- <path/to/a_file.ext>

Undo all the changes to tracked files

git checkout .

This changes the tracked files back to their last committed state. Untracked, i.e. new files, should be deleted manually.

Alternatively,

git reset --hard

can be issued to reset the branch to the last committed state.

If a commit has just been created, but was not pushed, it can be undone with

git reset --hard HEAD^

If it was pushed,

git revert <bad_commit>

creates a new commit which reverts the effects of the bad one. To inspect (and modify) changes before committing add --no-commit

To undo a merge which has not been pushed:

git reset --hard <remote>/<branch_name>

This will get the local branch in sync with remote one.

For undoing a merge that has been pushed there is no simple recipe.

Ignored files

Git keeps list of the patterns for ignored files in .gitignore file. To determine why a file is being ignored by Git, do:

git check-ignore -v <filename>

Adding entries to .gitignore does not affect already tracked files. For Git to stop tracking a file it needs to be removed from the index.

git rm --cached <file>

To remove a whole directory from index, along with its subdirectories, navigate to it and issue:

git rm --cached *

Other

To set a config variable:

git config <variable> <value>

For example, to set the Notepad as an editor for commit messages:

git config core.editor notepad

Create a patch file:

git diff <from-commit> <to-commit> > output-file

Squash last n commits into one.

git reset --soft HEAD~<n>

Do this only when commits were not pushed.

Apply arbitrary commits from other branches to current branch:

git cherry-pick <commit1> [<commit2>...]

--no-commit can be added to inspect proposed changes.

Delete untracked files

git clean -f

Add -n for dry run, -d for directories, -x to also delete ignored files

Still more useful information

If you are on Windows, don’t forget to check out posh-git. It adapts PowerShell command prompt with status information of the repository and provides autocompletion support.

Useful links:

17 January 2018

If I Could Tell Only One Thing

If I could give one advice to fellow developer(s) without knowing anything about their background or skill level it would be to turn on Break When Thrown for all CLR exceptions. I have witnessed developers less experienced and more experienced lose too much time trying to pinpoint the location where exception gets thrown.

How to enable the setting

Find it under Debug -> Windows -> Exception Settings

Debug -> Windows -> Exception Settings image

and ensure that checkbox next to Common Language Runtime Exceptions is checked.

Exception Settings window

Now, whenever an exception is thrown, Visual Studio will break and show the line where exception has happened.

Be aware that this setting, together with Just My Code unchecked, might break on a few exceptions which are caught and handled in .NET Framework or referenced libraries.

Impact of the setting

To illustrate difference in debugging experience, let’s look at a sample console application, first without checkbox checked and then with checkbox checked.

using System;

namespace Calculator
{
    class Program
    {
        static void Main(string[] args)
        {
            var result = ComplicatedCalculation();

            Console.ReadLine();
        }

        private static int ComplicatedCalculation()
        {
            try
            {
                return ComplicatedSubCalculation();
            }
            catch
            {
                // log
                throw;
            }
        }

        private static int ComplicatedSubCalculation()
        {
            try
            {
                return SimpleCalculation(7);
            }
            catch
            {
                // log
                throw;
            }
        }

        private static int SimpleCalculation(int arg)
        {
            throw new NotImplementedException();
        }
    }
}

When running this code with debugging (F5) Visual Studio breaks on outermost throw statement.

Not very obvious that it is SimpleCalculation that is in fact not implemented. Also, when looking at the exception here, there is no data available about original call stack which is very useful since it includes values of method parameters.

Often, top level code is wrapped in try .. catch .. so that the application can survive unplanned exceptions.

static void Main(string[] args)
{
    try
    {
		var result = ComplicatedCalculation();

		Console.WriteLine("Result is:" + result);
	}
	catch (Exception e)
	{
		Console.WriteLine(e);
	}
		
	Console.ReadLine();
}

After running the application now, we get the following console output:

So, exception has occurred and all the data is here - file name and line number where the exception is thrown. But in real life, this can be buried in log files and we might even be unaware that the exception has happened. Then it looks more like this:

Now, let’s check the checkbox and run the application:

Debugger helpfully stops on first exception and argument values are available.

Side note

Sometimes, this little gem automatically unchecks itself, so for quicker access shortcut Ctrl+Alt+E is rather useful.

03 October 2017

Who knew than JSON.parse() requires double quotes

The other day we were trying to get a small app off the ground. Sometimes even the simplest things which should obviously work, just don’t work.

The code in question was creating a small object:

var s = '{';
s = s + 'width : 17,';
s = s + 'length : 20';
s = s + '}';

var o = JSON.parse(s);

But after executing, o did not contain an object at all.

(As I write this, call to JSON.parse(s) fails with an exception in Chrome. I am fairly certain that during the development there was no exception.)

After unsuccessfully trying out a few things, a search on the internet revealed that property names must be double quoted. This was a bit surprising, since one can easily asume that whatever Javascript code is valid, it will still be valid when passed as a string into JSON.parse()

Turns out JSON has special syntax which prescribes double quotes.