I've fiddled with my blog template because I decided I wanted more horizontal viewing space, given that it was using less than a third of my 1920 horizontal pixels. If it feels too spread out for you, I added a drag-and-drop handle over to the left to let you resize the main content column. The javascript is pretty primitive. If it breaks, drop me a comment.

Sunday, November 14, 2010

Tomatoes and Routers

No, this isn't a candidate name for a new blog.
Over the past several months, during which my blog has been deafeningly silent, I've been writing RESTful web services based on Jersey and Spring 3 organized as a Maven project of around 20 modules. The entire project has an almost fully-automated build and release cycle with Hudson and Sonar running nightly code analyses. Our test coverage is about 87%, and we're aggressively pushing it higher. Cyclomatic complexity per method is 1.6. There are a ton of things I could blog about, but I just haven't taken the time to think through the distinct topics I should write about. This post is not about that.

This post is about my new Linksys WRT54GL. While I may be the last person on earth to get one of these (>3k customer reviews on Newegg? seriously??), I'm still going to give a brief synopsis, because it's just really cool. Yes, it's an old model--a dinosar in computer years. Yes, it only supports 802.11g. But respectively: there's a reason it's both the most- and highest-rated of all wireless routers on Newegg, and do you *really* need 802.11n? If you need the most speed possible out of your wireless network, then give this a pass and go with a wireless n product. If, like me, you never find yourself wishing for faster wireless--and you're at least 23% geek--not to say I'm limited to 23%--and not to say I'm not (how many points do I get for reading a Lisp book in my spare time?)--then this could be the perfect router for you.
Initial setup was a snap. The first thing out of the box was a paper with size 72 font that said "Put the CD in first". That was pretty much all the instructions, and the CD takes you step by step through everything in simple, well-presented steps accompanied by pretty pictures. With just that, you've got a number of powerful options for setting up your network, though maybe nothing you wouldn't find in competing products. What really gives this router its strength is the fact that it's a deliberately open platform:
"The Linux-based Wireless-G Linux Broadband Router was created specially for hobbyists and wireless aficionados." --http://homesupport.cisco.com/en-us/wireless/lbc/WRT54GL
There are numerous third-party firmwares available for it. From a fairly small amount of research, it seems that you can turn this router into virtually anything, as long as it fits in the available memory:
  • FTP server via ProFTPD
  • Windows file sharing client and/or server via Samba
  • Welcome/login page and access restrictions for network access for use in a public setting via Chillispot
And that's only a small sample of the stuff you can run on it using just the DD-WRT firmware.
That being said, I passed on DD-WRT and went with Tomato. It doesn't have all the capabilities, but the installation instructions for Tomato are very short and sweet. DD-WRT seems a bit more involved. Even so, with Tomato, you get static DHCP reservations (strangely missing from the stock firmware), internal DNS and DNS caching for your network (DHCP and DNS provided by Dnsmasq), SSH/telnet access to the router's OS--Linux, btw--Samba client for making storage available to the router, great visibility into your network usage via live graphs and historical metrics reporting, and a slick AJAX web UI.
All of that, and installing Tomato really was as simple as downloading it and using the router's standard "update firmware" screen to install it! If you don't already have a WRT54GL, buy one. Or maybe two. If you do have one but haven't switched to a third-party firmware, do it now! You won't regret it! Unless your power goes out in the middle of flashing!

Saturday, August 14, 2010

Best Salsa Ever

My family got this salsa recipe from a little, then-hole-in-the-wall Mexican food restaurant in San Antonio, TX somewhere around 25 years ago. It's still the best salsa I've found anywhere, and the restaurant--now successful and with multiple locations--is still making the same salsa. Naturally, our version and the restaurant's have diverged over the years, but the ground rules have never really changed.

First, a disclaimer: I don't actually have or follow a recipe! I just know the ingredients that are needed, and I put them together until it tastes right. What I'm about to give you are the guidelines I use for getting all the ingredients and some rough guesses at the right quantities to make it all work together. If you follow this recipe exactly, you'll probably (I think) have something edible. If you want it to really shine, you'll need to take your time and pay attention to the "tuning" notes I've added at the end of this post. You'll also probably need to make it several times before you get the hang of it. What makes it difficult is that the amounts of ingredients you need change based on variations in the quality and flavor of the ingredients (primarily the tomatoes) that you get. Now, on with the show...

The Ingredients

I've split the list into "main" ingredients, which are what make up the body of the salsa, and "flavor" ingredients, which you use to tune the flavor to your personal preference. The quantities--or at least the ratios--of the main ingredients that I use are pretty well-known (by me, that is):

Main Ingredients
  • 8 lb Roma tomatoes
  • 2 lb slicing tomatoes
  • 1 lb Serrano peppers
  • 1 lb sweet onions

Note: With these quantities of main ingredients, you'll end up with very near the right amount of salsa to serve about 50 people at a rehearsal dinner where taco salad is being served (ask me how I know!) It probably makes around six or eight quarts. Make as much or as little as you want. The important part is to get the ratios right. You need about 10:1 by weight tomatoes to peppers and roughly the same amount of onion as pepper. In other words, one pound of peppers for ten pounds of tomatoes or 1/4 pound of peppers for 2.5 pounds of tomatoes. The onion should always be roughly the same as the peppers. If you cut back on the main ingredients, remember to scale back on the flavor as well!

Here's where it gets tricky. I've tried to guess at rough quantities here based on the quantities of main ingredients I listed above, but this is just that: a guess. Take it as such! I'll cover these ingredients more in depth in the step-by-step directions later:

Flavor Ingredients
  • 1/3 cu lemon juice (bottled recommended)
  • 1/3 cu lime juice (bottled recommended)
  • 3 T garlic powder
  • 3 T salt

The Tools

Gather up the following tools and take them to the place where you'll be working:

  • Cutting board
  • Knife--sharp and/or serrated
  • Tomato corer--great for coring the tomatoes but even better for deseeding peppers
  • Small bowl or nearby trash can for scraps
  • Two large bowls, each big enough to at least hold all the tomatoes--you can get by with only one, but two is better
  • Food processor--NOT a blender!
  • Spatula for scraping/stirring
  • Dish towel--spill/splash cleanup
  • One or two powder-free latex gloves--for protecting your hand(s) from the peppers
  • Chips for sampling the salsa to fine-tune the flavor

The Directions

  1. Pop the stems off the tops of the peppers and wash the tomatoes and peppers.
  2. Protecting your hands with the gloves, halve and deseed the peppers. Taking the seeds out greatly reduces the heat from the peppers. The tomato corer is great for removing seeds from pepper halves. If you're feeling brave, skip this step. If you don't use gloves, you'll want to refrain from touching your eyes, nose, or any other sensitive areas for the next day or two.
  3. Core the tomatoes, and take off any other blemishes, bad spots, etc. Quarter the romas. As the slicing tomatoes are larger, you'll probably want to cut them into six or eight pieces. Put the tomato slices into one of the large bowls. If the tomatoes are excessively juicy, this lets the juice drain from them while they're waiting to be chopped up.
  4. On to the processing: chop up the peppers in the food processor pretty finely. You don't want it pureed, like a blender, but it's hard to get the peppers too fine in a food processor. High speed is fine for peppers.
  5. Cut the ends off of and peel the outer skin from the onions. Slice them up and chop them in the food processor, too. I save chopping the onions until right before I put them in to spare myself and others from the fumes. If your food processor is big enough, you can do the onions and peppers at the same time. I try to chop the onions less finely than the peppers, but that's not too important, either. High speed is fine here, too.
  6. Put the chopped peppers and onions in the second large bowl and cover them unless you want to drive everyone from the room you're working in.
  7. Now run the tomatoes through the food processor. You want to leave the tomatoes in larger chunks than the peppers: around 1/4 inch in size or larger. This may take some practice in your food processor. If you have a sharp blade, it's very easy to reduce the tomatoes to mush. Even the low speed of dual-speed food processors may be too fast. If so, you'll need to use the "momentary" setting and quickly flip it on and off to slowly dice up the tomatoes. As soon as you have no chunks left that are larger than about 1/2 inch, they're done. After chopping the tomatoes, pour them on top of the onions and peppers in that bowl.
  8. After all the tomatoes are chopped, you'll have some tomato juice in the bottom of the bowl that was holding them. You can add this if you like, but note that the salsa tends to float, so when you get to the bottom of the bowl, it'll be mostly juice. I usually just dump the extra tomato juice.
  9. Now for the hard part: the flavor ingredients. If you want to follow this recipe exactly (not recommended!), just add the lemon, lime, salt, and garlic in the amounts I listed and stir well with the spatula. Keep refrigerated, and let me know how it turns out. If you want truly great salsa, go on to the following paragraphs to learn the basics of how to balance the flavor ingredients with the main ingredients.

Fine Tuning

Instead of the full amount, start off by adding about three quarters of the amount of the flavor ingredients I listed, stir well, and then taste it. The exact amount isn't important. Just start slow and build up. Use the following guidelines to decide what to add:

  • If it tastes "flat", or too much like tomato, add more salt.
  • If it doesn't taste sweet enough, add some garlic and citrus (lemon and/or lime--your preference). Sometimes a touch of cumin, aka comino, will bring out the sweetness, too. Only use a little--like less that 1/4 teaspoon per cup of salsa. Some restaurants go crazy on the cumin in their salsa, and it just overwhelms everything else.
  • If it seems too hot, add citrus, especially lemon. It reduces the peppers' bite. Don't go overboard, though!
  • If you get too much lemon in, it can be balanced with salt up to a point. (Not sure if the same works for lime.)
  • If you get too much salt, citrus may help a little bit, but you'll probably have to pick up some more tomatoes to reduce the saltiness. A little bit of sugar or cumin can help here, too, mostly by masking the salt flavor, but I don't recommend it unless you've only gone a little bit over on the salt.
  • If it just doesn't taste quite "right", it probably needs more garlic. I can't help you any more there. I guess that one just comes with experience.
  • If you get to a point where you think you're adding too much of the flavor ingredients, and it still doesn't taste quite right, letting it sit for 1-2 hours--I recommend refrigerating ASAP--can change its flavor quite a bit as the ingredients blend. Just check it again later.

Additional Notes

If, while stirring the salsa, some light yellow foam forms on top, it means you're pretty close to the right mix of ingredients. I'm not sure what does that, but it always happens.

When made from good-quality ingredients, this salsa will keep for as long as 3 weeks in the refrigerator, but it's best if used within the first week or so. Freezing this stuff is a no-go. It basically turns to water with little scraps of tomato floating in it. Canning preserves it better than freezing, but don't attempt to can it unless you're familiar with the issues around safe home canning of tomatoes!! I've only canned it for one growing season so far, and the result was quite different from the fresh product, but it turned out well enough. I intend to keep working on it to see what I can do with it.

Some people like to add cilantro to their salsa. I admit that it does add a pleasant touch, but I generally don't find it to be worth the hassle. Cumin is another popular addition, but be careful: a little goes a long way.

You can use fresh lemons, limes, and garlic. If you know a fresher source of salt than the little cardboard container, let me know. Using all fresh ingredients can give the salsa a fuller flavor, but the flavor of citrus fruit and the potency of garlic varies widely from one store visit to the next. This makes it even more difficult to get the right blend of ingredients, and lemons with the wrong flavor can make a whole batch turn out poorly. That's why I nearly always use bottled lemon and lime juice and garlic powder. These products have a fairly uniform flavor and potency, so it's easier to get consistently good results with them.

Many salsa recipes call for Jalapeño peppers instead of Serannos, and if you enjoy the taste of Jalapeños you can use some here. Try using 50% Serrano and 50% Jalapeño. Just keep the tomato:pepper ratio correct, and don't leave out the Serranos entirely. They're part of the key to this recipe.

You may also be tempted to try different types of onion. I've tried a few, and I've never found any to be satisfactory. In fact, if I can't get sweet onions, I sometimes just go without any onion at all. Ideally, find some Texas 1015's.

On the other hand, feel free to experiment with different varieties of tomatoes. I list Roma and slicing tomatoes because that's generally what's easy to find at the grocery store, and using Romas as filler is cheaper than going with all slicing tomatoes; however, I've experimented with several other varieties of tomatoes including Phoenix, Celebrity, Better Boy, Beefsteak, and BHN 444. All of them made at least a decent salsa except for the Beefsteak. I didn't like the taste of that at all. Better Boy and Phoenix are my personal favorites so far. YMMV. Note that different tomato varieties will need different combinations of flavor ingredients--another complication!

Sunday, March 7, 2010

Setting Up a Thin Client Network with LTSP

