Archive

Archive for August, 2009

Showing commits not yet on master

August 27, 2009 Neal 2 comments

I use git most days now, it means I can work on my laptop and commit back to our subversion repository when I am connected again ( or when I finish working on the feature ).

As I have posted previously, I cannot dcommit multiple commits – git freaks out and makes a mess of things after the first commit. To get around this, I do all my work on a topic branch and then cherry-pick the commits from the topic branch into master and dcommit each one individually.  While this works, it is painful so I am slowly working out how to get the list of commits and cherry-pick then dcommit them individually by stringing a bunch of commands together.

The first thing I need to do is get a list of the commit hashes that need to be cherry-picked from the topic branch.


git checkout master
git log master..<topic_branch> --format="%h" > commits.txt

This gives me a nice list of commit hashes that I can use to issue the cherry-pick commands. Beware – at this point the list is in inverse commit order – that is, the oldest commits are at the bottom of the list.

I will keep updating this post as I get closer to having a single command that I can issue to mimic the following ( that I can use due to some weird error )

git checkout master
git svn rebase
git rebase <topic_branch>
git svn dcommit
git branch -D <topic_branch>

Update … with very little research and effort I have managed to create a bash script that mimics the workflow above.
The following bash script gets a list of the commits that are on the topic branch but not in the master in chronological order oldest first and replays them onto the master, one commit at a time, dcommitting the changes between each commit. Ironically, while I was researching / writing this script I may have found the solution to my issue – git svn dcommit --no-rebase.


#!/bin/bash
echo `git checkout master`
echo "Rebasing against remote svn repository"
echo `git svn rebase`
for commitHash in `git log master..$1 --reverse --format="%h"`
do
echo "Merging next commit"
echo `git cherry-pick $commitHash`
echo "Commiting changes to remote repository"
echo `git svn dcommit`
done
echo "$1 successfully merged and dcommitted - don't forget to delete the branch"
exit 0

The script can be put anywhere that is on the bash path – I currently leave it in the root of the folder I am working in ( same folder that houses the .git folder ). Calling the script is as simple as dcommit <topic_branch>.

Categories: Git

Fighting with git svn

August 25, 2009 Neal 1 comment

First up, a disclaimer – I am a git n00b therefore anything below may or may not be correct but “it works for me” so I am putting it out here for anyone else facing similar issues ( and also for future reference as I have a feeling this will happen a few more times yet ).

I have recently moved to using git on my laptop. Originally I installed GitExtensions ( which uses msysGit 1.6.3 ), but after some problems I uninstalled that and installed msysGit 1.6.4 using this excellent guide from Jason Meredith.

I cloned the repository I was working from using:

git svn clone https://<server>/svn/<projectname>/trunk

I didn’t need all of the branches so I only grabbed the trunk. My upstream repository is a subversion repository hosted within Visual SVN Server 2.05.  After cloning the subversion repository I wanted to work on, I started working as usual with the following workflow:


git checkout -b
... work work work ...
git commit -a -m ""
... more work / commit cycles ...

Once I had completed the task ( integrating the spark engine as it happens – what a joy to work with that is ), I jumped back to the master to commit my changes back to the upstream repository. I rebase the changes from my topic branch onto the master so that I end up with a linear commit history that doesn’t confuse git svn.


git checkout master
git svn rebase
git rebase
git svn dcommit

And this is where the trouble started. git svn completed the first commit of 4 and then decided there were no more changes between current head and refs/remotes/git-svn so it reset to the latest refs/remotes/git-svn and left me with a number of unstaged files that made up the changes for the remaining 3 commits. Poking around with git log showed me that git still had the commits in master but I could no longer dcommit them due to git and svn being out of sync.

To clean up git I reset master to the last committed svn revision and then removed all of the git commits after that using a combination of git svn reset -r , git reset –hard and git clean -f ( I can’t remember the exact sequence of commands so I won’t post them just yet ). Once I had both git and git svn synchronised back to the point of the last commit, I cherry picked each commit from the topic branch and dcommitted them one at a time to the remote subversion repository:


git checkout
# Use this to get the sha1 commit tags of the commits to cherry pick
git log
git checkout master
# Repeat the following for each commit on the topic branch that needs to be dcommitted
git cherry-pick
git dcommit

This is essentially the same as git rebase / git svn dcommit except you are pulling the commits one at a time.

While I still don’t understand why git svn dcommit refuses to work for multiple commits, at least I can continue using it to work offline, although I think I’ll be using git merge –squash going forward.

Categories: Git

Creating entities in unit tests

August 23, 2009 Neal 1 comment

An interesting question came up on twitter a moment ago about creating entities for unit tests, what follows is the approach that I use to enable me to create entities with the minimum possible code yet the maximum possible flexibility.

