Git

Git is the version-control system used on Growstuff; that is, it's the program that allows us to keep track of who changed what and when and why, and to pass code changes about from developer to developer.

Who needs to use git? Who doesn't?
Anyone who's working on Growstuff's code will need to use git.

Some of our non-code things (such as graphic design and policy documents) are kept in git too. If you're working on design, you can access the files using SparkleShare which is basically like Dropbox for Github.

If you are contributing documentation, most of that happens here on this wiki, so you don't need to use git.

If you're working with our data (eg. Crop wrangling) you generally don't need to use git.

Intro to git
Aka background you probably want to know. If you're already familiar with git, skip this section.

I have never used version control software before
Suppose every time you changed something in your code, you zipped up the whole source code directory and stored the zipfile somewhere with a note about why you'd made the change and which previous zipfile you'd started out with. To find out what exactly had changed between point N and point M you could unzip both zipfiles and compare their contents; to find out why they'd changed like that you could trace the line of zipfiles and read their associated notes. If you wanted to send someone your changes, you could just email them your last few zipfiles. This is basically what Git does, but it's much more efficient in its use of disk space and bandwidth. The "zipfiles" (not really zipfiles) are called commits, and the whole directory managed by Git is called a repository.

But now, suppose you sent your latest commit to Brenda. But - oh no! - she has also made some changes to her copy of the source code. You've got to work out where your copies started to diverge, what's changed on her side, and what's changed on your side, then somehow stitch the two sets of changes together. Most of the time this can be done automatically with programs called diff and patch; Git automates this process for you. It also makes it easy for you to have multiple lines of development yourself (perhaps to work on different features or bugfixes) and merge them together when they're finished.

This is rather like the way that the software powering Wikipedia (and this wiki!) tracks how pages have changed and lets you compare how a page looked at different points in its history. There are two main differences:


 * 1) Wikipedia doesn't allow two people to work on the same section at the same time and later merge their work.
 * 2) Wikipedia only versions one page at a time, rather than everything at once.

The first feature is a huge deal in collaborative teams, and the second feature is pretty much essential when you're versioning things like software, where (for instance) you might define a function in one file and call it in many others - if you change the function's name, you have to make the change in all those places simultaneously.

I have used version control software, but not Git
Git tracks snapshots of the entire source-code directory, and organises these snapshots (called commits) into a graph (think "join-the-dots puzzle"), describing the project's history. Here's a fragment of Growstuff's history graph, rendered by the GitK history viewer: the dots are commits, the green rectangles are labels on commits (called branches), and there's a line from each commit to its parent(s).



Version-control operations are performed by adding new nodes to this graph or by adding or moving labels on nodes. Federico has written a couple of great posts explaining in detail what happens when you make new commits on a branch and push or pull commits between repositories. Internally, Git uses an efficient representation that makes it very fast to compare or merge any two commits, no matter how far apart they are on the history graph. This, together with Git's rich history-tracking, makes maintaining multiple simultaneous lines of development (as we do on Growstuff) much easier than with many older systems.

If you're used to a system like Subversion or Darcs which tracks sets of changes rather than snapshots, then you'll probably have to adjust your expectations.

For more about how Git works, see these slides by Mark Jason Dominus.

Sharing changes between Git repositories
Git is a distributed version control system: every developer's repository has a copy of the full history graph (or the full history graph at the time when they last connected, anyway). This means that you can perform version-control operations while disconnected from the Internet and sync your changes when you get back online. Though we've designated a "central" repository, this is really only a convention; any repository containing the Growstuff source code would do.

GitHub is a website offering free hosting of Git repositories and some handy collaboration tools; we use it to host the central Growstuff repository.

Getting Git
The "Install Ruby and Rails" instructions on Development pre-requisites include instructions for installing Git on OS X; for other platforms, see this help page on GitHub.

User interfaces
The command-line interface is fine once you're used to it, but it can be a bit intimidating at first. I (pozorvlak) found the built-in git-gui program very useful when I was learning Git, and I still use the GitK graphical history browser all the time. Here's a list of other GUI clients. Lots of Mac users swear by GitX in particular. Your preferred editor/IDE may also have a Git plugin, which may or may not be easier to use than the command-line client.



''A screenshot from git-gui. Select some text and right-click on it to stage only those lines, or only that hunk.''

Learning Git
Git has a not-entirely-undeserved reputation for being hard to learn, but it makes up for that by being flexible, fast and scalable. If you're working on Growstuff's code, you'll need to learn at least a bit about Git to share your work with the rest of the team, and the more you learn about it the easier your life will be. Fortunately, every conceivable question about Git usage has already been asked and answered on Stack Overflow.

The better you understand what Git's doing under the hood, the easier you'll find this, regardless of what interface you're using. In particular, the standard Git command-line interface is a very thin layer over the graph-manipulation code. Have you read mjd's slides yet? :-)