I've become interested in thin client software as a potential way to use a cheap laptop as a mobile GUI for applications running on a more powerful desktop or server. Yesterday, I tried out one called the Linux Terminal Server Project (LTSP). I found out it's distributed with Edubuntu as a way for colleges and universities to set up thin client labs. I just wanted to see if I could get it working, and I was pleasantly surprised by how easy it was. My steps:

Install LTSP server

With VirtualBox, I created a VM with 512M RAM and a 5G dynamically sized hard drive. Following the Ubuntu Community Documentation, I downloaded the Ubuntu 9.10 alternate amd64 ISO to install the LTSP server on it. It was pretty simple: choose "Install an LTSP server" from the Modes menu, then "Install Ubuntu".

Realize I was supposed to have two network adapters

One thing I could've done differently to make things simpler later on: add a second network adapter to the VM. LTSP server wants two NICs: one for communicating with the outside world and one for communicating with the network where the clients are. There's supposed to be a way to configure it with just one NIC, but I belatedly added the second one and configured it myself rather than puzzling that out. The first adapter had the VirtualBox default setting of "NAT", which lets it communicate out through the host interface. When I created the second one, I set it to "Internal Network" so that it could only communicate with other VMs, where my client would be.

Make LTSP DHCP work with new adapter

LTSP server runs a DHCP server on the client-facing network interface. This is the first part of how LTSP clients start up: they get an IP address and some other instructions from the DHCP server on the LTSP server. Because I had only the one NIC at first, the DHCP server wouldn't start, either, and I had to figure out right network settings to make DHCP run on the second, "Internal" adapter.

Create LTSP client VM

After the DHCP server was working, I created a second VM with 512M RAM and no hard drive. I set its sole network adapter to "Internal Network" so that it would be able to see the second adapter of the LTSP server, where the DHCP server was running. Then I set it to boot only from the network and started it up. It almost immediately got a response from the DHCP server, but it failed with this error:

PXE-T01: File not found
PXE-E3B: TFTP Error - File not found.

Fix bad path in LTSP DHCP configuration

This one took me some research to figure out, but it turns out that it's just a simple path problem. In the DHCP configuration of the LTSP server, there's a line that gives a path to a file called "pxeconfig.0", which is the initial file that has to be retrieved for a network boot to work. The server tells the client via DHCP where that file lives, and the client uses TFTP, "trivial file transfer protocol", to retrieve it. For some reason, the path to the file was wrong when I installed it. I don't remember what it was set to at first, and I don't have the correct value in front of me. It was something like /var/lib/tftpboot/ltsp/amd64/pxelinux.0. Also, something does some "chroot"ing somewhere, making the correct path for requesting the file /ltsp/amd64/pxelinux.0 (I think). As soon as I got that right and restarted the DHCP server, I was good to go.

Create a user with a strong enough password to suit LTSP

One last thing: after fixing the path to pxelinux.0, the diskless VM was able to get all its required files and boot to a login screen, but I couldn't log in to the client with the same credentials I was using on the server. I had created a user with a trivial password because this was just a test VM, and I read something somewhere about LTSP having different password requirements than Ubuntu, so I made another user with a stronger password, and then I could log in. That's it. Now I can start the client VM, which boots from the network, and get to a Gnome desktop. Everything I run displays on the client but runs and stores files on the server. Pretty neat.

Wednesday, February 3, 2010

Managing Unmapped Tables with Hibernate

I've published a new post over on codehangover.com explaining how you can use Hibernate's "auxiliary database objects" to simplify managing your database schema during development and testing. Check it out.

Friday, January 22, 2010

Git Quick Reference

I've been lazy/sick/on vacation for a while, but I think I'm finally ready to release my Git Quick Reference into the wild. It's kind of a follow-up to my series of Git tutorial posts, collecting all the important stuff into one, relatively short document for easy access once you've started down the road of learning your way around Git. It's available on Scribd: Git Quick Reference Google Docs: Git Quick Reference

Update (2013-04-19): Since Scribd apparently no longer offers free access, here's the Quick Reference available via my Google Drive. I'm also attempting to include it inline here for convenience. I've made little effort to ensure it looks right here. It's just a quick export from LibreOffice plus a little minor surgery to clean up the major flaws. I recommend the PDF for serious use.


Articles in this series:

Note: The handful of useful diagrams don't appear in the inline version below. At the moment, see the PDF for the complete version!

- no title specified

Git Quick Reference