I have two entities Account and User, the code for them is shown below.


    public class Account : Entity<Account>
    {
        public Account( string accountName )
        {
            Name = accountName;
            Users = new List<User>();
            Questionnaires = new List<Questionnaire>();
        }

// ReSharper disable UnusedMember.Local
        private Account()
        {
            // Required by NHibernate
        }
// ReSharper restore UnusedMember.Local

        public string Name { get; private set; }
        public User Owner { get; private set; }
        public IList<User> Users { get; private set; }
        public IList<Questionnaire> Questionnaires { get; private set; }

        public User CreateUser( string username, string passphrase, string name, string emailAddress, string country, string timezone, bool asOwner )
        {
            var user = new User( username, passphrase, name, emailAddress, country, timezone, this );
            Users.Add( user );
            if( asOwner )
            {
                Owner = user;
            }

            return user;
        }

        public Questionnaire CreateQuestionnaire( string name )
        {
            Contract.Requires( () => name );
            var questionnare = new Questionnaire( name, this );
            Questionnaires.Add( questionnare );
            return questionnare;
        }
    }

    public class User : Entity<User>
    {
        public const int MaxAuthenticationRetryCount = 5;

        public User( string username, string passphrase, string name, string emailAddress, string country, string timezone, Account account )
        {
            Contract.Requires( () => username );
            Contract.Requires( () => passphrase );
            Contract.Requires( () => name );
            Contract.Requires( () => emailAddress );
            Contract.Requires( () => country );
            Contract.Requires( () => timezone );
            Contract.Requires( () => account );
            Username = username;
            Passphrase = passphrase;
            Name = name;
            EmailAddress = emailAddress;
            Country = country;
            Timezone = timezone;
            Account = account;
            EmailAddressConfirmed = false;
        }

// ReSharper disable UnusedMember.Local
        private User()
        {
            // Required by NHibernate
        }
// ReSharper restore UnusedMember.Local

        public string Username { get; private set; }
        public string Passphrase { get; private set; }
        public string Name { get; private set; }
        public string EmailAddress { get; private set; }
        public string Country { get; private set; }
        public string Timezone { get; private set; }
        public Account Account { get; private set; }
        public bool EmailAddressConfirmed { get; private set; }
        public int AuthenticationRetryCount { get; private set; }

        public void ConfirmEmailAddress()
        {
            EmailAddressConfirmed = true;
        }

        public bool Authenticate( string passphrase )
        {
            Contract.Requires( () => passphrase );
            if (IsAccountLocked())
            {
                return false;
            }

            var isAuthenticated = String.Compare( Passphrase, passphrase, StringComparison.InvariantCulture ) == 0;
            if (isAuthenticated)
            {
                AuthenticationRetryCount = 0;
            }
            else
            {
                AuthenticationRetryCount += 1;
            }

            return isAuthenticated;
        }

        public bool IsAccountLocked()
        {
            return AuthenticationRetryCount >= MaxAuthenticationRetryCount;
        }

        public void ResetPassphrase( string newPassphrase )
        {
            Contract.Requires( () => newPassphrase );
            Passphrase = newPassphrase;
        }

        public void UnlockAccount()
        {
            AuthenticationRetryCount = 0;
        }
    }

To manage creation of these entities during testing I have a static factory class for each that manages creating the entities and optionally populating any properties ( often using reflection ). The source for these is as follows:

    public static class AccountFactory
    {
        public static Account Create()
        {
            var account = new Account( "Account Name" );
            UserFactory.Create( account, true );
            return account;
        }
    }

    public static class UserFactory
    {
        public static User Create()
        {
            var account = AccountFactory.Create();
            return Create( account, true );
        }

        public static User Create( Account account, bool asOwner )
        {
            var user = account.CreateUser( "Username", "a valid passphrase", "Name", "email@address.com", "New Zealand", "GMT+12", asOwner );
            return user;
        }

        public static User WithUsername( this User user, string username )
        {
            user.SetPropertyValue( u => u.Username, username );
            return user;
        }

        public static User WithPassphrase( this User user, string passphrase )
        {
            user.SetPropertyValue( u => u.Passphrase, passphrase );
            return user;
        }

        public static User WithEmailAddress( this User user, string emailAddress )
        {
            user.SetPropertyValue( u => u.EmailAddress, emailAddress );
            return user;
        }

        public static User MimicFailedAuthenticationAttempts( this User user, int failedAttempts )
        {
            user.SetPropertyValue( u => u.AuthenticationRetryCount, failedAttempts );
            return user;
        }

        public static User MimicLockout( this User user)
        {
            user.SetPropertyValue( u => u.AuthenticationRetryCount, User.MaxAuthenticationRetryCount );
            return user;
        }
    }

The create methods return a valid entity initialised with a default set of values that can be used for testing any scenario you like. If you need to customise the data in the entities to fit a specific test scenario, the extension methods allow additional tweaking of the entity data ( often via reflection ).

This approach allows me to build entities quickly for testing purposes while ensuring that I don’t have to maintain a God class that manages building many different entities and their relationships, it is essentially convention over configuration for my entities.

Categories: Unit Testing, c#

A new approach to agile

August 18, 2009 Neal Leave a comment

I’m tired of hearing and reading about organisations that are “agile” when I know they aren’t.  Agile is the trendy new (old) thing, and as Janusz Gorycki points out so well in his post titled Agile is Dead, the main reason we have AINO ( agile in name only ) is PHBs.

I’m propose we create a new agile methodology, one that takes from scrum and xp and lean and provides a loose framework of guidelines designed to be used by competent professionals rather than talked about by PHBs and certificate hungry consultants.

To ensure managers don’t want to be known for having implemented this new methodology, I propose we call it

“Getting Shit Done”

How many PHBs want to front up to their bosses and explain they need to spend money on “getting shit done” – it’s what they are paid to do anyway, isn’t it?

To drive away the money grubbing courseware consultants, the methodology should be simple and the first rule of this new methodology should be:

You can’t teach getting shit done, you can only learn it

Now if it were only as simple to do as it is to write :(

Categories: General