andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 1 | # Git Cookbook |
| 2 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 3 | A collection of git recipes to do common git tasks. |
| 4 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 5 | See also [Git Tips](git_tips.md). |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 6 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 7 | [TOC] |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 8 | |
| 9 | ## Introduction |
| 10 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 11 | This is designed to be a cookbook for common command sequences/tasks relating to |
| 12 | git, git-cl, and how they work with chromium development. It might be a little |
| 13 | light on explanations. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 14 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 15 | If you are new to git, or do not have much experience with a distributed version |
| 16 | control system, you should also check out |
| 17 | [The Git Community Book](http://book.git-scm.com/) for an overview of basic git |
| 18 | concepts and general git usage. Knowing what git means by branches, commits, |
| 19 | reverts, and resets (as opposed to what SVN means by them) will help make the |
| 20 | following much more understandable. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 21 | |
| 22 | ## Excluding file(s) from git-cl, while preserving them for later use |
| 23 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 24 | Since git-cl assumes that the diff between your current branch and its tracking |
| 25 | branch (defaults to the svn-trunk if there is no tracking branch) is what should |
| 26 | be used for the CL, the goal is to remove the unwanted files from the current |
| 27 | branch, and preserve them in another branch, or a similar. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 28 | |
| 29 | ### Method #1: Reset your current branch, and selectively commit files. |
| 30 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 31 | 1. `git log` See the list of your commits. Find the hash of the last commit |
| 32 | before your changes. |
| 33 | 1. `git reset --soft abcdef` where abcdef is the hash found in the step above. |
| 34 | 1. `git commit <files_for_this_cl> -m "files to upload"` commit the files you |
| 35 | want included in the CL here. |
| 36 | 1. `git checkout -b new_branch_name origin/trunk` Create a new branch for the |
| 37 | files that you want to exclude. |
| 38 | 1. `git commit -a -m "preserved files"` Commit the rest of the files. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 39 | |
| 40 | ### Method #2: Create a new branch, reset, then commit files to preserve |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 41 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 42 | This method creates a new branch from your current one to preserve your changes. |
| 43 | The commits on the new branch are undone, and then only the files you want to |
| 44 | preserve are recommitted. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 45 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 46 | 1. `git checkout -b new_branch_name` This preserves your old files. |
| 47 | 1. `git log` See the list of your commits. Find the hash of the last commit |
| 48 | before your changes. |
| 49 | 1. `git reset --soft abcdef` Where abcdef is the hash found in the step above. |
| 50 | 1. `git commit <files_to_preserve> -m "preserved files"` Commit the found files |
| 51 | into the `new_branch_name`. |
| 52 | |
| 53 | Then revert your files however you'd like in your old branch. The files listed |
| 54 | in step 4 will be saved in `new_branch_name` |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 55 | |
| 56 | ### Method #3: Cherry pick changes into review branches |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 57 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 58 | If you are systematic in creating separate local commits for independent |
| 59 | changes, you can make a number of different changes in the same client and then |
| 60 | cherry-pick each one into a separate review branch. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 61 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 62 | 1. Make and commit a set of independent changes. |
| 63 | 1. `git log` # see the hashes for each of your commits. |
| 64 | 1. repeat checkout, cherry-pick, upload steps for each change1..n |
| 65 | 1. `git checkout -b review-changeN origin` Create a new review branch |
| 66 | tracking origin |
| 67 | 1. `git cherry-pick <hash of change N>` |
| 68 | 1. `git cl upload` |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 69 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 70 | If a change needs updating due to review comments, you can go back to your main |
| 71 | working branch, update the commit, and re-cherry-pick it into the review branch. |
| 72 | |
| 73 | 1. `git checkout <working branch>` |
| 74 | 1. Make changes. |
| 75 | 1. If the commit you want to update is the most recent one: |
| 76 | 1. `git commit --amend <files>` |
| 77 | 1. If not: |
| 78 | 1. `git commit <files>` |
| 79 | 1. `git rebase -i origin` # use interactive rebase to squash the new |
| 80 | commit into the old one. |
| 81 | 1. `git log` # observe new hash for the change |
| 82 | 1. `git checkout review-changeN` |
| 83 | 1. `git reset --hard` # remove the previous version of the change |
| 84 | 1. `cherry-pick <new hash of change N>` |
| 85 | 1. `git cl upload` |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 86 | |
| 87 | ## Sharing code between multiple machines |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 88 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 89 | Assume Windows computer named vista, Linux one named penguin. |
| 90 | Prerequisite: both machine have git clones of the main git tree. |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 91 | |
| 92 | ```shell |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 93 | vista$ git remote add linux ssh://penguin/path/to/git/repo |
| 94 | vista$ git fetch linux |
| 95 | vista$ git branch -a # should show "linux/branchname" |
| 96 | vista$ git checkout -b foobar linux/foobar |
| 97 | vista$ hack hack hack; git commit -a |
| 98 | vista$ git push linux # push branch back to linux |
| 99 | penguin$ git reset --hard # update with new stuff in branch |
| 100 | ``` |
| 101 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 102 | Note that, by default, `gclient sync` will update all remotes. If your other |
| 103 | machine (i.e., `penguin` in the above example) is not always available, |
| 104 | `gclient sync` will timeout and fail trying to reach it. To fix this, you may |
| 105 | exclude your machine from being fetched by default: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 106 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 107 | vista$ git config --bool remote.linux.skipDefaultUpdate true |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 108 | |
| 109 | ## Reverting and undoing reverts |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 110 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 111 | Two commands to be familiar with: |
| 112 | |
| 113 | * `git cherry-pick X` -- patch in the change made in revision X (where X is a |
| 114 | hash, or HEAD~2, or whatever). |
| 115 | * `git revert X` -- patch in the **inverse** of the change made. |
| 116 | |
| 117 | With that in hand, say you learned that the commit `abcdef` you just made was |
| 118 | bad. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 119 | |
| 120 | Revert it locally: |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 121 | |
| 122 | ```shell |
| 123 | git checkout origin # start with trunk |
| 124 | git show abcdef # grab the svn revision that abcdef was |
| 125 | git revert abcdef |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 126 | # an editor will pop up; be sure to replace the unhelpful git hash |
| 127 | # in the commit message with the svn revision number |
| 128 | ``` |
| 129 | |
| 130 | Commit the revert: |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 131 | |
| 132 | ```shell |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 133 | # note that since "git svn dcommit" commits each local change separately, be |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 134 | # extra sure that your commit log looks exactly like what you want the tree's |
| 135 | # commit log to look like before you do this. |
| 136 | git log # double check that the commit log is *exactly* what you want |
| 137 | git svn dcommit # commit to svn, bypassing all precommit checks and prompts |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 138 | ``` |
| 139 | |
| 140 | Roll it forward again locally: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 141 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 142 | ```shell |
| 143 | # go back to your old branch again, and reset the branch to origin, which now |
| 144 | # has your revert. |
| 145 | git checkout mybranch |
| 146 | git reset --hard origin |
| 147 | |
| 148 | |
| 149 | git cherry-pick abcdef # re-apply your bad change |
| 150 | git show # grab the rietveld issue number out of the old commit |
| 151 | git cl issue 12345 # restore the rietveld issue that was cleared on commit |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 152 | ``` |
| 153 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 154 | And now you can continue hacking where you left off, and since you're reusing |
| 155 | the Reitveld issue you don't have to rewrite the commit message. (You may want |
| 156 | to go manually reopen the issue on the Rietveld site -- `git cl status` will |
| 157 | give you the URL.) |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 158 | |
| 159 | ## Retrieving, or diffing against an old file revision |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 160 | |
| 161 | Git works in terms of commits, not files. Thus, working with the history of a |
| 162 | single file requires modified version of the show and diff commands. |
| 163 | |
| 164 | ```shell |
| 165 | # Find the commit you want in the file's commit log. |
| 166 | git log path/to/file |
| 167 | # This prints out the file contents at commit 123abc. |
| 168 | git show 123abc:path/to/file |
| 169 | # Diff the current version against path/to/file against the version at |
| 170 | # path/to/file |
| 171 | git diff 123abc -- path/to/file |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 172 | ``` |
| 173 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 174 | When invoking `git show` or `git diff`, the `path/to/file` is **not relative the |
| 175 | the current directory**. It must be the full path from the directory where the |
| 176 | .git directory lives. This is different from invoking `git log` which |
| 177 | understands relative paths. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 178 | |
| 179 | ## Checking out pristine branch from git-svn |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 180 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 181 | In the backend, git-svn keeps a remote tracking branch that points to the the |
| 182 | commit tree representing the svn repository. The name of this branch is |
| 183 | configured during `git svn init`. The git-svn remote branch is often named |
| 184 | `origin/trunk` for Chromium, and `origin/master` for WebKit. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 185 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 186 | If you want to checkout a "fresh" branch, you can base it directly off the |
| 187 | remote branch for svn. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 188 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 189 | git checkout -b fresh origin/trunk # Replace with origin/master for webkit. |
| 190 | |
| 191 | |
| 192 | To find out what your git-svn remote branch name is, you can examine your |
| 193 | `.git/config` file and look for the `svn-remote` entry. It will look something |
| 194 | like this: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 195 | |
| 196 | ``` |
| 197 | [svn-remote "svn"] |
| 198 | url = svn://svn.chromium.org/chrome |
| 199 | fetch = trunk/src:refs/remotes/origin/trunk |
| 200 | ``` |
| 201 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 202 | The last line (`fetch = trunk/src:refs/remotes/origin/trunk`), says to make |
| 203 | `trunk/src` on svn into `refs/remote/origin/trunk` in the local git checkout. |
| 204 | Which means, the name of the svn remote branch name is `origin/trunk`. You can |
| 205 | use this branch name for all sorts of actions (diff, log, show, etc.) |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 206 | |
| 207 | ## Making your `git svn {fetch,rebase}` go fast |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 208 | |
| 209 | If you are pulling changes from the git repository in Chromium (or WebKit), but |
| 210 | your your `git svn` commands still seem to pull each change individually from |
| 211 | svn, your repository is probably setup incorrectly. Make sure the entries in |
| 212 | your `.git/config` look something like this: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 213 | |
| 214 | ``` |
| 215 | [remote "origin"] |
| 216 | url = https://chromium.googlesource.com/chromium/src.git |
| 217 | fetch = +refs/heads/*:refs/remotes/origin/* |
| 218 | [svn-remote "svn"] |
| 219 | url = svn://svn.chromium.org/chrome |
| 220 | fetch = trunk/src:refs/remotes/origin/trunk |
| 221 | ``` |
| 222 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 223 | Here, `git svn fetch` will update the hash in refs/remotes/origin/trunk as per |
| 224 | the `fetch =` line under `svn-remote`. Similarly, `git fetch` will update the |
| 225 | **same** tag under `refs/remotes/origin`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 226 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 227 | With this setup, `git fetch` will use the faster git protocol to pull changes |
| 228 | down into `origin/trunk`. This effectively updates the high-water mark for |
| 229 | `git-svn`. Later invocations of `git svn {find-rev, fetch, rebase}` will be be |
| 230 | able to skip pulling those revisions down from the svn server. Instead, it |
| 231 | will just run a regex over the commit log in `origin/trunk` and parse all the |
| 232 | `git-svn-id` lines. To rebuild the mapping. Example: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 233 | |
| 234 | ``` |
| 235 | commit 016d28b8c4959a3d28d2fbfb4b86c0361aad74ef |
| 236 | Author: [email protected] <[email protected]@0039d316-1c4b-4281-b951-d872f2087c98> |
| 237 | Date: Mon Jul 19 19:09:41 2010 +0000 |
| 238 | |
| 239 | Revert r42636. That hack is no longer needed now that we removed the compact |
| 240 | location bar view. |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 241 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 242 | BUG=38992 |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 243 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 244 | Review URL: http://codereview.chromium.org/3036004 |
| 245 | |
| 246 | git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52935 0039d316-1c4b-4281-b951-d872f2087c98 |
| 247 | ``` |
| 248 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 249 | Will be parsed to map svn revision r52935 (on Google Code) to commit |
| 250 | 016d28b8c4959a3d28d2fbfb4b86c0361aad74ef. The parsing will generate a lot of |
| 251 | lines that look like `rXXXX = 01234ABCD`. It should generally take a minute or |
| 252 | so when doing an incremental update. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 253 | |
| 254 | For this to work, two things must be true: |
| 255 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 256 | * The svn url in the `svn-remote` clause must exactly match the url in the |
| 257 | git-svn-id pulled form the server. |
| 258 | * The fetch from origin must write into the exact same branch that specified |
| 259 | in the fetch line of `svn-remote`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 260 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 261 | If either of these are not true, then `git svn fetch` and friends will talk to |
| 262 | svn directly, and be very slow. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 263 | |
| 264 | ## Reusing a Git mirror |
| 265 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 266 | If you have a nearby copy of a Git repo, you can quickly bootstrap your copy |
| 267 | from that one then adjust it to point it at the real upstream one. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 268 | |
andybons | 22afb31 | 2015-08-31 02:24:51 | [diff] [blame^] | 269 | 1. Clone a nearby copy of the code you want: `git clone coworker-machine:/path/to/repo` |
| 270 | 1. Change the URL your copy fetches from to point at the real git repo: |
| 271 | `git set-url origin http://src.chromium.org/git/chromium.git` |
| 272 | 1. Update your copy: `git fetch` |
| 273 | 1. Delete any extra branches that you picked up in the initial clone: |
| 274 | `git prune origin` |