These days, most software developers collaborate using Git. In this follow up to an introduction to Git and How to Get Started with GitHub, you’ll get some hands on experience working with branches in Git and learn how you can use them to collaborate effectively with your team.
Creating a project
Let’s start by creating a repository and pushing it to GitHub.
If you’re on a Mac or Linux, open a terminal window. If you’re using Windows, open up Git Bash.
Let’s see what directory we’re in:
> pwd /Users/peterbell
And Change Directory (cd) to somewhere that you keep your code. For me that’s:
> cd code/flatiron
And let’s make sure we’re not in a Git repository by mistake:
> git status fatal: not a git repository (or any of the parent directories): .git
Perfect - we’re not! Next up, let’s create a new git repository and cd into it:
> git init branching-demo Initialized empty Git repository in /Users/peterbell/Dropbox/code/flatiron/branching-demo/.git/ > cd branching-demo
Great. Now let’s create a file, add and commit it to Git:
1 2 3 4 5 6 7 8
> echo "Hello world" > index.html > git status On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) index.html nothing added to commit but untracked files present (use "git add" to track)
1 2 3 4 5
> git add . > git commit -m “Create home page” [master (root-commit) 1abfba0] Create home page 1 file changed, 1 insertion(+) create mode 100644 index.html
And now let’s go to GitHub, click on the “+” in the top right hand corner, and select “New Repository” from the dropdown.
Fill out the form, giving it the same name as we used for the directory, make it public (or private if you don’t want to share), make sure not to check the “initialize with README” option, and click the “Create Repository” button.
And then cut and paste the instructions for “push an existing repository from the command line.” In my case it is:
1 2 3 4 5 6 7 8 9
> git remote add origin https://github.com/PeterBell/branching-demo.git > git push -u origin master Enumerating objects: 3, done. Counting objects: 100% (3/3), done. Writing objects: 100% (3/3), 225 bytes | 225.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) To https://github.com/PeterBell/branching-demo.git * [new branch] master -> master Branch 'master' set up to track remote branch 'master' from 'origin'.
Refresh the page in your browser, and congratulations - your project is now up on GitHub!
The purpose of branches
Now that we’ve got a project to play with, let’s think a little bit about what branches are and why we might use them. Branches allow you to create different versions of your code. These days one of the most common uses is for “feature branches” where you add a new feature off on a branch. The benefit of feature branching (when done right) is that it allows you to keep master (the main branch) releasable.
Say you’re in the middle of building a big new feature and you need to quickly fix a bug in production. You can just commit your changes to master, test them, and push them to production rather than having to wait for the new feature to be completed before you can roll out any changes.
Your first feature branch
Time to give it a try. Let’s imagine that we want to create an “About Us” page for a website and we decide to use a branch to do so. Firstly, let’s check what branches we have locally:
> git branch * master
Unsurprisingly, there’s just the master branch, because we haven’t created any others yet. Now let’s create a new branch and confirm that it worked:
1 2 3 4
> git branch about-us > git branch about-us * master
(The * next to master lets you know that is the branch you are on)
And now let’s check out (go to) the branch:
> git checkout about-us Switched to branch 'about-us'
This is a common enough operation to have a shortcut - we could have just typed git checkout -b about-us which would have told Git to both create and check out the branch.
OK, now let’s create an About Us page, and add and commit that to the about-us branch we’re on:
1 2 3 4 5 6
> echo "About us" > about.html > git add . > git commit -m “Add about us page” [about-us 644a0bc] Add about us page 1 file changed, 1 insertion(+) create mode 100644 about.html
Perfect. So now we have this work on a branch. Let’s share it with our team by pushing it up to GitHub:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
> git push -u origin about-us Enumerating objects: 4, done. Counting objects: 100% (4/4), done. Delta compression using up to 8 threads Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 283 bytes | 283.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) remote: remote: Create a pull request for 'about-us' on GitHub by visiting: remote: https://github.com/PeterBell/branching-demo/pull/new/about-us remote: To https://github.com/PeterBell/branching-demo.git * [new branch] about-us -> about-us Branch 'about-us' set up to track remote branch 'about-us' from 'origin'.
What we did was we told Git to push the branch we were on, and to create an “upstream” branch on “origin” (the default name for your first remote location — quite often GitHub).
Now go refresh the page on GitHub and you’ll see that there are two branches now:
But where is the about.html? Well, we’re looking at the master branch and the work isn’t there yet. Click on where it says “2 branches” (near the middle of the page), then click on the “about-us” branch and it’ll show the latest work there.
Great, so we have created a new “feature branch”. As far as Git is concerned, it's just a branch. But, given the naming we used, it’ll be pretty obvious to our team it’s a new feature branch for working on the About Us page.
From a branch to a pull request
At this point anyone could clone (download a copy of) the repo, git checkout about-us and work on the branch. But it might be nice to have a place where we could all discuss the branch and share mockups, thoughts, or questions about the new page. To do that, let’s create a Pull Request. Click on the Pull Requests tab near the top of the page, then click the “New Pull Request” button. Leave base as master and set compare to “about-us,” and click the “Create pull request” button. You can then fill out the title and description for the PR, and then click the button to “Create pull request”.
OK, now we have a URL we can share with the rest of the team to discuss the About Us page (for me it’s https://github.com/PeterBell/branching-demo/pull/1 ). Anyone (technical or not) can now engage with any conversation we might want about the new feature.
Perhaps someone suggests we should tell people how great we are...
And we can do that. Let’s go back into our terminal window, open the about.html file with any text editor (I’ll use vi), and add “We are a really great company.” Then add and commit the changes, and push them to GitHub:
> vi about.html > git add .
1 2 3
> git commit -m "Mention how great we are" [about-us 14fdd71] Mention how great we are 1 file changed, 2 insertions(+)
1 2 3 4 5 6 7
> git push Enumerating objects: 5, done. Counting objects: 100% (5/5), done. Delta compression using up to 8 threads Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 320 bytes | 320.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0)
And now our changes show up right in the pull request page so even non-technical members of the team can see what we’ve done!
You see the link next to my commit message? For me the link is the 14fdd71 - the SHA 1 hash (a unique identified) for the commit. Your commit id will be different, but click on it and you’ll see the commit and the changes within it:
At some point in time, you’ll be done with the new feature. You’ll have written the code, got sign off on the wording, confirmed the design looks good on mobile and made sure you have some automated tests to check that the functionality works like you’d expect. At that time, you want to “merge” it back into master. You can either do that in GitHub (see that “Merge pull request” button?!) or on the command line. Let's just use the command line so we know how to do it.
Firstly go to (check out) the master branch, then merge in the `about-us` branch:
1 2 3
> git checkout master Switched to branch 'master' Your branch is up to date with 'origin/master'.
1 2 3 4 5 6
> git merge about-us Updating 1abfba0..14fdd71 Fast-forward about.html | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 about.html
Now push those changes:
1 2 3 4
> git push Total 0 (delta 0), reused 0 (delta 0) To https://github.com/PeterBell/branching-demo.git 1abfba0..14fdd71 master -> master
And refresh the PR page in the browser. You’ll see that it now shows as being merged:
And if you go back to the main project page (just click the “branching-demo” link at the top left of the page), you’ll see that the about page has now been added to the master branch.
It’s important to understand how to work with branches in Git. They are useful and they’re the default way that most professional dev teams currently collaborate. But now that you’re learned the basics of branches (we’ll get into more advanced topics like rebasing and resolving merge conflicts later in this series), it’s also good to learn about the trend towards trunk based development.
Years ago, when teams wrote software, typically there was a specification phase (what are we gonna build?), an implementation phase (let’s go code it!) and an integration phase (let’s get it working with everyone else’s code). You might have 5 or 6 teams of developers all working on their different features using separate branches in their version control system of choice (perhaps cvs or svn back then) for maybe 6 months or a year and then you’d have a month or two of integration at the end.
Unfortunately, that didn’t usually work very well. Firstly, the integration period was usually painful as each team found their code was conflicting with what the other teams had done (perhaps two teams created a new piece of code with the same name doing different things) and secondly, the month for integration often stretched out into three or six months - or even longer.
There is a popular saying in the dev world that “if something hurts, do it more often”. In the case of integration, it’s saying that if you integrate more frequently then it’ll hurt less (because you’ll have less conflicting code and it’ll be fresher in your mind). It’s where the whole “Continuous Integration” movement comes from with products like CircleCI and Travis CI that automatically run your tests against all of your code every time you push it to GitHub, reducing the time it takes to find and fix regressions (regressions are new bugs that you introduced into existing functionality).
Most high performing dev teams have settled on the “short lived feature branch” compromise which is where each team works on new features using feature branches, but they merge them into master every 0.5-3 days. Where a feature isn’t fully built out, they use a tool like LaunchDarkly to hide the new functionality behind “feature flags” until it’s ready to share with end users. In that way, they can make sure their code integrates with the code being written by other teams while still working on features that might take a couple of weeks to finish out completely.
But be aware, there is now a trend towards “trunk based development”. With that trend, you simply commit to master and use feature flags to keep your code from showing for end users, simplifying the workflow and minimizing the pain of integration by effectively integrating every time you push your work to GitHub.
Whatever workflow your team ends up using, it’s important to be familiar with how to use branches in Git and Pull Requests in GitHub. Keep an eye out for upcoming articles covering resolving merge conflicts, rebasing, and undoing almost anything using Git!
Head of Data Science
Peter is a veteran technologist, CTO, entrepreneur, and longtime educator, having taught digital literacy at Columbia and authored numerous programming books.