| # Git Cookbook |
| |
| A collection of git recipes to do common git tasks. |
| |
| See also [Git Tips](git_tips.md). |
| |
| [TOC] |
| |
| ## Introduction |
| |
| This is designed to be a cookbook for common command sequences/tasks relating to |
| git, git-cl, and how they work with chromium development. It might be a little |
| light on explanations. |
| |
| If you are new to git, or do not have much experience with a distributed version |
| control system, you should also check out |
| [The Git Community Book](http://book.git-scm.com/) for an overview of basic git |
| concepts and general git usage. Knowing what git means by branches, commits, |
| reverts, and resets (as opposed to what SVN means by them) will help make the |
| following much more understandable. |
| |
| ## Excluding file(s) from git-cl, while preserving them for later use |
| |
| Since git-cl assumes that the diff between your current branch and its tracking |
| branch (defaults to the svn-trunk if there is no tracking branch) is what should |
| be used for the CL, the goal is to remove the unwanted files from the current |
| branch, and preserve them in another branch, or a similar. |
| |
| ### Method #1: Reset your current branch, and selectively commit files. |
| |
| 1. `git log` See the list of your commits. Find the hash of the last commit |
| before your changes. |
| 1. `git reset --soft abcdef` where abcdef is the hash found in the step above. |
| 1. `git commit <files_for_this_cl> -m "files to upload"` commit the files you |
| want included in the CL here. |
| 1. `git checkout -b new_branch_name origin/trunk` Create a new branch for the |
| files that you want to exclude. |
| 1. `git commit -a -m "preserved files"` Commit the rest of the files. |
| |
| ### Method #2: Create a new branch, reset, then commit files to preserve |
| |
| This method creates a new branch from your current one to preserve your changes. |
| The commits on the new branch are undone, and then only the files you want to |
| preserve are recommitted. |
| |
| 1. `git checkout -b new_branch_name` This preserves your old files. |
| 1. `git log` See the list of your commits. Find the hash of the last commit |
| before your changes. |
| 1. `git reset --soft abcdef` Where abcdef is the hash found in the step above. |
| 1. `git commit <files_to_preserve> -m "preserved files"` Commit the found files |
| into the `new_branch_name`. |
| |
| Then revert your files however you'd like in your old branch. The files listed |
| in step 4 will be saved in `new_branch_name` |
| |
| ### Method #3: Cherry pick changes into review branches |
| |
| If you are systematic in creating separate local commits for independent |
| changes, you can make a number of different changes in the same client and then |
| cherry-pick each one into a separate review branch. |
| |
| 1. Make and commit a set of independent changes. |
| 1. `git log` # see the hashes for each of your commits. |
| 1. repeat checkout, cherry-pick, upload steps for each change1..n |
| 1. `git checkout -b review-changeN origin` Create a new review branch |
| tracking origin |
| 1. `git cherry-pick <hash of change N>` |
| 1. `git cl upload` |
| |
| If a change needs updating due to review comments, you can go back to your main |
| working branch, update the commit, and re-cherry-pick it into the review branch. |
| |
| 1. `git checkout <working branch>` |
| 1. Make changes. |
| 1. If the commit you want to update is the most recent one: |
| 1. `git commit --amend <files>` |
| 1. If not: |
| 1. `git commit <files>` |
| 1. `git rebase -i origin` # use interactive rebase to squash the new |
| commit into the old one. |
| 1. `git log` # observe new hash for the change |
| 1. `git checkout review-changeN` |
| 1. `git reset --hard` # remove the previous version of the change |
| 1. `cherry-pick <new hash of change N>` |
| 1. `git cl upload` |
| |
| ## Sharing code between multiple machines |
| |
| Assume Windows computer named vista, Linux one named penguin. |
| Prerequisite: both machine have git clones of the main git tree. |
| |
| ```shell |
| vista$ git remote add linux ssh://penguin/path/to/git/repo |
| vista$ git fetch linux |
| vista$ git branch -a # should show "linux/branchname" |
| vista$ git checkout -b foobar linux/foobar |
| vista$ hack hack hack; git commit -a |
| vista$ git push linux # push branch back to linux |
| penguin$ git reset --hard # update with new stuff in branch |
| ``` |
| |
| Note that, by default, `gclient sync` will update all remotes. If your other |
| machine (i.e., `penguin` in the above example) is not always available, |
| `gclient sync` will timeout and fail trying to reach it. To fix this, you may |
| exclude your machine from being fetched by default: |
| |
| vista$ git config --bool remote.linux.skipDefaultUpdate true |
| |
| ## Reverting and undoing reverts |
| |
| Two commands to be familiar with: |
| |
| * `git cherry-pick X` -- patch in the change made in revision X (where X is a |
| hash, or HEAD~2, or whatever). |
| * `git revert X` -- patch in the **inverse** of the change made. |
| |
| With that in hand, say you learned that the commit `abcdef` you just made was |
| bad. |
| |
| Revert it locally: |
| |
| ```shell |
| git checkout origin # start with trunk |
| git show abcdef # grab the svn revision that abcdef was |
| git revert abcdef |
| # an editor will pop up; be sure to replace the unhelpful git hash |
| # in the commit message with the svn revision number |
| ``` |
| |
| Commit the revert: |
| |
| ```shell |
| # note that since "git svn dcommit" commits each local change separately, be |
| # extra sure that your commit log looks exactly like what you want the tree's |
| # commit log to look like before you do this. |
| git log # double check that the commit log is *exactly* what you want |
| git svn dcommit # commit to svn, bypassing all precommit checks and prompts |
| ``` |
| |
| Roll it forward again locally: |
| |
| ```shell |
| # go back to your old branch again, and reset the branch to origin, which now |
| # has your revert. |
| git checkout mybranch |
| git reset --hard origin |
| |
| |
| git cherry-pick abcdef # re-apply your bad change |
| git show # grab the rietveld issue number out of the old commit |
| git cl issue 12345 # restore the rietveld issue that was cleared on commit |
| ``` |
| |
| And now you can continue hacking where you left off, and since you're reusing |
| the Reitveld issue you don't have to rewrite the commit message. (You may want |
| to go manually reopen the issue on the Rietveld site -- `git cl status` will |
| give you the URL.) |
| |
| ## Retrieving, or diffing against an old file revision |
| |
| Git works in terms of commits, not files. Thus, working with the history of a |
| single file requires modified version of the show and diff commands. |
| |
| ```shell |
| # Find the commit you want in the file's commit log. |
| git log path/to/file |
| # This prints out the file contents at commit 123abc. |
| git show 123abc:path/to/file |
| # Diff the current version against path/to/file against the version at |
| # path/to/file |
| git diff 123abc -- path/to/file |
| ``` |
| |
| When invoking `git show` or `git diff`, the `path/to/file` is **not relative the |
| the current directory**. It must be the full path from the directory where the |
| .git directory lives. This is different from invoking `git log` which |
| understands relative paths. |
| |
| ## Checking out pristine branch from git-svn |
| |
| In the backend, git-svn keeps a remote tracking branch that points to the the |
| commit tree representing the svn repository. The name of this branch is |
| configured during `git svn init`. The git-svn remote branch is often named |
| `origin/trunk` for Chromium, and `origin/master` for WebKit. |
| |
| If you want to checkout a "fresh" branch, you can base it directly off the |
| remote branch for svn. |
| |
| git checkout -b fresh origin/trunk # Replace with origin/master for webkit. |
| |
| |
| To find out what your git-svn remote branch name is, you can examine your |
| `.git/config` file and look for the `svn-remote` entry. It will look something |
| like this: |
| |
| ``` |
| [svn-remote "svn"] |
| url = svn://svn.chromium.org/chrome |
| fetch = trunk/src:refs/remotes/origin/trunk |
| ``` |
| |
| The last line (`fetch = trunk/src:refs/remotes/origin/trunk`), says to make |
| `trunk/src` on svn into `refs/remote/origin/trunk` in the local git checkout. |
| Which means, the name of the svn remote branch name is `origin/trunk`. You can |
| use this branch name for all sorts of actions (diff, log, show, etc.) |
| |
| ## Making your `git svn {fetch,rebase}` go fast |
| |
| If you are pulling changes from the git repository in Chromium (or WebKit), but |
| your your `git svn` commands still seem to pull each change individually from |
| svn, your repository is probably setup incorrectly. Make sure the entries in |
| your `.git/config` look something like this: |
| |
| ``` |
| [remote "origin"] |
| url = https://chromium.googlesource.com/chromium/src.git |
| fetch = +refs/heads/*:refs/remotes/origin/* |
| [svn-remote "svn"] |
| url = svn://svn.chromium.org/chrome |
| fetch = trunk/src:refs/remotes/origin/trunk |
| ``` |
| |
| Here, `git svn fetch` will update the hash in refs/remotes/origin/trunk as per |
| the `fetch =` line under `svn-remote`. Similarly, `git fetch` will update the |
| **same** tag under `refs/remotes/origin`. |
| |
| With this setup, `git fetch` will use the faster git protocol to pull changes |
| down into `origin/trunk`. This effectively updates the high-water mark for |
| `git-svn`. Later invocations of `git svn {find-rev, fetch, rebase}` will be be |
| able to skip pulling those revisions down from the svn server. Instead, it |
| will just run a regex over the commit log in `origin/trunk` and parse all the |
| `git-svn-id` lines. To rebuild the mapping. Example: |
| |
| ``` |
| commit 016d28b8c4959a3d28d2fbfb4b86c0361aad74ef |
| Author: [email protected] <[email protected]@0039d316-1c4b-4281-b951-d872f2087c98> |
| Date: Mon Jul 19 19:09:41 2010 +0000 |
| |
| Revert r42636. That hack is no longer needed now that we removed the compact |
| location bar view. |
| |
| BUG=38992 |
| |
| Review URL: http://codereview.chromium.org/3036004 |
| |
| git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52935 0039d316-1c4b-4281-b951-d872f2087c98 |
| ``` |
| |
| Will be parsed to map svn revision r52935 (on Google Code) to commit |
| 016d28b8c4959a3d28d2fbfb4b86c0361aad74ef. The parsing will generate a lot of |
| lines that look like `rXXXX = 01234ABCD`. It should generally take a minute or |
| so when doing an incremental update. |
| |
| For this to work, two things must be true: |
| |
| * The svn url in the `svn-remote` clause must exactly match the url in the |
| git-svn-id pulled form the server. |
| * The fetch from origin must write into the exact same branch that specified |
| in the fetch line of `svn-remote`. |
| |
| If either of these are not true, then `git svn fetch` and friends will talk to |
| svn directly, and be very slow. |
| |
| ## Reusing a Git mirror |
| |
| If you have a nearby copy of a Git repo, you can quickly bootstrap your copy |
| from that one then adjust it to point it at the real upstream one. |
| |
| 1. Clone a nearby copy of the code you want: `git clone coworker-machine:/path/to/repo` |
| 1. Change the URL your copy fetches from to point at the real git repo: |
| `git set-url origin http://src.chromium.org/git/chromium.git` |
| 1. Update your copy: `git fetch` |
| 1. Delete any extra branches that you picked up in the initial clone: |
| `git prune origin` |