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