Sunday, March 08, 2009

Git it on

I switched to Git as my main version control system about five months ago and I haven’t looked back. I’m currently working on a Javascript project where I have to test and develop code both on the Mac and on the Windows VM-Ware images. I have usually had problems with this kind of setup, not so with Git.

Git Basics

I set up my git repository with the usual:

# Create directory
mkdir projectname

# Change to it
cd projectname

# Initialize the repsitory
git init


Then I add a .gitignore file with the specifics for this project.

# Local .gitignore
dist
pkg

And then I add the file to project and commit it.

# Add the .gitignore file
git add .gitignore

# Commit the changes
git commit -m 'initial'

My global ignore file $HOME/.gitignore contains everything that I have common for all projects.

# Global $HOME/.gitignore
*~
.DS_Store
tags
*.gz

Git works with changes, this is important, so you are not actually adding a file to the repository, you are adding the changes. This means that you will have to add files every time you have made a change to them. I like this since it gives me an extra level of control, but if you don’t, you can always use the -a option when you commit and it will include the changes to files that have been added to the repository at least once.

# Add and commit the changes
git commit -a -m 'changes to all added files'

After setting up the initial repository and adding the .gitignore file, I am set to go. I usually start with creating a new branch for development.

# Create the branch but don't switch to it.
git branch dev 

# Or, create the branch and switch to it.
git co -b dev 

# The co above is an alias for checkout, it was created like this
git config --global alias.co "checkout"

After creating, and moving to, this branch I create another branch for the task that I am going to work on. For example

# Create a new branch, setup_tests
git co -b setup_tests

I do what I have to do to get the task done and then I switch to the dev branch and merge the changes in.

# Switch branch to dev
git co dev

# Merge in the changes from setup_tests
git merge setup_tests

If at any point I feel like I need to do something that doesn’t have to do with this task, such as add some utility functions, I just commit the current branch and start a new one from where I am. This gives me very fine-grained control over the source code and it lets me throw away changes that goes bad without parsing and removing the bad changes manually.

If I forget to do my fine-grained branching, Git allows me to just add some of the changes from a file. The simplest way to do this is via the interactive add command.

# Enter git interactive add 
git add -i

Another command I find myself using more and more is stash. If the change that I need to do is totally unrelated to what I am doing now and I just need to fix it, I stash my current branch on the stash-stack and move to the branch where I need the change.

# Saves the changes on the stash stack
git stash

# Switch branch, change, and commit
git co development
git commit -a -m 'urgent change'

#Switch back, and apply the stash.
git co setup_tests
git stash apply

Like I said, the stash is a stack and the command git stash apply applies the top of the stack. If you have the need to apply a stash that is not on top, this is also possible. See the help, git help stash, for information on this.

Cross-VM Workflow

When I need to test my changes on Windows, especially on the awful IE6, I switch to this virtual machine and then I clone the repository.

# Clone the repository
git clone path_to_my_local_mac_directory

Now, I have a perfect copy of my repository and I can test and make the needed changes at will. After this I just push the changes back to the Mac again and pull them back to windows from now on.

# Pushes the current branch
git push 

# Pull the changes back
git pull

I usually find doing this in the development branch the best way to go and then I reserve the merging into the master branch to the repository that I have designated as main, the one on the Mac.

At the moment I am developing on my main repository and there is one caveat with this. If you push changes into this repository the working directory will not be updated. Thus, if you have local changes in your working directory and just commit them without checking the status. You will revert the changes that have been made on the remote repository. This is by no means a fatal problem since you can just revert your changes and move on, but it is still annoying. The lesson to learn is to always use git status before you commit.

# Shows the status of the repository
git status

Parallel Branches

In addition to working on a Project with Javascript, I am also developing a course in it. It is called Javascript, the Esperanto of the Web because that is what Javascript has become. When Java didn’t cut it as the language for the web, Javascript was there and that was all that it took.

When developing this course I have the usual issues with keeping questions in sync with the answers. Git solves this without any problems at all. All I do is to keep to branches of the tree, master, and solution. I do all the course development in the solution-branch and then I just merge it into the master branch where I remove all the correct answers. After that I move back to developing in the solution-branch.

If you want to get started with Git I can highly recommend the series of screencasts at “Gitcasts”: Start with the RailsConf Git Talk, it gives a good overview. If you’re on Windows and like GUI tools move on with the Git on Windows Talk. I use the Cygwin version for my windows version of Git since I have these tools installed anyway.

No comments: