Tuesday, 17 April 2012

Understanding Distributed Version Control Systems

This post is based on a short article I wrote to explain DVCS to my colleagues at work (hence the comparison with TFS). For those wanting to go further, I strongly recommend Eric Sink’s book Version Control By Example. I’ll be presenting some of this material at an upcoming session at NxtGenUG in Southampton.

The last decade has seen a lot of innovation in version control systems, with the rise of the Distributed Version Control System the most notable development. Distributed Version Control Systems (DVCS) are an idea that was first implemented in the 90s, but in the last five years, open source DVCS such as Git and Mercurial have rapidly started to gain market share over the more established centralized version control systems, such as SourceSafe, TFS or Subversion.

 

The Centralized Model

In the centralised version control systems (CVCS) that you are already familiar with, every developer connects to the central server and asks for the latest version of the source code. They then work locally on their machine until they have completed a feature and then they check in. As soon as they check in, those changes are made available on the central server for other developers. If someone checked in before them, they must get latest and merge before they check in.

 

Understanding DVCS with DAGs

To understand Distributed Version Control systems, you need to think of your repository in terms of a directed acyclic graph (a “DAG”). Each node in the graph represents a revision, and each arrow represents which revision the current revision changes (it’s ‘parent’ node), so the arrows go in the opposite direction to time.

Imagine that our repository has had three commits (1, 2 and 3). This can be represented by the following simple DAG. Revision 3 is a change based on revision 2, and revision 2 is a change based on revision 1.

dvcs1

Cloning

With a CVCS, we would do a get latest and just get the state of all files at revision 3. However, in the world of DVCS, we do a clone instead. This means that we take a copy of the whole DAG. (n.b. This makes some people concerned about speed, but a clone is only done once, and DVCS are well known for their much faster speeds than CVCS). Suppose BILL decides to take a clone from the server. Now his local copy looks like this:

dvcs2

In other words, it is identical to the SERVER. This is why people say that you don’t need to have a central server for DVCS, since we could take away SERVER and nothing would be lost. However, in practice, it usually makes sense to designate one computer as the central repository.

 

Committing

Now BILL wants to make some changes. He writes some code and performs a commit. Now his DAG has an extra node in it:

dvcs3

However, unlike a checkin in TFS or another CVCS, nothing has been sent to the server. Revision 4 is only on BILL’s machine.

BILL can actually carry on developing and do another commit. Now he has two local revisions that the SERVER doesn’t have.

dvcs4

Already, the benefits should be obvious. It is like BILL has his own personal branch to work on. If Revision 5 was a mistake, he can roll back to Revision 4 and try again. He is able to make regular and often small checkins without fear that he will break the build by committing to the SERVER.

 

Pushing

At some point, BILL is ready to share his work with everyone else so he does a push. This simply compares his DAG (which contains Revisions 1-5) with the SERVER’s DAG (which currently still only has revisions 1-3). When he does a push, the DVCS works out that the server needs revisions 4 and 5, so it appends them onto its own DAG. Now the server’s DAG is identical to BILL’s again.

dvcs5

 

Pulling

But what if someone else got in there before BILL? Maybe the SERVER now looks like this, with revisions 10 and 11 having been pushed from someone else:

dvcs6

Maybe we would like this to happen when we push:

dvcs7

But this is not allowed (and in fact not possible). Revision 4’s parent is Revision 3, not Revision 11, so we can’t just stick it on the end of our DAG. What will happen is much the same as with a CVCS, which will block you from checking in, and tell you that you need to do a get latest to merge those changes into your working copy. A DVCS will warn you that you probably don’t want to do a push because it will create two “heads”, and instead you should do a pull and a merge.

So BILL does a pull, which is the opposite of a push. It looks at the SERVER and sees what nodes it has that he doesn’t. In this case, it is revision 10 and revision 11. They are pulled from the SERVER and added to his DAG. But notice that now both Revision 4 and Revision 10 are changes to Revision 3, and it means that our repository now has two “heads” - revision 5 and revision 11.

