Using git

First you need to tell git who you are. These commands will write to ~/.gitconfig, and will be used to identify you when you commit changes. Plus, I like color.

% git config --global user.name "John Doe"
% git config --global user.email "john@doe.com"
% git config --global color.ui=always

Now we will clone the repository, which involves downloading about 2MB of files. Git repositories are very compact. We will clone the repository from github. This takes a minute or so.

% cd
% git clone git://github.com/pbeckingham/task.git task.git
% cd task.git

You just pulled the entire history of task changes since it was uploaded to github. That will be missing about a year and a half of prior changes, which were in Subversion. Let's build task:

% autoreconf
...
% ./configure --prefix=/usr/local
...
% make
...

The task binary will be in the src directory. If you run:

% sudo make install

Then task will be copied to /usr/local/bin, according the --prefix argument used above. /usr/local is the default prefix, so that argument wasn't necessary, but illustrates it's use.

Meanwhile, you have just created a local copy of the repository. You can do anything to this copy except push to github. Only the repository owner can push to github, or additional users identified by the owner (with additional monthly github fee). In order to get your changes back to github, they have to go through the owner, either in the form of a patch via email, or by creating your own github account, pushing to you own task repository clone (github calls it a fork), then asking the owner to pull directly from your repository.

You only need to clone once, unless you feel like starting over, or creating additional clones. Ordinarily, you update your local copy by pulling changes from github with:

% git pull

Right now, nothing new should be pulled, because you just cloned. Sometimes you'll see changes that have either been made by the owner, or merged in from others, by the owner. Let's take a look at the commit history:

% git log

You see a whole series of commits. Each commit is a set of file changes that were made somewhere. Let's look at branches. Try this:

% git branch
* master

This is telling you that you have only one branch, called master. The asterisk tells you that this is your current branch, which is redundant because you only have one. But there are other branches on github. See those with:

% git branch -a
* master
  remotes/origin/1.6.1
  remotes/origin/1.7.0
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

A remote is what git calls another repository. The origin remote is the repository that this clone originated from. This output tells you that there is a 1.6.1 branch on github, a 1.7.0 branch on github, and a master branch on github. It's not obvious, but your local master (the first one shown) is tracking remotes/origin/master, which means changes on the remote will get merged to the local tracking branch on pull.

The convention used by task is to create a new branch whenever work begins on a new version. When that version is released, the branch is merged to master, but retained. At time of writing, 1.6.1 is the currently released version of task, and so remotes/origin/1.6.1 and remotes/origin/master are currently identical, with all new development happening on the 1.7.0 branch. When 1.7.0 is released, it will get merged to master, and the 1.6.1 branch will be deleted. Nothing will be lost, because 1.6.1 is already merged to master. Let's look at tags:

% git tag
... v1.4.3 v1.5.0 v1.6.0 v1.6.1

Those tags are just labels that represent the last commit for that version. You may notice that a change in the tag naming convention, that occurred when the owner realized that git will not allow a tag and a branch of the same name at the same time, so now there is a "v" in the tags.

Let's make a branch called 1.7.0 that tracks changes to remotes/origin/1.7.0. That means you can pull 1.7.0 changes from github into your local branch, to keep up to date.

% git checkout -b 1.7.0 origin/1.7.0
Branch 1.7.0 set up to track remote branch 1.7.0 from origin.
Switched to a new branch '1.7.0'

% git branch -a
* 1.7.0
  master
  remotes/origin/1.6.1
  remotes/origin/1.7.0
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

Now you can see that 1.7.0 is your current branch (*). That means you are looking at the 1.7.0 codebase. Let us now assume you intend to make a change, and submit the patch. We will add Solaris 8 as a supported OS. This will affect two files. First check status:

% git status
# On branch 1.7.0
nothing to commit (working directory clean)

No changes. That's what we expect. Now make the changes (any editor will suffice, but assume vi):

% vi NEWS
% vi html/task.html

Now we expect to see changes. Status says:

% git status
# On branch 1.7.0
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   NEWS
#	modified:   html/task.html
#
no changes added to commit (use "git add" and/or "git commit -a")

Git sees that those two files have changed, but as git states, they are not added to the commit. Git allows you to stage changes, then commit the staged changes - a two-step process that gives you complete control. Git also allows you to commit all changes and bypass the staging, but that's a shortcut that is risky, because you can inadvertently commit things you didn't want to. Let's see what git thinks changed:

% git diff
diff --git a/NEWS b/NEWS
index 74adecd..30d4f8f 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,7 @@ Task has been built and tested on the following configurations:
   - Ubuntu 8.10 Intrepid Ibex
   - Ubuntu 9.04 Jaunty Jackalope
   - Arch Linux
+  - Solaris 8
   - Solaris 10
   - Cygwin 1.5.25-14
 
diff --git a/html/task.html b/html/task.html
index 66ee777..a2254f6 100644
--- a/html/task.html
+++ b/html/task.html
@@ -193,6 +193,7 @@
                     <li>Ubuntu 8.10 Intrepid Ibex
                     <li>Ubuntu 9.04 Jaunty Jackalope
                     <li>Arch Linux
+                    <li>Solaris 8
                     <li>Solaris 10
                     <li>Cygwin 1.5.25-14
                   </ul>

Git has correctly spotted the changes. The "git diff" command always tells you the difference between the last commit and the current state. Now we will stage the two changes:

% git add NEWS html/task.html
% git diff

No changes are reported. That's because if files are staged, then diff shows the difference between the staged files and the current state. We staged all changes, hence no diff. If we wanted to see the difference between the last commit and the staged files, try this:

% git diff --cached
(same as unstaged diff)

Now the status shows that the files are staged:

% git status
# On branch 1.7.0
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   NEWS
#	modified:   html/task.html
#

Now a commit command will commit the staged files:

% git commit -m "Added Solaris 8 as a supported platform"
[1.7.0 03308eb] Added Solaris 8 as a suported platform
 2 files changed, 2 insertions(+), 0 deletions(-)

Now make a patch:

% git format-patch HEAD^
0001-Added-Solaris-8-as-a-supported-platform.patch

HEAD is a label that means the current head of the branch, or in other words, what was last committed. HEAD^ means the commit before that. HEAD~2 is the one before that, HEAD~3 etc. When we say create a patch of HEAD^, it means the same as:

% git diff HEAD^

Which means show the difference between the previous commit and the current commit, but the patch has extra identifying information in it. Take a look at that patch file. If you ran:

% git format-patch HEAD~10

Git will create 10 patch files, one for each commit. To get the patch applied to the repository, email it to the owner. The owner will then apply the patch with:

owner> git apply 0001-Added-Solaris-8-as-a-supported-platform.patch

Then push the changes with:

owner> git push

And that makes them available on github, which means in turn that the next time you run:

% git pull

Your changes will have come full circle.



Copyright 2006-2009, P. Beckingham. All rights reserved.