Recently, I got a chance to be a part of one of the most kick-ass communities, whose software basically rules the tech world. Yeah, Git. The aim was to get my hands dirty with something new and amazing. So though this post isn’t as much about my experience there (so far, great), but rather a stash of useful commands that one may require when actually contributing to Git. I’ll keep it updated as often as possible. Though the documentation for getting involved is very educative and covers everything you’re required to do, I decided to write this post since I couldn’t find a complete set of commands (and concepts) we need to actually get started. Man pages are nice, but nothing beats a well written tutorial, right?
TL;DR: Google it, click the first StackOverflow link, copy-paste the command in the top answer and hit enter. That’ll [make/fix/do] it.
So, here’s how we’re gonna do. First, we’ll walk through the basics of Git in case you want a refresher. Then we move on to the stuff we’ll need very frequently for development. Let’s get started.
Getting started with Git
First, if you’re not familiar with what Git is and what it’s for, go read this Atlassian tutorial to get an idea what we’re dealing with here. Ideally, you should know some basic Git commands before can get started with contributing. Here’s a summary of the important commands and concepts you should have an idea of. For everything else, RTFM, or… StackOverflow. If you think you know the basics pretty well, or have read through the Atlassian docs, go ahead skip to the contributing section:
1. Creating new repo, adding and committing
The first thing you need to do to get started is, no surprise, install Git. If you haven’t installed on your system yet, get it from git-scm downloads.
Now, like a fine English gentlemen, let’s introduce ourselves to the newly installed software, starting with name and email. This is done as:
$ git config --global user.name "User Name [:nick]"
So, if you are John Travolta, with nickname jtrav, you’ll do:
$ git config --global user.name "John Travolta [:jtrav]"
You may vary the name string, like remove the colon before the nick, or the nick altogether. I put it there because 1. I like it there, and 2. it tells others your nick in case they wanna reach out on an irc or otherwise. But it’s completely up to you.
Your email-id is as important as your name, especially for the Git community, so here’s what we do:
$ git config --global user.email "<email@example.com>"
This should be same email ID you would be using for contributing to Git.
If you want to keep this ID separate from your usual id, you can skip the
--global tag and configure the identity details separately for different projects.
Great, now you’re all set to get started.
2. The basics
Now, we need a sample repository to get started with. You can make any regular directory (folder) a git-managed repository by running this simple command.
$ git init # Tell git to initialize this directory as repository
For those who like details, this creates a tiny hidden directory by the name
.git in the current directory (which is now, by the way, a repository) that stores a lot of information the current and past states of this repository from this point on.
Now when you run
$ git status
it’ll list all the files in the repository in red. This basically means that all the files are although in the directory, but git is not tracking them yet. So, we simply tell to do so, by
$ git add .
. tells git to add all files for tracking (more precisely, the staging area). You can add selected files by specifying the path. Now, we commit the changes to make them solid by committing them:
$ git commit -m "A commit message"
-m let’s you add an inline-comment. If you don’t use that option, your default editor will be opened for you to enter
your commit message in. We try to keep commit messages short descriptive of the changes made in the commit.
Now, we are ready to ‘push’ the changes to a remote, i.e., upload them to your server. If you’re using Github or Bitbucket, it’ll be you’re repository’s link (You can figure it out, it’s no big deal). To add a new remote, we use:
$ git remote add origin <link-to-remote>
We push local changes to remote using:
$ git push origin master
Note that here,
origin is the name of the remote, as we added in the second-last command, and
master is the branch
where you are pushing the changes.
If you want to clone a repository hosted somewhere, you can do so by simply using:
$ git clone <link-to-repository>
Getting into more detailed description of these commands, and some other you may occasionally need will eventually turn this blog into another version of Git man pages, so we’ll finish the overview here and get started with some dev.
Contributing to Git
The first step you must read the man pages. I can’t emphasize enough, RTFM. Having cloned the Git repo from their Github repository, you should definitely read the README, the CodingGuidelines and the SubmittingPatches docs. They’ll tell you everything you must know.
Also, it is suggested that you should fork Git’s Github repo and set up hooks for Travis-CI. So, before finally making a patch, you can check that your patch doesn’t break the build for OS X or Linux. It’s fairly easy to use and a free Travis account would serve you well, for both Git development and otherwise. Now, as promised, the commands. Here we go.
The first thing we need quite often is
diff is used, as you probably guessed, to diff between two commits, current HEAD and some commit, a single file’s diff over a two commits and more; basically, anything you may need to compare with anything else. Particularly, these get handy:
1. Diff between unstaged/uncommited changes and last commit:
$ git diff -- [<path-to-file>]
Square brackets indicate optional parameter. You can specify a single file or directory to diff. By default, all changes are diffed against last commit. To diff the current HEAD with any commit, we use:
$ git diff commit_id HEAD
where commit_id is the sha1 ID of the commit you want to diff HEAD with. To diff the current HEAD and the last commit (last HEAD), simply use:
$ git diff HEAD^ HEAD
You can also
diff to generate patches, example this creates a patch for the last commit:
$ git diff HEAD^ HEAD > last_commit.patch
Another cool thing you can do with diff is check if you introduced extraneous whitespace while making changes. To do that, just execute:
$ git diff --check [commit_1 commit_2]
2. Formatting patches using
If you’re going to send patches to git, you need to properly format them so they are readable
for all community members, and also adhere to the git standards. For this, an awesome command comes
with git, called
format-patch. This allows you to make an email-ready formatted patch that you can send directly using
send-email or using another MUA client. More on MUA clients later.
A basic usage of
format-patch is this:
$ git format-patch master -1
This creates a patch of the last 1 commit on master branch. You can do a lot more than just that make a simple patch for last commit. You can make patches for multiple commits as well. One standard form I use a lot is:
$ git format-patch -1 -o ../patches/receive-pack-parse-option-api/ --signoff --in-reply-to='<message-id>' --patience [-vN]
Dissection of the above is as follows,
-1 tells git to make patch for last 1 commit. You may use -1commit id, commits relative to HEAD (Like HEAD~5), or a combination of commit ID and number of commits following it.
-o <dir> tell git which directly to put the generated patch in.
--patience tells it to use Patience algorithm (occasionally, patches are not very readable to others when using the default algorithm, so trying another algorithm helps).
--in-reply-to=<message-id> adds an extra line to the patch head, indicating which email is this patch in reply to. It’s usually used for replying to previous versions of your patch. You specify version number of your patch using
-vN where N is the version number (integer, eg -v3). This makes your patch subject look like “[PATCH v3] the commit message”.
3. Undo commits
If often happens, that you get the patch right in the first try. In such a case, if you have committed the changes already, you want to un-commit them. For that, just do
$ git reset HEAD~n
replacing ‘n’ with required number of commits to be undone. This doesn’t delete the changes as a hard reset would, but simply uncommits them, and places them outside the staging area. This gets in handy when you need to make only slight modifications and commit again.
4. Squashing commits
So you made 4 commits, but realize that they should ideally belong to one. What do you do? Undo them with
reset and commit again? What if there are conflicts? Fret not,
rebase is to the rescue. So, we simply run
$ git rebase -i <after-this-commit>
But what if you accidentally pushed it to your forked repository? That rebase will work for local, but the remote won’t accept it and you’re pretty much back to square one, right? Not quite. Luckily, we can force a push, thus overwriting history of the remote [CAUTION: There are serious implications of remote history rewrite. Try that you never have to use the following command.]. For this, after squashing the commits locally, we push with slight change in syntax as:
$ git push gitfork +master
where gitfork is the name of your forked repo. The
+ tells git to force push only the specified branch, unlike –force, which would force-push all refs.
Again, this isn’t a very good thing to do, so try not to push changes that you aren’t 1000% sure of.
5. Sending mail with git
Once you’ve prepared a patch and are ready to send it to the mailing list, you well, send it. But how? Your regular email client (GMail, Google Inbox, Outlook) won’t be of much help. That is because most likely, your client uses HTML formatted emails and also supports word-wrapping, and sometime adds extraneous whitespace, which can make patches not apply cleanly. To avoid this, you can either learn to use
git send-email or configure your MUA client for sending correctly formatted patches. I prefer sending patches using send-email, and use Thunderbird for regular emails (though you can configure Thunderbird to format patches) 1. First, let’s see the good ol’ command line method. Before we can use it, we need to install git send-email. On Linux, you can do something like
$ sudo apt-get install git-email
For other platforms, you may wanna Google it. Next, we need to configure the email. This blogpost covers it well. I’d only like to add this little thing, if you are going to use a GMail account, you may need to take one extra set to get everything working. You would be needed to go your account settings, and from there enable ‘Access for Less Secure Apps’. That’s it, you’re all set.
Now, to actually send an email, write:
$ git send-email ../patches/patch1.patch --firstname.lastname@example.org' [--in-reply-to='<message-id>'] [--email@example.com']
Dissection of the command;
../patches/patch1.patch is the patch you want to send.
--to argument is the person (in this case, mailing list) you want to send the message to.
--in-reply-to comes in handy when: 1. you’re sending multiple patches, in which case you send the consequent patches in reply to the first patch; 2. when you’re sending alternate/updated versions of a patch, which you do again, at the root of the email thread.
--cc allows you to CC people, and you can specify this options multiple times with different argument each time. Other options are quite self-descriptive and the git docs are very well written ;)
That’s it for now. I’ll keep updating this post as find and learn more useful commands like the ones above. If you get stuck at anything, and git man pages don’t help, try Googling it. It’s ok, no one gets it right the first time. Usually, the top answer in the first StackOverflow link does the job.
1 For more details on setting up MUA for sending patches, refer to format-patch documentation, under the heading ‘MUA Specific Hints’.
Sorry for the long post, here’s a cute potato