dvcs8

The good news for BILL is that his local commits, 4 and 5 are still perfectly safe and intact. Unlike a get latest with CVCS, no automatic merging has taken place that could overwrite or break anything he has already done. The merge takes place as a separate step.

 

Merging

Now that BILL has the latest changes from the SERVER, he performs a merge. If there are no conflicts then this is quite trivial. If someone else has changed the same bit of code as him, then he must use the typical merge tools you are already familiar with to select which change is the correct one – there is no getting round this. However, once he has done the merge, he then makes another local commit, in this case, revision 6. Now his repository has only one head and is ready to be pushed to the SERVER.

dvcs9

 

What are the benefits of DVCS over CVCS?

Hopefully some of the benefits of DVCS over CVCS are already apparent, but let me list a few.

First, it promotes little and often checkins. With a CVCS, you only want to check in if you are 100% sure that your work is fully tested and won’t break the build. This encourages developers to work sometimes for weeks at a time without checking in. With DVCS, you can check in dozens of times a day, and still only push to the SERVER when you are ready. (n.b. it is up to you whether you want to combine all your local checkins in to one before you push to the server. Different developers have different philosophies about this).

Second, it gives every developer multiple personal branches. It is common to have to context switch in the middle of working on something (“drop everything and fix this bug right now”). Whilst TFS has Shelvesets and Workspaces that allow you to separate out multiple tasks you are working on, they are rather cumbersome to use, and end up getting neglected. With DVCS, it is trivially easy to create another local branch (or clone, depending on your preference), to work on the new feature. You can even merge freely between your local branches if necessary.

Third, it allows ad-hoc teams. You don’t have to push everything via the central server. If you are working with another developer and want to share some work-in-progress changes you have made, you can simply pull from each other. Despite both developers now having the same revisions in their local DAG, each one will only get pushed to the server once, and will be attributed to the developer who wrote the code. TFS shelvesets cannot do this.

Fourth, it gives complete branching flexibility. With TFS you can only safely merge into a parent or child branch. This restriction is completely removed with DVCS. You are free to pull or push to whatever branch you want. Obviously there will still need to be processes in place that say what branches ought to be pushed to.

Fifth, it allows for much more flexible handling of merges. Unlike with a CVCS you are not forced to deal with them the moment you do a get latest. Your local repository can have two heads for a time, allowing you to defer the merge until you are ready. It also no longer means that the first developer to check in wins, while the second is lumbered with the merge. You can ask the developer who made the conflicting changes to pull from your local repository, perform the merge, and then you can pull that merge back from them.

Sixth, it allows disconnected working. If you need to work from home without network access, you can still commit locally and push when you have access to the network. This is also good for outsourced teams. They can make commits to their clone of your repository, and you can pull those revisions into your own without ever needing to give them commit access to your own server.

Seventh, it is great for backup. The central server could suffer a catastrophic disk failure, but it can be instantly recreated by cloning from a developer’s repository. Also, a developer can easily backup their work in progress by pushing to a repository on another computer.

 

What are the limitations of DVCS?

So far I have painted a very positive picture of DVCS. Is there anything it doesn’t do well?

One feature offered by some CVCS that you will probably lose is the ability to lock files so that only one person can work on them at a time. This is most often needed for binary assets. Obviously if your developers are disconnected from a central server, they have no way of knowing whether someone else is also working on a file. However, some DVCS have extensions allowing you to manage files that need to be locked via a central server.

DVCS are not typically a good choice if you want to store very large files or to have very large repositories where people might not want to do a get latest of every folder in the repository. Most users of DVCS simply develop practices that don’t require storing huge binaries in source control, and split vast projects up into smaller sub-projects.

