How about that title there? Yeah it’s rough.
The other day I went through the process of adding new code to a large commercial GitHub repository. I sort of stumbled through it, so I wanted to make a quick tutorial in case you’re lost too! Or in the much more likely event that I get lost doing it in the future — it’s pretty straightforward.
Local Git Flow
First of all let’s look at Git Flow, the process by which we add code to a website in Git. I’ll go through a pretty common branch model, but lots of people and teams run things a little differently. If you want to know more about this, Atlassian have a walkthrough with an interactive tutorial.
BASIC FOLDER STRUCTURE
I’ll run through the types of branches we can have to understand the flow of fixing an issue or adding a new feature.
- Master — this is your ‘live’ branch, with your active code on it. Generally speaking we don’t push straight to this unless we’re deploying a hotfix.
- Develop — we need a branch to push our features to. Somewhere that looks exactly like Master, but we aren’t in danger of our code breaking on the real website. We use develop, then, as an integration branch. We try a new feature on develop, then commit to master when we’re sure it works.
- Feature — a feature is something that needs adding to the existing functionality of the site. Whether this is a new menu item or a colour change, we make a branch called
- Hotfix — this is similar to Feature that it isn’t a ‘permanent’ branch. Where the Develop branch never moves, only gets amended, Hotfix and Feature branches are made, merged into Develop and removed often.
- Release — Release is used to bridge the gap between Develop and Master. Let’s say we have a few features in develop, but we want to release them altogether. In that case we need a Release branch. We put all of our features from Develop into Release, test, then merge into Master.
Let’s look at a few examples of this. First of all let’s say there’s a plan to release a single new feature. Here are the steps we take:
- Firstly we’re going to want to pull develop down. We need to make sure our local copy of develop is up-to-date with the remote copy.
- We have to create a feature branch off develop, let’s say we call it
feature/new-feature. This means that our code isn’t affecting any live code just yet.
- Then is the development work, we need to actually build the feature.
- Next we push our local branch to a remote repository, creating the branch there as well as on our local.
- It’s always a good idea to test new features. If you have a development environment for this, or a Docker container, they’re great resources to make sure our new code isn’t breaking anything!
- When we’re happy that the new feature is working, we can merge our feature branch into develop.
- It’s a good idea to test again, but then when we’re happy we can merge develop into master.
- Don’t forget, if you have lots of other branches, they’ll all need syncing with develop as well to make sure they’re up-to-date. The new code on the live site needs to be in all branches now.
This is the basic process when using Git. There are some changes and extra features that are useful for different scenarios. Lets say, first of all, that we have a bunch of features we want to release all at once! This is normally the case in larger teams. In this case we add an extra step. We can merge all of our feature branches into one single branch: a release branch. Then when we test this branch we need to make sure that none of our different features cause issues with each other and that the rest of the site or app isn’t affected. Then, when we’re sure it’s all good, we can release this whole branch with all our new stuff!
What about a hotfix branch then? This workflow can follow exactly the same flow as the feature branch, but it’s recommended in the Git Flow Documentation that hotfix branches work off Master rather than Develop, so that the fix gets out sooner.
COMMAND LINE STEPS
In this part I’ll talk you through the actual git commands to get this done! Hopefully this post will become a nice little resource for all the different git actions (for you and for me!).
Firstly we need to clone from our remote repository.
git clone https://git.clone.url
You can normally find the clone url on the repository page. In GitHub, it’s on the right when you click the big green button saying ‘Clone or Download’.
When we have our repository cloned, we need to fetch all of the branches.
This will fetch all of the remote branches for your choosing.
When you want to look at one of these branches, we need to ‘checkout’.
git checkout name-of-branch
Or if you need to you can create a whole new branch:
git branch -b name-of-new-branch
-b flag is going to make sure we checkout the new branch immediately. We could leave this out and then run
git checkout name-of-new-branch to the same effect.
We’re now in our branch, but what if in this time the remote branch changes? Then you need to ‘pull’ the changes down.
Easy enough right? If not, these will all become second-nature to you as you start to use Git more.
Now we’ve done our work, we need to make sure it goes up to our repository, right? First thing’s first, we need to see what’s changed in our branch.
This is going to return a list of the changes you’ve made. You might also get an instruction saying some of your changes aren’t staged for commit. But we need to add those changes!
git add path/to/file/or/folder.html
By now hopefully Git is starting to look pretty semantic. We can add individual files or whole folders, and we can add more than one file by leaving a space between them. Finally, all our changes are staged and ready, now we need to commit them.
We do this with the command
git commit, which has a bunch of useful parameters. Firstly, make sure all of our changed and added files are included with
-a, which is shorthand for
— all. Then we need to add a message to tell other people working on the project what we did at a glance. We add a commit message with
-m”Message about the commit”. This is actually shorthand for
— message””, so you can use either! There’s a lot of these shorthands about in command line stuff.
Finally our full commit command should look a bit like this:
git commit -a -m”Message about commit”
And it’s committed!
So far we’ve only changed our local repository. That’s great if we have local test environments and want to look at our fixes before pushing them to the remote, but when we’re ready for that we need to get the local code up to the remote repo!
git push remote-name branch-name
Normally your remote-name will be origin and your branch name the same as your local branch! If you created your branch locally then your push is going to have trouble — it won’t be able to find the remote branch to push to. In that case we simply need to add the
-u parameter, or
— set-upstream, and git will know to create this branch in the repo.
That’s all of the basic git commands covered. There are a lot more out there and they all have lots of flags or parameters to adjust them in some way.
Handy Git Links
If you’re starting out, a great resource is the Atlassian’s git tutorials. These articles from the creators of BitBucket and SourceTree are clear and well-laid out, and they even compare different git workflows — there’s no wrong answer!
- Git has some really useful documentation when looking up commands or flags. Search any command to see all of the possible options.
- David Walsh also has a great article on useful and sometimes less-well-known git commands.
Here’s some other stuff I’ve found useful in my time with Git, maybe they’ll be handy for you too:
“Oh no! Something looks broken on this branch. But didn’t I just fix this on a different branch?”
That’s where cherry picking comes in. You can take any commit from any branch and inject it into your code. If there’s a fix for a bug on another branch, you can take it and add it to your branch easily!
git cherry-pick commit-id
You can find the git commit ID in your repository history, either on GitHub/BitBucket or you can run
git log to see all the commits for the current branch.
“Oh no! We just committed some super janky code to develop, totally by accident!”
It happens, right? Luckily all we need to do here is find the ID of the commit before our broken one, then we can ‘reset’ the branch to there. Firstly, checkout develop and run
git log to get the IDs, then we can run this:
git reset — hard commit-id
We used the
— hard parameter here to tell git that we don’t care about our outstanding changes — they can be ignored. If we need to keep our current changes we can change this to
“I need to do a hotfix real quick, but I’m not finished building my feature!”
I do this all the time. I’m halfway through one thing but I need to do another urgently, so what do I do? I can actually save my current changes for later.
But when we want it back, we can do that really easily too.
So we can stash our feature-in-progress, do a hotfix, then pop them back in to continue. If you use SourceTree — Atlassian’s Git Client — you can also handle this easily from the UI.
“I’ve been doing this for a while now, and I’m clogged up with all these branches”
After some time you’re going to realise that when branches get merged and deleted on the repository, they don’t get deleted on your local. Every now and then I like to do a cleanup of my local branches, getting rid of anything I’m not going to need.
git branch -d name-of-local-branch
As you can see this is similar to the command used to create a new branch, but that
-d flag in there means this branch gets deleted instead.
That’s a pretty neat roundup of some basic and some more intermediate git commands in a workflow, but now I’m going to show you how to apply them to a project on GitHub that you don’t own. Fixing a bug for someone or adding a feature to a plugin is a great way to learn and get your code out there. In fact, some teams like to look at a candidate’s GitHub activity during the hiring process — to see if the person is curious to learn and help others. It’s a little different using someone else’s project, and there are a few gotchas that might trip you up.
When we see a GitHub repository, we first need our own copy. We could just clone it as we did earlier, but unless we’re a contributor of the repo, we won’t be able to commit and push any new stuff! We need to take our own copy, make changes there and request that our copy be merged into the original. Making our own copy is called ‘forking’. This creates a repository on our GitHub account identical to the original.
The next steps are the same as above. We create a branch to do our changes on, make the changes, commit and push them. This will update our copy of the repo on GitHub. Now we need to politely ask the original owners to accept it.
REQUESTING A PULL
Luckily on the original repo there’ll be a little button that said “New pull request”. It’ll show you the differences between the original and your branch and you can check to make sure the code looks fine. Then you can submit your request and the contributors of the original repo will be able to review it.
EDITING YOUR REQUEST
Sometimes the feedback comes back with some changes, and they’re really easy to make! In the same way as we did on our local, make another change, commit and push it. Now the pull request will automatically update with your change. GitHub has a handy timeline of comments etc, and your change will appear there. Then finally if approved, your code will be visible on the original repository!
I hope this has been helpful in some way if you’re just getting started in Git, or if you need help with GitHub’s workflow. I’m no expert on this, but I do know you need to find a way of using Git that works for you and your project. In bigger teams it might be that this exact flow doesn’t work for you and that’s fine, there’s no wrong answer! If you do work in a different way I’d love to hear about it! Be sure to leave a comment or tweet me!