summaryrefslogtreecommitdiffstats
path: root/chromium/docs/website/site/developers/how-tos/get-the-code/working-with-branches/index.md
blob: 52f14a41843c3b5c02e0e9b3ef624899b315c4c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
---
breadcrumbs:
- - /developers
  - For Developers
- - /developers/how-tos
  - How-Tos
- - /developers/how-tos/get-the-code
  - 'Get the Code: Checkout, Build, & Run Chromium'
page_name: working-with-branches
title: Working with Branches
---

*This applies to commits to Chromium repository branches. For changes to
Chromium OS repositories, see the information
[here](/chromium-os/how-tos-and-troubleshooting/working-on-a-branch).*

### Basics

Enumerate your local branches:

cd src
git branch

Switching from one branch to another: Example: Switching from branch 'branch1'
to branch 'branch2'.

cd src
git checkout branch2

Note that `-` can be used to refer to the previous branch, which is useful when
switching between two branches.

```none
git checkout some_branch
git checkout -  # back to previous branch!
```

### Suggested branching workflow

We normally do our feature work in branches to keep changes isolated from each
other. The recommended workflow is to make local branches off the server master,
referred to as the origin/master branch. The `git-new-branch` command (in
depot_tools) will do this:

    git new-branch branch_name

Note that this is equivalent to the following:

    git checkout -b new_branch origin/master

### Branch off a branch

If you have dependent changes, a very productive workflow is to have a branch
off a branch. Notably, this means that your patch sets (in Rietveld) will show
the separate changes, and be easy to review, rather than showing the merged
changes (including irrelevant changes), and means you can commit downstream CLs
without having to rebase them after the upstream has landed. Do this as follows:

```none
git checkout master
git checkout branch1
# some edits, commit
git checkout -b branch2
git branch --set-upstream-to=branch1
# some edits, commit
```

Now when you update the first branch, you can simply git pull on the second
branch as normal to pull in your changes:

```none
git checkout branch1
# some edits, commit
git checkout branch2
git pull
```

**Splitting up a CL**

A common variant of "branch off a branch" is splitting up a large CL into
pieces. Given a local branch `big`, you'd like to split it up into `branch1` and
`branch2`.

One way to do this is to split off `branch1`, have that reviewed and committed,
update local repo (`gclient sync`), and then rebase `big` to `master`. This is
ok, but adds latency, and you can get the same result locally, so the second
part of the CL can be uploaded and reviewed even before the first part is
committed.

This can be done manually, particularly if the files don't overlap, by making a
new branch `branch1`, manually copying the changed files in the first part, then
branching `branch2` off of that, and manually copying the files in the second
part.

However, this can be done more cleanly via git. The main points are `git-add -i`
([Interactive
Staging](http://git-scm.com/book/en/Git-Tools-Interactive-Staging): to
interactively choose files or hunks of a patch set) and `git-rebase` (to set the
second part to be dependent on the first part, removing the common changes);
[`git-cherry-pick`](http://git-scm.com/docs/git-cherry-pick) is a technicality:

```none
# first split off branch1
git checkout origin/master
git new-branch branch1
git cherry-pick -n ..big  # apply and stage all ancestors of big that are not ancestors of HEAD, do not commit
git add -i  # interactively choose which files or hunks to stage
git commit  # commit staged changes
git checkout -- . # discard unstaged changes
# next set big to be a branch off of branch1
git checkout big
git branch --set-upstream-to=branch1
git rebase branch1  # may need to resolve conflicts
git branch -m branch2  # done!
```

Concretely, `git-cherry-pick -n` applies and stages all changes, but does not
commit them. In `git-add -i`, you can unstage files via `3: **r**evert`, and
then stage files (or hunks of files, or edit the diff manually) via `5:
**p**atch`, with manually editing via by the `e - manually edit the current
hunk` option when staging a hunk. While tedious, this is often easier than
resolving conflicts during rebasing.

### Deleting an obsolete branch

You generally want to delete local branches after the changes have been
committed to master. It is safest to check that your work has, in fact, been
committed before deleting it.

Remember that you can always apply a CL that has been posted via:

git checkout -b myworkfrompatch
git cl patch 12345 # Using CL issue number, not bug number, applies latest patch
set
git cl patch https://codereview.chromium.org/download/issue12345_1.diff # Use
URL of raw patch for older patch sets

...so as long as your CL has been posted, you can easily recover your work.
Otherwise, you'll need to dig through the git repository, so be careful.

Simply, if master (remote or local) is up-to-date and your branch has been
rebased, git branch -d will delete the branch. If not, it will refuse; to force
deletion, use git branch -D. So make sure master is up-to-date, rebase branch,
and then try deleting (optionally check manually before).

Beware that if your local branch has many revisions (instead of always amending
a single revision), rebasing to master may fail, since it will try to apply the
patches incrementally. You can avoid this by squashing your local revisions into
a single revision (see [6.4 Git Tools - Rewriting
History](http://git-scm.com/book/en/Git-Tools-Rewriting-History), or more simply
[squashing commits with
rebase](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html)).
This may be more trouble than it's worth, but it's the safest way.

To check that the branch has been committed upstream:

git checkout mywork
git rebase origin/master
git diff --stat origin/master # optional check

If there are no differences, delete the branch:

git checkout origin/master
git branch -d mywork # will only work if has been merged

**NOTE:** If you haven't waited long enough after your commit, it is possible
that 'git fetch' did not get your commit and git will continue to believe that
you have local changes, which will prevent "git branch -d" from succeeding. It's
best to redo the steps later, (The repo is updated every 3 minutes) but you can
also instruct git to force-delete your branch.

Note that when you delete your branch, it will give you the SHA-1 hash for its
tip:

Deleted branch mywork (was 123abc0).

You can then recover the branch via:

git checkout -b mywork 123abc0

If you forget the hash, you can find it via git reflog. (Reference: [Can I
recover branch after the deletion in
git?](http://stackoverflow.com/questions/3640764/can-i-recover-branch-after-the-deletion-in-git))

### Prevent commits to master

If you commit to master, updating will be messy. (This is a good reason to
simply delete master entirely, as noted near the top of this document. You only
need to read the remainder of this section if you don't do this.)

You can prevent this by adding a pre-commit hook that checks if you're in master
and stops you from doing this. Create a file named
chromium/src/.git/hooks/pre-commit and add the below to it, then mark
executable. (Blink developers: add in blink directory as well.)

#!/bin/bash
# Prevent commits against the 'master' branch
if \[\[ \`git rev-parse --abbrev-ref HEAD\` == 'master' \]\]
then
echo 'You cannot commit to the master branch!'
echo 'Stash your changes and apply them to another branch, using:'
echo 'git stash'
echo 'git checkout <branch>'
echo 'git stash apply'
exit 1
fi

If you've accidentally uploaded a change list from master, you can clear the
association of an issue number with master via:

git cl issue 0

If you've accidentally committed to master, then, after copying your work to a
new branch (e.g., make patch and then apply to new branch), you can clean up
master by deleting your accidental commit as per [this
answer](http://stackoverflow.com/a/6866485) to [How to undo the last Git
commit?](http://stackoverflow.com/questions/927358/how-to-undo-the-last-git-commit)
-- see more details there.

git reset --hard HEAD~1