With a DVCS, history is immutable. If you want to modify revision 2, or expunge it altogether from the history of your source control, you can get into trouble, as you effectively end up with a completely new DAG. When the next user does a push, the old revision 2 and its ancestors will come back. You have to make the change on a central repository and then ask all your developers to destroy their local repositories and re-clone from the central one to permanently change history.

 

How do I get started?

There’s nothing stopping you trying out DVCS for yourself, and I strongly recommend it for any small projects you write. Even if you are the only developer it is great to have a commit history to remind you what you were doing, and the ability to go back in time if you make a mistake. I use Mercurial for all my personal projects, and if you ever collaborate on open source projects you will likely end up needing to learn git at some point, as it is the most popular.

Tuesday, 20 March 2012

The Perils of Removing Features

In the last couple of weeks, Microsoft have released preview versions of two new software products. First up is Windows 8, the highly anticipated successor to Windows 7, sporting the brand new “Metro” interface, which promises to deliver a Windows UI suitable for tablet PCs. The other new product is Visual Studio 11, which debuts a number of key new features, including the next version of .NET and the ability to create Metro applications for Windows 8. All very exciting, and lots of us rushed to try out the preview versions as soon as possible.

Given that both products essentially are upgrades to existing and successful products, one would expect the reaction to these new products to be very positive. Both are loaded with cool new features and usability improvements, and yet the majority of the online chatter has been to complain.

What is the problem? As well as giving us new stuff, both products have taken something away. Windows 8 has taken away the Start menu, and VS 11 has taken away the colours from the icons. These two omissions, though relatively minor in the context of the overall changes, have managed to dominate the reaction to the new products.

One senses a little bit of frustration on the part of Microsoft (e.g. these two posts from Scott Hanselman), and you can understand why. All that hard work on new features has seemingly gone unnoticed, and all people can do is moan about two decisions that were intended as a bold step to improve user experience.

So what is the moral of the story? Users hate having features taken away from them. It doesn’t matter if you tell them that the feature removed was redundant, or poorly implemented. They feel robbed and short-changed. They feel like they are not understood. And they resent having to change the way of working they have been comfortable with.

Microsoft have a few options available. First, they stick to their guns, and say “you’ll thank us later, once you understand”. That may of course be the case, but is it really worth upsetting your customers over this? Second, they back down, and give us back our start menu and our coloured icons. A victory for the customer perhaps, but that is a recipe for never making any progress. The third option is to make it configurable. Let us choose if we want the old-fashioned start menu, or the colourful icons. If the new UX is truly an improvement, over time, people will be won over, and the obsolete features can be removed in the next version.

In summary, if you want to remove a feature from your application, even if you consider it to be an unnecessary one, consider first making it optional (whether that be on or off by default). The customer isn’t always right about what the best UX is, but they are right about what they like, and if they don’t like it, they won’t buy it. It will be very interesting to see which direction Microsoft take when the next preview versions come out. If they get it right, I’m sure they’ll get more bloggers talking about how cool these new products are, instead of moaning about what was taken away from them.

Saturday, 10 March 2012

Creating Resizable Shape Controls in WPF

In WPF, you can create a resizable shape by using the Path control. Simply set up your shape’s path data and then set the Stretch property to Fill and your shape will resize itself to fit the space available. It even takes into account the stroke thickness.

But what if you want a shape that resizes differently? For example, you might want the corner radius of a rounded corner to stay constant, irrespective of how wide or tall you made your shape. In that case, things get quite a bit more complicated as you can’t use the Stretch=Fill technique.

I’ve recently built a small open source library of useful WPF shapes (available on NuGet), and in this post, I’ll briefly explain how to make your own resizable WPF shape.

Custom Shape Using Stretch.Fill

The first and simplest example is a shape that does use Stretch.Fill. If proportional stretching is all you want, then this is the simplest way to do it. Here’s a simple Diamond (Rhombus) shape:

public class Diamond : Shape
{
    public Diamond()
    {
        this.Stretch = Stretch.Fill;
    }