This is a quick reference for Git, the DVCS. If you're looking for more detailed help than what's here, see the “Additional references” listed at the end of this document. My email address is there as well, and if you have questions I may be able to help answer them.

I've tried to list the most commonly used/useful commands here. They're broken up into a handful of categories describing general activities you perform in Git. Most commands have multiple variants, listed separately, composed of different options and arguments. A few commands have variants listed under multiple categories because they have quite varied uses. Options that apply to multiple variants of a command are listed to the far right.

Setting up the repo

Use these commands to create and configure a repository and get up and running with Git.

git init

Create a repo in the current directory.


Create a bare repo (no working tree)

git clone <url>

Clone the repo at the given URL into a local directory.

git clone <url> <dir>

Clone into the specified directory.

git config <key> <value>

Set the "key" to "value" in the config file.


Change user-wide configuration.


Change system-wide configuration.

git config -–unset <key>

Remove "key" from the config file.

git config -e|--edit

Edit the config file.

Getting things into your repo

These commands deal with turning your work into history (commits) stored in the repository. There are four areas that can hold changes, where a "change" is roughly equivalent to a new file, a removed file, or a modification to an existing file.
  1. 1) Working tree—This is your workspace. As soon as you change a file, you've changed your working tree. 

  2. 2) Index—This is a staging area for changes that are ready to be committed to the repository. When a change is staged to the index, it is no longer considered to be in your working tree, though your filesystem still reflects changes that have been staged. You stage changes when preparing to commit them. 

  3. 3) Repository—Upon commit, any and all staged changes turn into a commit in the repository. Commits are a permanent part of history. 

  4. 4) Stash—This is an out-of-sight holding area for changes. When you stash changes, they are no longer in your working tree, and the filesystem doesn't reflect them anymore, either. 

The diagram to the right roughly illustrates the flow of changes around your local repository.

The commit is the basic building block of a Git repository. Each commit has, among other things, an author, a timestamp, 0 or more parent commits, and a complete snapshot of the state of the project when the commit was made. Every commit is identified by a SHA-1 hash that is calculated from the previously mentioned properties plus some. This means that if two commits have the same hash, then they have the same parent(s), author, etc. This identity is unique even across repositories.

git add <path> [<path>...]

Stage files and directories to the index. Directories are staged recursively.

git add -p [<path>...]

Interactively choose what to stage for commit.

git mv <src> <dest>

Stage a file rename.

git add --all

Stage everything, including deletions and untracked files.

git rm <file>

Stage a file deletion.

git rm -r <dir>

Stage a recursive directory deletion.

git commit

Commit staged changes to your local repository. Opens your configured editor for a commit message.

-m "message"

Specify commit message on command line.

-C <commit>

Use the message from the given commit for this commit. Very useful in conjunction with --amend to reuse the last commit message (-C HEAD).

git commit <path> [<path>...]

Commit changes in the given files and/or directories.

git commit -a

Stage and commit all changes to tracked files.

git commit --amend

Alter the most recent commit to also contain staged changes.


This group of commands is for moving changes between the working tree, index, and stash. At first, the stash may feel like an odd thing to have in a VCS, but it will soon become second nature. A typical workflow goes like this:

  • code, code, code 

  • boss brings a hot bug that needs fixing NOW 

  • git stash 

  • fix bug 

  • commit 

  • git stash pop 

  • code, code, code 

Any time you need a quick place to shove aside your in-progress work, the stash is there waiting.

git reset

Unstage all changes. I.e. move them from the index back to the working tree.

git reset <path> [<path>...]

Unstage changes to the given files and directories.

git clean -f

Delete any non-ignored, untracked files in your working tree.


Dry run; only show what would be deleted.

git clean -fd

Delete directories as well as files.

git checkout <path> [<path>...]

Revert working tree changes to the given files and directories. Doesn't affect staged changes.

git stash

Push all changes in the working tree and index onto the top of the stash. (Acts like a stack.)

git stash list

List each stash that's been made..

git stash apply

Apply most recent stash to the working tree.


Use stash number n instead of most recent stash.

git stash pop

Apply most recent stash and remove it (if no conflicts occur).

git stash drop

Delete most recent stash.

git stash show

Show a diffstat of the most recent stash.

git stash show -p

Show a diff of the most recent stash.

Working with branches