You should pick up basic Git usage through Pairing. Keep Google/StackOverflow handy for when you run into an unfamiliar situation: searching for "Git [description of $task]" invariably brings up explicit instructions of how to perform $task. Keep a GitK/GitX window open so you can see what effect your commands are having on the history graph (you'll need to manually refresh GitK by pressing F5). Set up your shell prompt so it contains the name of your current Git branch - instructions here. Read error messages carefully: they're often surprisingly helpful, and tell you exactly what to do.

Many new Git users are caught out by the staging area, sometimes called the index; in Git, changed files are first added to the staging area by git add, then when the staging area is as desired, its contents are turned into a new commit with git commit. See mjd's slides, or this explanation.

You may find Jessamyn's blog post about learning Git helpful.

''See also: Learning resources

Useful Git commands
Words like $file should be replaced with the actual filename (or whatever) you care about.

Clone your Growstuff repository onto your local machine:

git clone http://github.com/$username/growstuff

Start working on a new story:

git checkout -b storydescription # eg. "profilepage" or "multiplegardens" -- anything descriptive

Can't remember where you're at? Check your status.

git status git diff

You've done some work and you want to commit it:

git commit -a -m "commit message"

Alternatively, if you'd like to leave a longer message, the following command will open will open your default editor and show you all of your changes and will allow you to add a multi-line commit message:

git commit -av

Now you want to push it back up to github so other people can see it:

git push origin $branchname # that's the "storydescription" from above, eg. "profilepage"

Update your growstuff repo based on the main Growstuff/growstuff dev branch

git checkout dev git pull upstream dev

When you've got the hang of day-to-day Git use, have a look at this more extensive list of useful Git commands.

Merging in Git
You have some changes in your awesome branch that you want to merge into the dev branch. To do this, run the commands

git checkout dev git merge awesome

Conversely, if you want to merge recent changes to dev into a story branch, just swap the roles of dev</tt> and awesome</tt>:

git checkout awesome git merge dev

Usually, git is able to merge all the changes automatically. But sometimes both dev</tt> and awesome</tt> contain changes to the same regions of one or more files, and there's a conflict that git can't resolve on its own. When this happens, you'll get a message that looks like

Auto-merging metasyntactic.rb CONFLICT (content): Merge conflict in metasyntactic.rb  Automatic merge failed; fix conflicts and then commit the result.

If you look inside metasyntactic.rb</tt>, you'll see something that looks like

1 This 2 is 3 a  4 test <<<<<<< HEAD 5 Be 6 alarmed 7 Be 8 very 9 alarmed ======= 5 Do  6 not 7 be 8 alarmed 9 >>>>>>> awesome 10

The section between <<<<<<< HEAD</tt> and >>>>>>> awesome</tt> shows the conflicted region; the lines between <<<<<<< HEAD</tt> and =======</tt> show the version in dev</tt> and the lines between =======</tt> and >>>>>>> awesome</tt> show the version in awesome</tt>. Edit the file so the conflict markers are removed and the code contains the fixes from both branches (this is the bit that requires human intelligence and understanding of the code). Once the tests all pass again, you should git add</tt> the conflicted files and then git commit</tt> the merge.

git add metasyntactic.rb git commit

A useful git alias for merging
Add the following lines to ~/.gitconfig</tt>:

[alias] conflicts = !git diff --name-only --diff-filter=U

Now you can type <tt>git conflicts</tt> to get a list of files with merge conflicts. You can also add all the conflict fixups to the index in one go with

git add `git conflicts`

Growstuff's Git policies
We follow roughly the policy described here: every developer has a personal, publicly-visible repository (at http://github.com/$developername/growstuff.git), in which history is not required to be stable. Once a story is finished, push your changes to your personal GitHub repository and then use the GitHub website to send a pull request to the central Growstuff repository.

We suggest you create a new branch for each story or chore you're working on.

You are encouraged to rewrite history using <tt>git commit --amend</tt> and <tt>git rebase --interactive</tt> (or if you're feeling ambitious, using this workflow) before sending a pull request upstream. Your commits ideally should be


 * small (no "code bombs")
 * atomic (one complete change per commit)
 * correct (all tests should pass on every commit)

This isn't to hide your mistakes - everyone makes mistakes! - but rather to make your changes easier to review, and to make it easier to track down bugs using <tt>git bisect</tt> in future. Just as the code should tell a useful story about how the program works, the revision history should tell a useful story about how the program was developed - which isn't always the same thing as a literally correct history.

If you make a mistake while rewriting history, don't worry - your old commits are still in there. You can find out their IDs with <tt>git reflog</tt>, and then return to a good point with <tt>git reset --hard $commit-id</tt>.

Federico has written a good explanation of history-rewriting.

Sharing changes between pairing partners
If you're remotely pairing, then you'll need to exchange commits between each others' computers when you change "drivers". There are two ways to do this: using direct push/pull, or by sending pull requests.

Method 1: pull requests
To use pull requests:


 * the old driver pushes their changes to GitHub with <tt>git push origin $storybranch</tt>;
 * using the GitHub website, they send a pull request to the new driver's GitHub repo;
 * using the GitHub website, the new driver accepts the pull request;
 * the new driver fetches the changes to their local repository with <tt>git pull origin</tt>.

Method 2: push/pull
To use push/pull:


 * the old driver pushes their changes with <tt>git push -u origin $storybranch</tt>;
 * the new driver creates a local pointer to the old driver's repository with <tt>git remote add $old_driver https://github.com/old_driver/growstuff.git</tt> (skip this stage if you've done it before);
 * the new driver gets the old driver's changes with <tt>git fetch $old_driver</tt>;
 * the new driver creates a local branch for the story with <tt>git checkout -b $storybranch $old_driver/$storybranch</tt>.

Use whichever you prefer.