    protected override Geometry DefiningGeometry
    {
        get { return GetGeometry(); }
    }

    private Geometry GetGeometry()
    {
        return Geometry.Parse("M 100, 0 l 100, 100 l -100, 100 l -100, -100 Z");
    }
}

There are only two things we needed to do. First is to set the Stretch property to Fill by default, saving the users of our control the need to do that in XAML. And then override the DefiningGeometry. I like to use the XAML Path mini-language as I find it to be easy to use. Here I just draw a diamond, using “Z” to close the shape at the end. It doesn’t matter what size I draw the shape, since we are stretching to fit.

Adding Custom Properties

Another useful thing to be able to do is add custom properties to your Shape object. These should be dependency properties to fully benefit from the power of the WPF framework. For example, in my Triangle shape, I added a TriangleOrientation property that allows you to select what direction your triangle is pointing in.

public enum Orientation
{
    N,
    NE,
    E,
    SE,
    S,
    SW,
    W,
    NW
}

public Orientation TriangleOrientation
{
    get { return (Orientation)GetValue(TriangleOrientationProperty); }
    set { SetValue(TriangleOrientationProperty, value); }
}

// Using a DependencyProperty as the backing store for TriangleOrientation.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty TriangleOrientationProperty =
    DependencyProperty.Register("TriangleOrientation", typeof(Orientation), typeof(Triangle), new UIPropertyMetadata(Orientation.N, OnOrientationChanged));

private static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs ek)
{
    Triangle t = (Triangle)d;
    t.InvalidateVisual();
}

Calling InvalidateVisual when the TriangleOrientation property changes causes DefiningGeometry to be called again. Now we simply have to return the correct geometry for the selected orientation:

private Geometry GetGeometry()
{
    string data;
    if (TriangleOrientation == Orientation.N)
        data = "M 0,1 l 1,1 h -2 Z";
    else if (TriangleOrientation == Orientation.NE)
        data = "M 0,0 h 1 v 1 Z";
    else if (TriangleOrientation == Orientation.E)
        data = "M 0,0 l 1,1 l -1,1 Z";
    else if (TriangleOrientation == Orientation.SE)
        data = "M 1,0 v 1 h -1 Z";
    else if (TriangleOrientation == Orientation.S)
        data = "M 0,0 h 2 l -1,1 Z";
    else if (TriangleOrientation == Orientation.SW)
        data = "M 0,0 v 1 h 1 Z";
    else if (TriangleOrientation == Orientation.W)
        data = "M 0,1 l 1,1 v -2 Z";
    else 
        data = "M 0,0 h 1 l -1,1 Z";
    return Geometry.Parse(data);
}

Controlling Resize Behaviour

As I mentioned at the start, controlling resize behaviour unfortunately is not nearly so simple. My first attempt was to use the control’s ActualHeight and ActualWidth property, or the RenderSize property while I created the DefiningGeometry. But that actually caused really strange resize behaviour with the shape growing larger as you resized the control smaller.

The trouble is caused by the fact that the parent container is asking the Shape “how much space do you need?” and the shape is asking the container “how much space have you got?”. Someone needs to make a decision what the size will be. The solution is for our Shape to say that it will take up the full amount of space it is being offered, by overriding MeasureOverride to simply pass back the constraint we are given:

protected override Size MeasureOverride(Size constraint)
{
    // we will size ourselves to fit the available space
    return constraint;
}

Now in the Geometry method, we can examine the ActualWidth and ActualHeight properties and use those to know how much space we have to draw the shape in. Unfortunately, we have to take the StrokeThickness into account ourselves, so make sure you leave a margin round the edge of at least StrokeThickness / 2 (it may even need to be more depending on your line join style and the angle of line joins). Here’s the code for my Label control, which is a rectangle with the top left and bottom left corners cut off. It has a CornerWidth dependency property to define the amount of corner to cut off, which stays constant however big you resize the control:

