The most popular questions about git always seem to be the same: When should I commit? How often should I commit? What exactly is a branch in my project? What is a tag?
Before getting into dirty grounds (and we’ll eventually get there) you should know a little about best practices when using git.
Note: If you haven’t read the post A Useful Git Tutorial – Part I you can check it here.
Branching Workflows in git
There are whole chapters in books dedicated to branching workflows and you should definitely check this link if you want know a lot about this. But since this is a useful tutorial to get you work pretty quick I’m gonna summarize it drastically here.
The best practice is to have the following branches:
- master: This is the stable branch. The code that will be released. Only very well tested code is merged here. Nobody works in this branch directly, just merges from branches that were thoroughly tested. And it is named liked that: master
- develop: These are the branches where the actual developing happens. Most people name them after the version number like “1_0_1” or “1_0_X” or “RC_1_0”.
- topic: These are short-lived branches that implement a single feature. If you don’t want to get in problems later, like you implemented two features in the same branch at the same time, you should stick to that. Usually they are named after the feature.
This will be your workflow:
So when do you tag? Most people tag the release commit and that’s it. Let’s say you have a branch called 1_0_X where all the development of that version is happening. When you’re ready to publish your version 1.0.1, then you merge the master branch with your 1_0_X branch and tag that commit as your release 1_0_1.
How often do I have to commit? Often. But commit things that work. Never commit code that doesn’t work or worse one that doesn’t even compile. Commit a feature. Never commit two features in a single commit. Are you worried that you have way too many commits? There’s something called Hide the Sausage Making, we’ll get to that in a few posts.
So let’s get down to business for a little while. All my examples are based in the fork of the qBitorrent project because I wanted to implement an optional cookie field when downloading torrent files from a URL.
First clone the repository you want to work on to:
$ git clone https://github.com/qbittorrent/qBittorrent.git Cloning into 'qBittorrent'... remote: Counting objects: 71643, done. remote: Total 71643 (delta 0), reused 0 (delta 0), pack-reused 71643 Receiving objects: 100% (71643/71643), 109.18 MiB | 124.00 KiB/s, done. Resolving deltas: 100% (59896/59896), done. Checking connectivity... done.
Create my development branch where I will do all the mess. I’ll give it the lamest name possible:
$ git branch haxor ~$ git checkout haxor Switched to branch 'haxor' $ git branch * haxor master
Now I’m in the haxor branch where I can start making my changes. But then again keeping my changes local doesn’t make me feel safe and I want to push them to a remote repository I have access to. Obviously I can’t mess with the official qBittorrent repository, but I can clone it in GitHub and then I will have a remote repository that I can push my things to. First I have to know how to work with remote repositories and after that I will have to solve the nightmare of having two remote repositores, where you can’t rebase commits that exists somewhere else. We’ll get to that eventually, don’t worry.
Like I said before, remote repositories are bare repositores that allow collaboration between developers. You can have several of them for the same project. You can push to some of them, others are just read only.
Right now I should have only one remote and that’s the Official GitHub repository for qBittorrent. I can check my remotes like this:
$ git remote origin $ git remote -v origin https://github.com/qbittorrent/qBittorrent.git (fetch) origin https://github.com/qbittorrent/qBittorrent.git (push)
The origin name is the default name of the repository when you do a git clone.
Now I need a remote repository where I have read/write access, so I can push my changes and don’t depend only of the local files. Just like some sort of backup. So I’m gonna add my GitHub repository for qBittorrent and name it github:
$ git remote add github http://github.com/naikel/qBittorrent.git
Example: Actual Work With Two Repos
Now let’s fix an annoying bug in qBittorrent where it shows the value “Unknown” if you have downloaded in the same session more than the size of a signed integer of 32 bits. That’s because 64 bits integers should be used. We need to change two functions from int to qlonglong: totalPayloadUpload() and totalPayloadDownload().
So first you need to create a new branch to do this:
git branch payload_fix
Then switch to that branch to start working on it:
git checkout payload_fix
Common Problem: I already modified the files in the wrong branch!
Don’t worry, that happens a lot. To see what you have done use the git status command:
$ git status On branch master Changes not staged for commit: (use "git add ..." to update what will be committed) (use "git checkout -- ..." to discard changes in working directory) modified: src/core/bittorrent/torrenthandle.cpp modified: src/core/bittorrent/torrenthandle.h
You can create a new branch and move your modified files there by typing the command:
git checkout -b payload_fix
Now let’s commit the changes. We’ve already seen how to do this in a previous post:
$ git add -u $ git status On branch payload_fix Changes to be committed: (use "git reset HEAD ..." to unstage) modified: src/core/bittorrent/torrenthandle.cpp modified: src/core/bittorrent/torrenthandle.h $ git commit -m "Changed payload functions from int to qlonglong in TorrentHandle class" [payload_fix 2a2c947] Changed payload functions from int to qlonglong in TorrentHandle class 2 files changed, 4 insertions(+), 4 deletions(-)
Now I’m going to push the commit to the repo, but I want it to push it to the github repository and not the origin. Also I want that branch tracking the github repository:
$ git push -u github
After that whenever I’m in that branch I just need to type git push and it will push to github. Here’s the output:
$ git push -u github Username for 'https://github.com': Password for 'https://@github.com': Counting objects: 207, done. Delta compression using up to 4 threads. Compressing objects: 100% (44/44), done. Writing objects: 100% (125/125), 17.95 KiB | 0 bytes/s, done. Total 125 (delta 106), reused 99 (delta 81) To http://github.com/naikel/qBittorrent.git * [new branch] payload_fix ->; payload_fix Branch payload_fix set up to track remote branch payload_fix from github.
To see the differences between this branch and the master you can use this command:
git diff master
And to see all the history of commits in the current branch:
We’ll get into worse git problems and how to solve them in the next post about this topic.