You can clone with
HTTPS or Subversion.
At the end of chapter 2, there is a small "exercise" showing a few different ways that git can be used to solve a problem. It would be great to have one or two of these in each chapter.
My aim here wasn't to test the reader, but to show off the flexibility of git. Even with basic commands, you can solve a problem in several ways. Ideally, the reader is encouraged to think up new uses for old commands throughout the tutorial.
Instead of "Here are some ways to do X", I felt it was more thought-provoking to ask "How would you do X?". I went further and gave it in an "Exercise" title: I involuntarily sit up and pay more attention when I come across exercises in a technical book, and I'm hoping others do the same.
But I suppose it is a bit of a tease, as it suggests there are more exercises ahead. I'd be happy to add a few, if they're helpful and interesting. I just haven't thought of any yet.
Here's one that's sort of similar to what happens to me from time to time. I don't know which chapter it would fit into best.
You're on branch B1, and you commit some changes (commit C1). Then you start working on a new set of changes which will eventually become commit C2, once they're finished. Suddenly you realize that both C2 and C1 actually belong on branch B2. What do you do?
There's probably a good answer involving git stash but here's what I would do:
Finish up the work for C2 and commit it. Then:
git checkout B2
git cherry-pick C1
git cherry-pick C2
Then remove the commits from B1:
git checkout B1
git reset --hard C1^
I hope this all made sense...
Nice! A realistic question (I've encountered this before too) that poses two problems at once (moving commits, and what to do with the uncommitted edits) with multiple solutions.
As you suggest, instead of putting up with the mess until C2, you can use stash:
$ git stash
$ git checkout B2
$ git cherry-pick C1
$ git stash apply
Then clean up B1, using your solution for example. Some might prefer avoiding the (eventually) destructive "reset --hard" by renaming the erroneous branch: e.g. maybe you committed C1.5 between C1 and C2 but only remembered it much later:
$ git branch -m B1 oops
$ git branch B1 C1^
[Months pass... $ git log oops # Phew! C1.5 is still around!]
Rebase solves the problem elegantly. After committing C2 or stashing:
$ git branch -m tmp # Let's call our current state "tmp".
$ git branch B1 C1^ # Move B1 to its rightful place.
$ git rebase --onto B2 B1 # Magic! Git transplants B1..tmp onto B2.
$ git branch -M B2 # We're where B2 should be. Rename accordingly.
Besides being able to show off deeper knowledge of Git, I like this because it scales well for many commits: one line does all the work. The branch names are administrative details.
However, rebase can cause confusion if wrongly invoked. You may want to first make a backup of tmp (e.g. git branch oldtmp tmp), or get comfortable with git reflog so you can undo. Run git log to verify.
There may also be subtle differences: if a commit between B1 and tmp is textually the same as one in B2 (i.e. it only differs in "meta"-information such as the timestamp, author, comments, etc.), it will be dropped. This is because rebase is meant for rearranging your tree after pulling in the official repo, which may already contain some of your earlier commits.