private Geometry GetGeometry()
{
    double cornerWidth = CornerWidth;
    double width = ActualWidth - StrokeThickness;
    double height = ActualHeight - StrokeThickness;

    return Geometry.Parse(String.Format("M {0},{1} h {3} v {4} h -{3} l -{2},-{2} v -{5} Z", 
        cornerWidth + StrokeThickness/2, StrokeThickness/2, cornerWidth, width-cornerWidth, height, height-(2 * cornerWidth)));
}

Try it out

You are welcome to try my shapes out for yourself. The code is available on CodePlex. I welcome any contributions to the library either of new shapes, or adding some more dependency properties onto the existing shapes to allow for better customisation. The current shapes are Speech Bubble, Diamond, Rounded Sides Rectangle, Hexagon, Label, Triangle (available in several orientations) and Chevron. Here’s what the shapes in the first version look like being resized:

shapes

Saturday, 3 March 2012

NAudio Preview Versions

NAudio 1.5 was the first release of NAudio I managed to get up onto NuGet. Although the vast majority of NAudio users still continue to get their downloads from CodePlex, I’m hoping that NuGet will become the preferred means of adding it to a project, as it makes adding a reference very simple and allows easy upgrades to new versions.

Recently, NuGet added a feature that allows you to upload preview releases, by using semantic versioning such as NAudio 1.5.1-beta. Packages versioned like this won’t appear by default, but can be installed using the NuGet package manager console window using the –Pre switch on the Install-Package command.

Install-Package NAudio -Pre

The benefit of this is that it gives me a very quick and simple way of uploading preview versions of NAudio. This is useful for me, as I use NAudio in a lot of small projects, and often I want to use the features I have just added. The ability to push pre-release versions to NuGet is a big time-saver, and much quicker than creating a full release on CodePlex. So if you are looking for the latest build of NAudio, head over to NuGet. The NAudio feed page will show the latest version available (scroll to bottom for the full list).

Monday, 20 February 2012

To rebase or not to rebase, that is the question

In recent weeks I’ve made my first code contributions to open source projects on GitHub. A number of GitHub hosted projects like their contributors to rebase their changes before issuing a pull request. Since I normally use Mercurial, which doesn’t enable rebasing by default (although is easily turned on with an extension), I haven’t used rebase much except to experiment with it. And rebase is notorious for causing problems if you use it incorrectly.

Why would you want to rebase?

1. Simplified history

The main argument for rebasing is that it makes the history of your repository much simpler. Instead of seeing lots of short-lived branches and merge commits, you get one single linear history for your branch.

This might seem like a case of OCD on the part of the developers wanting rebase, but unfortunately, the task of looking back through source control history is something we’ve all had to do at some time or another, so the desire for it to appear as simple as possible is understandable.

If like me you quite often forget to do a pull before making a change to your local clone, you may end up requiring a merge when you do get round to doing a push. And more often than not, it is a trivial merge, since your change doesn’t conflict with the most recent commits that you forgot to fetch. In this scenario, rebase is able to present your change as though you had remembered to do the pull before starting work, and eliminates the superfluous merge commit.

Also if you work on a feature for a few weeks, you might end up with lots of intermediate merge commits as you keep pulling from the source repository. Rebasing simplifies the history of your feature by eliminating the many (often trivial) merge commits.

2. Commit combining

At the same time as rebasing, it is a common practice to combine your commits into one. Again, this is more about keeping the history of your repository nice and clean. If you made 20 commits to implement a single feature, perhaps with a few of them going in directions that you later backtracked out of, do you really want all those to be pushed up into the master repository?

Again, at some point in the future, someone will be trawling back through history trying to find where something went wrong, and it is a waste of their time to look at a revision that perhaps contains a silly mistake (might not even build), when your work could be combined into one revision.