Branches in Git are lightweight and highly flexible. In fact, a branch is little more than a Post-it note stuck to a commit that shows where the tip of the branch is. When a new commit is made at the branch tip, the Post-it moves to the new commit. Since a branch is so light, it has no knowledge of the commits it contains within itself. It just points at a commit and is considered to "contain" all ancestors of that commit. The ancestry determines the content of the branch, and it makes merging relatively trivial. To merge n branches, a new commit is created which has n parents. Each parent was the tip of one of the branches being merged. The new commit then shares the ancestry of all n branches. As you come to understand the concept of ancestry determining branch content, you'll begin to understand the power that Git places at your fingertips.

While Git is very good at merging, conflicts are sometimes unavoidable. When a conflict occurs, the merge won't be committed automatically. Instead, successfully merged files will be left in your index, and unmerged files will be in your working tree. To finish the merge, resolve the conflicts, stage the unmerged files, and do the commit yourself. Note that merge conflicts occur not only when merging, but can also happen when popping a stash, rebasing, performing remote operations, or anything else that involves combining commits somehow. For information on integrating Git with various merge tools, check out the "merge.tool" option of git config.

git branch

List local branches.

git branch -r

List remote-tracking branches.

git branch -a

List all branches (remote and local).

git branch <name>

Create a branch with the given name at your current commit.


Use with the second form above to create a branch that tracks another branch, typically a remote branch. Then remote operations on the new branch will automatically use the tracked remote branch.

git branch <name> <commit>

Create a branch with the given name pointing at the given branch or commit.

git checkout <branch name>

Make the given branch your current branch. Your working tree will reflect the state of the commit at the tip of the branch, and new commits will be applied to the branch.

git checkout <commit>

Make your working tree reflect the given commit. You won't be on a branch, and any commits made will be lost if you don't create a branch to contain them.

git checkout -b <name>

Create a new branch with your currently checked-out commit as its parent, and check out the new branch.

git checkout -b <name> <commit>

Like the git branch command of similar form, but checks out the new branch.

git branch -d/-D <name>

Delete the given branch. If not an ancestor of your current branch, you must force deletion with -D.

git merge <branch name>

Merge the given branch into your current branch.

git merge <branch name> -m "message"

Perform a merge using the given message for the commit created by the merge.

git cherry-pick <commit>

Copy the given commit to your current branch.

git reset <commit>

Move to the given commit, making it your HEAD and the tip of your current branch. Changes introduced are left in working tree.

git reset --soft <commit>

Same as above, but leave changes in the index.

git reset --hard <commit>

Same as above, but don't leave changes anywhere.


While this really belongs to the section on branches, it's a difficult concept, so I gave it its own spot. If you're coming from a centralized VCS, rebasing will take some time to wrap your head around. To make it as simple as possible, think of the commits in a branch like they're links in a chain. A rebase lets you measure out a length of the chain, cut it off at a particular link, then reattach the part you cut off to a different link somewhere else, maybe even on a different chain.

On top of that, a rebase actually reattaches a single link of the chain at a time, and you can tell it to let you modify and reorder the links (commits) as it applies them. This is called an "interactive" rebase. You can even use it to go back in history and make some changes without even moving anything.

If a rebase is interrupted by a conflict or if you're doing an interactive rebase and choose to modify a commit during the process, you'll have to give Git further instructions after you do your work. When a rebase stops, it will stop after a commit's changes have been applied to the index and/or working tree, but before the changes are committed. At that point, you can continue it, abort it, or skip the commit that it stopped on.

git rebase <commit>

Take commits in the current branch that aren't ancestors of the given commit, and move them onto the given commit. (<commit> = green; moved commits = purple)

git rebase --onto <commit2> <commit1>

Like above, but move to the "onto" commit instead. This lets you specify a range of commits to be "clipped" and moved over to any arbitrary commit. (<commit1> = orange; <commit2> = green; moved commits = purple)

git rebase -i <commit>

Rewind all commits back to <commit>, and reapply them, optionally editing commits along the way. In fact, any rebase operation can be made interactive by supplying this flag.

git rebase --continue

Continue an interrupted rebase.

git rebase --abort

Abort an interrupted rebase, returning to the state before the rebase started.

git rebase --skip

Skip the current commit and continue with the rebase. The skipped commit won't appear in the final result.

Working with other repos

