Git Tricks – Forking Pattern
I have been using Subversion now for 5+ years, and to be honest, I thought it was great. Until I started working on more complex branches, or had some dredded unresolvable mess because I had cherry picked merges across branches and then was trying to reintegrate the branch, and ended up wasting hours and having to double check the final merges did actually merge as expected…
…But the real reason I stopped thinking Subversion was great and saw the power of Git was because I actually had a reason to use Git. Sure I have occasionally contributed something on GitHub, but most of the time if I need to make a pull request or otherwise, I stumble through a bunch of Stack Overflow articles not really understanding what’s going on – or thinking of it in terms of Subversion before finally making the pull request (what even is that?!) and breathing a sigh of relief as it was all over.
The Git Revolution
I started a new job where they use Git. It’s amazing… It really is. And furthermore, I am so glad I had a reason to learn it. The whole point of this post is really to share this tutorial from Atlassian. It takes about 2/3 hours to work through, and by the end of it you will be thinking in Git terms, instead of Subversion (or otherwise).
Irritatingly, Subversion now just feels heavy and clunky.
I do have a couple of hints and something to share as well though. We use the Forking Workflow at work – this is basically the workflow used on GitHub when you are contributing to someone else’s repository – you fork first, then make a pull request from your fork repository to their repository. It provides a useful layer of control as only people with write access to the main repository (the one everyone forked from) has the access level to merge the pull requests. It also means the people working in their forked repositories can rebase commits if they want (one of the hugely powerful aspects of Git) without messing up everyone else (as long as other people aren’t using their fork). This means you have a well planned series of commits when finally making the pull request to the main repository, and the history will be so much better for it.
In Git you branch all the time. For any small piece of work, you create a branch first. You also swap around branches on the local repository non-stop. It’s brilliant. Pull requests are then made against branches – this means that you can add commits to that branch if people have feedback, and they automatically get included in that pull request (conversely, this is why it is so important to do each bit of work in its own isolated branch!)
After merging in a branch (via a pull request) on the repository it is normal to delete the branch afterwards.
On your local repository, you then run
git fetch -p where
-p tells Git to prune remotely deleted branches from the local repository.
However, if you are working on the forked repository where you created a branch, you make the pull request to the main repository, and afterwards, the branch gets deleted but it will not prune from your local copy (although the tracking will get removed when it occurs).
In this instance, I have created a handy script to remove merged branches from your local repository – you can find it on Gist here, or see below:
#!/bin/sh # # This script helps to automatically synchronise a GitHub fork to the upstream repository # # It will perform the following functions: # - Fetch from upstream # - Prune branches (disconnect from remote) # - Swap to the master branch # - Merge upstream/master into origin/master # - Remove local branches which have been pruned # # NOTE: The upstream repository should be added with: # $ git remote add upstream <url/ssh> # # NOTE: Changes should be pushed to the origin repository after # # Mike Goodfellow 2015 # https://mikegoodfellow.co.uk # # Fetch upstream changes git fetch upstream # Prune deleted branches git fetch -p # Swap to master branch git checkout master # Merge in the changes from upstream/master git merge upstream/master # Check if any branches need pruning git branch --merged master | grep -v 'master$' if [ $? -eq 1 ]; then echo "No local branches need deleting" else echo "Local branches need deleting" git branch --merged master | grep -v 'master$' | xargs git branch -d fi echo "Finished!"
Quick Guide: Git Forking Workflow
Here’s a quick guide to a Git Forking Workflow – assume you have a main repository (Note: these examples are using SSH instead of HTTPS) –
1. Fork the main repository
2. Clone your repository to a local repository:
git clone email@example.com:yourName/SomeProject.git
3. Define the main repository as the “upstream” (where upstream is simply a name we are giving the remote):
git remote add upstream firstname.lastname@example.org:somePerson/SomeProject.git
You can view your current remote repositories using:
git remote -v
4. Create a new branch called
new-feature-branch and checkout it out:
git checkout -b new-feature-branch
5. Make some changes to the local copy, and then stage them when ready:
git add <files>
6. Commit these changes to the local repository:
7. Push these changes to your fork repository (it doesn’t exist yet, so setup the remote tracking first):
git push -u origin new-feature-branch
For any future pushes, you can now just use:
8. Now you can open the pull request against that branch on the fork repository to the main repository
9. If you need to provide more commits to the pull request, make the changes locally, then use the following set of commands:
git add <files> git commit git push
This barely even scratches the surface of the power of Git (see rebasing among other things!) but still, hopefully some of this guide will help.