Having your feature contained in a single commit also makes life simpler for the code reviewer, since they can just look at the diff for that one revision (although your tools ought to be able to show you a combined diff for a range of revisions – I noticed that github does this very elegantly with pull requests that contain multiple revisions).

Some people have called commit combining a “pretend you are perfect” feature – where you show the final outcome of your work without revealing all the stupid mistakes you made along the way. But that shouldn’t be the point of combining commits. You do it to save time for whoever in the future needs to look back at the history of the project.

What are the dangers?

So if rebase makes our history simpler, why would some people not want to use it. There are a few dangers associated with rebasing and combining commits.

1. Rebasing published revisions

The biggest danger with rebase is rebasing already published revisions. This is most likely to happen if you accidentally rebase the wrong way round (rebasing changes you pulled in from elsewhere onto your own work), something I imagine would be quite easy to do by mistake for a beginner with DVCS. Or maybe you forgot or unintentionally already pushed the revisions you are about to rebase to a public repository.

Doing this means that the revisions you rebased, rather than disappearing, will keep coming back again, and end up getting merged back in alongside the rebased versions of the same change. It can be very hard with a DVCS to get rid of revisions you no longer care about once they have been published.

In Mercurial 2.1, there is a new feature called phases, which gives the repository the ability to know which revisions have been published. This means that commands like rebase can now refuse to work on published revisions, making it a much safer command to use. It will be interesting to see how well this works in practice, and if it does work, maybe Mercurial will allow rebase and other history changing extensions, to be enabled by default). Having said that, since (unlike git), Mercurial by default pushes and pulls all heads, you might find you end up sharing a work in progress branch earlier than you intended.

2. Loss of information provided by a merge commit

One of the benefits of rebase, the removal of the merge commit, is also one of its dangers. It is possible for a rebase to complete successfully, with no merge conflicts, but the resulting code to be broken (e.g. one developer adds a call to functionX, while another developer renames functionX to functionY).

The original commit you made may well have been working perfectly and passing all its unit tests when it was in its own branch, but now it has been rebased there is a commit with your name against it that doesn’t build or contains bugs.

With an explicit merge commit, it is much easier to identify the point at which things went wrong. This remains the main reason why I am not convinced that rebase should be a major part of my workflow. The important thing to remember is that a rebase is just like a merge – it needs to be tested before it can be considered a success, even if there were no conflicts.

3. Loss of intermediate revisions

The goal of rebasing and collapsing is to get rid of intermediate revisions. But I wonder whether you could shoot yourself in the foot by over-enthusiastic collapsing of multiple revisions into one. Often after getting a feature working, I might quickly go over the code and do some last minute cleanup, refactoring a few class names, deleting TODO comments etc. But what if I accidentally break something in these final commits? If I collapse to a single commit it is too late to do a revert of the offending revision, or to rewind to the last good revision and make the changes correctly. Keeping your intermediate commits allows you to backtrack to the last good point.

Summary

In short, I think rebase is a useful tool to have available, but one that should be used with caution. Innovations like Mercurial’s phases could make it much safer, but on the whole, I prefer my source control history to show what really happened, rather than what I would have liked to happen in an ideal world.

As always, I welcome your thoughts on this in the comments.

Saturday, 18 February 2012

Modular WPF Screencast Part 3

Due to popular demand, I have finally got round to recording part three of my modular WPF application screencast series. Rather than jumping directly to a fully featured solution, I wanted to show how we might evolve an architecture step by step, and without being afraid to make some wrong choices that we will need to refactor later.

  • Part 1 covered setting up the framework to use MEF
  • Part 2 covered adding MVVM to make it unit testable

In this episode I walk through adding a new feature, the ability to cancel switching between modules, which turns out to be a bit more tricky than we might have anticipated, and ends up with us creating a templated list of buttons to replace our original ListBox. (n.b. an even better choice might have been to use a tab control, but I didn’t think of that when I created this tutorial, so maybe that can be another refactoring for a future episode).