This section has all the commands you need to communicate between repositories. Remoting is a key part of Git. It's what makes Git distributed. Remoting in Git consists of connecting a repository to other ones (remotes) and of pushing and pulling commits to and from those repositories. You provide a name and a URL when you configure a remote. The name is your identifier for a remote, and the URL is how Git locates it. There are a number of supported transports, but all use a similar URL format:

  •  rsync://host.xz/path/to/repo.git/ 

  •  http://host.xz/path/to/repo.git/ 

  •  https://host.xz/path/to/repo.git/ 

  •  git://host.xz/path/to/repo.git/ 

  •  ssh://[user@]host.xz[:port]/path/to/repo.git/ 

  •  file:///path/to/repo.git/ 

For a fuller treatment of URLs, see "GIT URLS" in "git help pull".

When you use remote repositories, Git automatically creates branches called "remote-tracking" branches that store the state of those remotes. You never work directly on a remote-tracking branch. They're just mirrors of your remotes that are used as synchronization points for pushing and pulling commits.

It's worth mentioning that when you clone a repository, Git creates a remote in the new repository named "origin" that points at the repository you cloned.

git remote

List this repository's remotes.

git remote add <name> <url>

Add a remote repository with the given name and located at the given URL.

git remote rename <old> <new>

Change the name of a remote repository. Updates branch names and the remote configuration as well.

git remote rm <name>

Remove a remote repository and its remote-tracking branches and configuration entries.

git fetch

Fetch unfetched commits from all branches in the remote named "origin" into remote-tracking branches.

git fetch <remote name>

Fetch from the given remote instead of "origin".

git pull <remote name> <branch>

Fetch a branch from a remote and merge it into your current working branch.

git push

Push all new commits in "matching" branches to the remote named "origin". A "matching" branch is one that exists both locally and on the remote.


Push all local branches, creating them in the remote if they don't exist.

git push <remote name>

Push matching branches to the given remote.

git push <remote name> <branch>

Push the given branch to the given remote.

Seeing the things in your repo

These commands are all read-only and give you different ways of looking at the history of your files. Many Git commands take a commit as input. In this guide, that is generally denoted with the placeholder <commit>. Most of these commands actually accept what's known as a "treeish", meaning anything that can be dereferenced to a "tree", which is an internal object stored by Git. Here are some popular forms that a treeish comes in:

  • commit hash: c42b40edc2b5b09e565e20663079e9c14b37aa21 

  • small, unique part of a commit hash: c42b4 

  • branch name: master 

  • special "current commit" ref: HEAD 

  • the suffix "^" indicates "parent of": HEAD^ master^ c42b4^ 

  • "^" applies multiple times: HEAD^^^ 

See "SPECIFYING REVISIONS" in "git help rev-parse" for more ways of naming a commit/treeish.

git status

Display paths with uncommitted changes. Shows three sections: changes in the index, changes in the working tree, and files not being tracked by Git.

git log

Show the history of your current branch or commit.


Under each commit shown in the log, show a diff of the changes introduced by that commit.


For each commit, show a summary of files changed by the commit.

git log <commit>

Show history a particular branch or commit instead of the current one.

git log <path> [<path>...]

Only show commits that affect the give path(s).

git diff

Show changes in the working tree relative to the index.


Show changes that are in the index instead of the working tree.

git diff <commit1> <commit2>

Show changes introduced by going from commit1 to commit2.

git diff <commit1>...<commit2>

Show changes that would be applied if you were to merge commit2 to commit1.

git diff <path> [<path>...]

Only show changes that affect the given path(s).

git show <commit>

Show details of and changes introduced by the given commit.

git show <commit>:<path>

Show the content of the given path at the given commit.

Additional references

For when you need a little more help...

git help

Brief list of the most common commands.

git help git

Extensive list of commands and options to the "git" command itself.

git help tutorial[-2]

Two parts of a rapid tutorial introducing basic Git operations.

git help glossary

Glossary of common Git terms to use along with other documentation.

git help <command>

Detailed help on a particular Git command.

You can find a walk-through tutorial for Git on my blog: http://shotgunsandpenguins.blogspot.com/search/label/Git

Feel free to email me with comments or questions:

Ryan Stewart <rds6235@gmail.com>

This work is licensed under the Creative Commons Attribution-Share Alike 3.0 United States License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/us/ or send a letter to Creative Commons, 171 2nd Street, Suite 300, San Francisco, California, 94105, USA.