git makes you feel stupid, by design. It doesn't mean you are, it's just hard to get to know enough to be efficient, like C++.
This presentation assumes you've already played with git a little and banged your head on the wall a few times. Otherwise, you may want to review:
t to see the table of content and
h for help.
You need these tools to be efficient:
If it's still unclear to you, the rest of this document may sound like foreign language. It'll be easier if you refer back to the topics as you are trying them, like for instance a pipelined review or squashing a change.
The following assumes you know the aliases in bin_pub:
st = status co = checkout ci = commit br = branch sr = svn rebase
git ci -a -m. does a commit with the message ".". It's faster because it's
doesn't brings up the text editor. It adds changes to all tracked files but
not untracked files, e.g. new files. For this, you need to use
git add -A .
It is easy to confuse with local and remote branch. Remote branches on a git
repo are caches of the actual remote branches. Beware of
/ in branch names.
Try this at home to be confused:
git co -b origin/trunk origin/trunk git br -a
To delete the local branch named like a remote branch, use:
git br -D origin/trunk
--prune to remove the cache of remotely
A tracked branch is the default branch that will be used when
It's also used by git-cl
to diff against. You can set a tracking branch with
br --set-track or
specifying the optional tracking branch when
co -b <new_branch> <tracked
You can do multiple changes in parallel if they are independent of each other.
You need to do multiple serial changes then a change requires a previous change to be checked in.
subversion, you can have multiple gcl changes on different files or with
multiple checkouts. You cannot do serialized changes.
git you can do serial changes. This means some changes depends on the
previous changes to be committed. For instance you have the first change
implementing an interface and the second one using it. In general you don't want
to push the second one before pushing the first one.
Pipelining is the fact of serializing multiple changes instead of sending them in parallel. It happens when a change cannot be done before the previous change is checked in. By pipelining reviews, you reduce the impact of review latency by continuing to work on your next patch. The cost is when changes are requested on your review, you will get rebase conflicts.
You cannot pipeline with svn, but you can do parallel changes by having multiple checkouts. On git, parallel changes is simply having 2 unrelated branches based of master or trunk.
git co -b 1_foo origin/trunk touch bar; git ci -a -m. git cl upload -r firstname.lastname@example.org --send-mail git co -b 2_bar touch bar; git ci -a -m. # Send the review against the previous branch. git cl upload -r email@example.com 1_foo git co -b 3_bar touch bar; git ci -a -m . # Again. git cl upload -r firstname.lastname@example.org 2_bar
# Go back to first branch as reviewer sent his # comments. git co 1_foo # Do silly style nits he requested. touch bar # Amend the commit to keep the number of commit # unchanged in the 'commit log'. git ci -a --amend # Upload a new patchset for rietveld issue # associated with 1_foo. git cl upload
Now that the original branch
1_foo doesn't point to the original hash but a
new totally unrelated hash, we need to rebase
2_bar against the new
# Rebase the branch. git co 2_bar git rebase 1_foo # Only if conflicts. git mergetool git rebase --continue # Now 2_bar is rebased against the 'new' # 1_foo. Upload a new patchset. git cl upload 1_foo
It's easy to delete a branch by error. But git is not stupid and doesn't delete information.
Rebasing is somehow creating a new branch and deleting the old one, if your rebase was done incorrectly, you may want to find the previous commit tree.
git reflog git co <hash> git co -b forgotten_branch
Or use the format
Google for more details. You can revert the
last 3 commits by checking out
Committing often is important with git. Often, commits don't stand on their own, they are worth squashing together.
# Make sure rebase is clean. git rebase trunk # Rebase in interactive mode. git rebase trunk -i # Put `s` on every commit you want to squash. # Then edit new commit description.
Warning: squash and merges don't play well together. git won't let you squash multiple changes with a merge in the lot. You'll need to squash the merge on its own.
$ git rebase -i trunk
Which brings up:
pick 49a8dd7 Force all unit tests to run. pick 2c86ef4 . pick 0ab32a4 . pick 742d03d .
To squash effectively, replace the text to:
pick 49a8dd7 Force all unit tests to run. s 2c86ef4 . s 0ab32a4 . s 742d03d .
Instead, you can simply use my
git squash alias from
This will do the same as the manual example from the last slide. Manual squashing is still useful to cherry-pick a change out of your squash.
Merging keeps the (non-linear) history, rebasing "linearilize" the history, keeping it simpler but you must not rebase a branch once you've pushed.
You can rebase when
--rebase. It's the equivalent of
rebase <tracked branch>.
You can simply checkout a single file from another branch.
git co master -- path/to/screwed.cc git ci --amend
-- separates branch name from the files list.
You can use wildcards in quotes:
"tools/*.py", otherwise bash will interprets
it. It's simpler and safer to checkout than reset.
Sometimes you start hacking on a source file to realize it should be sent as two separate reviews.
To split a single commit in two:
add -pto add parts of a file to the index.
committhe first part in a named branch without using
committhe remaining in another pipelined branch using
git co -b 1_foo master # Read git merge man page. git merge --squash crappy_change # Remove everything from the index. git reset --mixed # Add only stuff you want git add -p # Do not use -a ! git ci -m "Part 1" git co -b 2_bar # Adds unversioned files since # reset --mixed will drop them. git add -A . git ci -m "Part 2"
It's hard to set partners right to achieve a 3 ways but it is useful in collaborative environment. Examples:
In that scenario, they can push and pull directly from each others, without having to access the remote official repository at all. With hashes, they can confirm the whole history of commit without fear. The trick here is to merge and not rebase so the history relationship is kept.
Use case: clone the git repo from your workstation to your laptop so you can work during your MTV visit.
# Computer #1 (initial setup on workstation) git clone \ http://git.chromium.org/git/chromium.git \ ~/path/to/src # Computer #2 (the laptop) git clone computer_1:path/to/src # 3rd way. git add remote official \ http://git.chromium.org/git/chromium.git
# Now you can sync once in a while from # wherever you want. git pull official trunk # And push a specific branch git co awesome # 'origin' is the workstation git push awesome origin awesome # To make sure everything is in order git remote -v
This way, you can continue to work on your laptop seamlessly, and push back your changes to your workstation to continue working once you're back on the ground.
To push on a repository that has the branch checked out, you'll need to set
git config receive.denyCurrentBranch = warn
So you will be warned if you push to a repository but it will still let it
through. Once you back at the workstation you will want to
git reset --hard to
reset the files to match what is in the index.
Let's say you have a remote named
origin, which is your workstation and you
are working on your laptop.
When you push, you want to push all the branches all the time since your workstation is the golden copy, then set this configuration:
git config remote.origin.push \ "+refs/heads/*:refs/remotes/laptop/*"
This will push all your local branches as remote branches on the remote repository, as if they were on the remote repository "laptop".
People working on chromium.git may want to have separate checkouts for their branches to improve compilation speed; switching branch forces rebuilding a lot of files; See git-new-workdir in contrib more details.
I heard Linus uses cvs at home:
« What will you idiots do next? Force everyone to use emacs? »
Look for more git aliases on the official git wiki.