Also, loads of people are asking for the code, so I have made the Mercurial repository publicly available. In the video, the reason you don’t see me coding, is because I am just switching between revisions of my repository (and it also keeps the video much more succinct). I’ve bought a new headset too, so the voice quality ought to be better than before (although I’ll probably reduce the level slightly for next time). I also tried capturing at 1280x720, so there is a bit more screen real estate. Let me know if this is better or worse.

Thursday, 16 February 2012

Fork First or Just in Time?

I’ve been following the progress of Code 52 a bit over recent weeks. It is an audacious attempt to create a new open source project every week for a year. They also seem willing to accept code contributions, so I was tempted to download a few of their projects and make some minor improvements.

I’ve been using Mercurial for my projects at CodePlex for some time now, but I hadn’t used git in anger, and since Code52 store all their projects on the very impressive github, it gave me a good excuse to learn.

I read up a few tutorials on how to fork a repository in github. The official guide is good, and Scott Hanselman wrote a great post on how to contribute to Code 52 projects. But one thing they all have in common, is a workflow of Fork, Clone, Commit, [optional: Pull & Merge], Push, Pull Request. This has always struck me as being the wrong way round. (CodePlex recommends essentially the same workflow for Mercurial).

Fork First

The reason I don’t like this workflow, is that it assumes the first thing I want to do is create a fork. But that’s not how I typically interact with an open source project. My workflow goes like this:

  1. I come across a new open source project and maybe I find it interesting
  2. Often I will just want to download compiled binaries, but maybe I want to explore the code to see how it was implemented
  3. I clone it (git/hg clone) and maybe I will get round to playing with it later
  4. I attempt to build it locally and maybe it succeeds on my machine (surprising how often it doesn’t)
  5. I attempt to use it and maybe I find a bug or I wish it had a new feature
  6. I report the bug or feature to the developers, and maybe I think I could fix it myself
  7. I explore the source code, and maybe I understand it well enough to make a change
  8. I begin coding a fix/feature, and maybe I get it working
  9. I realise my code needs cleaning up before I issue a pull request, and maybe I get round to doing so
  10. If I have made it this far, now is the time I am ready to push to a public fork and issue a pull request. I estimate I get to this step on less than 1 percent of open source projects I come across.

As you can see, it is only at step 10 that I need to have a fork, but the tutorials all want me to make my fork at step 3. This results in lots of projects having multiple forks that have never been pushed to. Or have been pushed to but no pull request ever submitted, leaving you wondering what the status of the changes is.

Just in time fork

In my opinion, forks (which are really just publicly visible clones), should be made just in time. Currently, Code 52’s Pretzel project has 47 forks, and as far as I can tell, many (most?) of them have had no changes pushed to them at all. (In fact, a nice github feature would be to hide forks that have not been pushed to yet, and to highlight forks that have pull requests outstanding).

The just in time fork workflow isn’t difficult. First clone from the main repository. Think of this clone as your private fork if that helps.

git clone https://github.com/Code52/pretzel.git

Once you decide to make some changes to your repo, you can make a branch to work on (not strictly necessary, but recommended).

git checkout –b my-new-feature

Now work away on your feature. You can pull in changes from the master repository, and optionally merge them into your working branch whenever you like.

Once you are sure that you want to contribute to the project, at this point, you create your public fork on github. Now you add it as a remote:

git remote add myfork https://myname@github.com/myfork/pretzel.git

You can now easily push to your github fork. I think it is probably best to also have a feature branch on your github fork, which means that if you wanted to contribute another unrelated feature, you could do that in another branch, and have two pull requests outstanding that weren’t dependent on each other.

git push myfork my-new-feature

The github gui makes it very easy to issue a pull request from a branch.

Summary

Why create dozens of unused forks when it is straightforward to create them at the point they are needed? Am I missing some important reason why you shouldn’t work like this? Let me know in the comments.