]> git.proxmox.com Git - rustc.git/blame - src/doc/rustc-dev-guide/src/git.md
New upstream version 1.69.0+dfsg1
[rustc.git] / src / doc / rustc-dev-guide / src / git.md
CommitLineData
6a06907d
XL
1# Using Git
2
3<!-- toc -->
4
5The Rust project uses [Git] to manage its source code. In order to
6contribute, you'll need some familiarity with its features so that your changes
7can be incorporated into the compiler.
8
9[Git]: https://git-scm.com
10
11The goal of this page is to cover some of the more common questions and
12problems new contributors face. Although some Git basics will be covered here,
13if you find that this is still a little too fast for you, it might make sense
14to first read some introductions to Git, such as the Beginner and Getting
15started sections of [this tutorial from Atlassian][atlassian-git]. GitHub also
16provides [documentation] and [guides] for beginners, or you can consult the
17more in depth [book from Git].
18
9c376795
FG
19This guide is incomplete. If you run into trouble with git that this page doesn't help with,
20please [open an issue] so we can document how to fix it.
21
22[open an issue]: https://github.com/rust-lang/rustc-dev-guide/issues/new
6a06907d
XL
23[book from Git]: https://git-scm.com/book/en/v2/
24[atlassian-git]: https://www.atlassian.com/git/tutorials/what-is-version-control
923072b8 25[documentation]: https://docs.github.com/en/get-started/quickstart/set-up-git
6a06907d
XL
26[guides]: https://guides.github.com/introduction/git-handbook/
27
28## Prerequisites
29
30We'll assume that you've installed Git, forked [rust-lang/rust], and cloned the
31forked repo to your PC. We'll use the command line interface to interact
32with Git; there are also a number of GUIs and IDE integrations that can
33generally do the same things.
34
35[rust-lang/rust]: https://github.com/rust-lang/rust
36
37If you've cloned your fork, then you will be able to reference it with `origin`
38in your local repo. It may be helpful to also set up a remote for the official
39rust-lang/rust repo via
40
41```sh
42git remote add upstream https://github.com/rust-lang/rust.git
43```
44
45if you're using HTTPS, or
46
47```sh
48git remote add upstream git@github.com:rust-lang/rust.git
49```
50
51if you're using SSH.
52
136023e0
XL
53**NOTE:** This page is dedicated to workflows for `rust-lang/rust`, but will likely be
54useful when contributing to other repositories in the Rust project.
55
56
6a06907d
XL
57## Standard Process
58
59Below is the normal procedure that you're likely to use for most minor changes
60and PRs:
61
62 1. Ensure that you're making your changes on top of master:
63 `git checkout master`.
136023e0 64 2. Get the latest changes from the Rust repo: `git pull upstream master --ff-only`.
9c376795 65 (see [No-Merge Policy][no-merge-policy] for more info about this).
6a06907d
XL
66 3. Make a new branch for your change: `git checkout -b issue-12345-fix`.
67 4. Make some changes to the repo and test them.
68 5. Stage your changes via `git add src/changed/file.rs src/another/change.rs`
69 and then commit them with `git commit`. Of course, making intermediate commits
70 may be a good idea as well. Avoid `git add .`, as it makes it too easy to
71 unintentionally commit changes that should not be committed, such as submodule
72 updates. You can use `git status` to check if there are any files you forgot
73 to stage.
136023e0
XL
74 6. Push your changes to your fork: `git push --set-upstream origin issue-12345-fix`
75 (After adding commits, you can use `git push` and after rebasing or
76pulling-and-rebasing, you can use `git push --force-with-lease`).
77 7. [Open a PR][ghpullrequest] from your fork to `rust-lang/rust`'s master branch.
6a06907d
XL
78
79[ghpullrequest]: https://guides.github.com/activities/forking/#making-a-pull-request
80
136023e0
XL
81If you end up needing to rebase and are hitting conflicts, see [Rebasing](#rebasing).
82If you want to track upstream while working on long-running feature/issue, see
9c376795 83[Keeping things up to date][no-merge-policy].
136023e0 84
6a06907d
XL
85If your reviewer requests changes, the procedure for those changes looks much
86the same, with some steps skipped:
87
88 1. Ensure that you're making changes to the most recent version of your code:
89 `git checkout issue-12345-fix`.
90 2. Make, stage, and commit your additional changes just like before.
91 3. Push those changes to your fork: `git push`.
92
9c376795
FG
93 [no-merge-policy]: #keeping-things-up-to-date
94
6a06907d
XL
95## Troubleshooting git issues
96
97You don't need to clone `rust-lang/rust` from scratch if it's out of date!
98Even if you think you've messed it up beyond repair, there are ways to fix
99the git state that don't require downloading the whole repository again.
100Here are some common issues you might run into:
101
9c376795
FG
102### I made a merge commit by accident.
103
104Git has two ways to update your branch with the newest changes: merging and rebasing.
105Rust [uses rebasing][no-merge-policy]. If you make a merge commit, it's not too hard to fix:
106`git rebase -i upstream/master`.
107
108See [Rebasing](#rebasing) for more about rebasing.
109
6a06907d
XL
110### I deleted my fork on GitHub!
111
112This is not a problem from git's perspective. If you run `git remote -v`,
113it will say something like this:
114
115```
116$ git remote -v
9c376795
FG
117origin git@github.com:jyn514/rust.git (fetch)
118origin git@github.com:jyn514/rust.git (push)
119upstream https://github.com/rust-lang/rust (fetch)
120upstream https://github.com/rust-lang/rust (fetch)
6a06907d
XL
121```
122
123If you renamed your fork, you can change the URL like this:
124
125```console
9c376795 126git remote set-url origin <URL>
6a06907d
XL
127```
128
129where the `<URL>` is your new fork.
130
9c376795
FG
131### I changed a submodule by accident
132
133Usually people notice this when rustbot posts a comment on github that `cargo` has been modified:
134
135![rustbot submodule comment](./img/rustbot-submodules.png)
136
137You might also notice conflicts in the web UI:
138
139![conflict in src/tools/cargo](./img/submodule-conflicts.png)
140
141The most common cause is that you rebased after a change and ran `git add .` without first running
142`x.py` to update the submodules. Alternatively, you might have run `cargo fmt` instead of `x fmt`
143and modified files in a submodule, then committed the changes.
144
145To fix it, do the following things:
146
1471. See which commit has the accidental changes: `git log --stat -n1 src/tools/cargo`
1482. Revert the changes to that commit: `git checkout <my-commit>~ src/tools/cargo`. Type `~`
149 literally but replace `<my-commit>` with the output from step 1.
1503. Tell git to commit the changes: `git commit --fixup <my-commit>`
1514. Repeat steps 1-3 for all the submodules you modified.
152 - If you modified the submodule in several different commits, you will need to repeat steps 1-3
153 for each commit you modified. You'll know when to stop when the `git log` command shows a commit
154 that's not authored by you.
1555. Squash your changes into the existing commits: `git rebase --autosquash -i upstream/master`
1566. [Push your changes](#standard-process).
157
158### I see "error: cannot rebase" when I try to rebase
159
160These are two common errors to see when rebasing:
161```
162error: cannot rebase: Your index contains uncommitted changes.
163error: Please commit or stash them.
164```
165```
166error: cannot rebase: You have unstaged changes.
167error: Please commit or stash them.
168```
169
170(See https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F#_the_three_states for the difference between the two.)
171
172This means you have made changes since the last time you made a commit. To be able to rebase, either
173commit your changes, or make a temporary commit called a "stash" to have them still not be commited
174when you finish rebasing. You may want to configure git to make this "stash" automatically, which
175will prevent the "cannot rebase" error in nearly all cases:
176
177```
178git config --global rebase.autostash true
179```
180
181See https://git-scm.com/book/en/v2/Git-Tools-Stashing-and-Cleaning for more info about stashing.
182
6a06907d
XL
183### I see 'Untracked Files: src/stdarch'?
184
185This is left over from the move to the `library/` directory.
186Unfortunately, `git rebase` does not follow renames for submodules, so you
187have to delete the directory yourself:
188
189```console
190rm -r src/stdarch
191```
192
193### I see `<<< HEAD`?
194
195You were probably in the middle of a rebase or merge conflict. See
196[Conflicts](#conflicts) for how to fix the conflict. If you don't care about the changes
197and just want to get a clean copy of the repository back, you can use `git reset`:
198
199```console
200# WARNING: this throws out any local changes you've made! Consider resolving the conflicts instead.
201git reset --hard master
202```
203
9c376795
FG
204### Git is trying to rebase commits I didn't write?
205
206If you see many commits in your rebase list, or merge commits, or commits by other people that you
207didn't write, it likely means you're trying to rebase over the wrong branch. For example, you may
208have a `rust-lang/rust` remote `upstream`, but ran `git rebase origin/master` instead of `git rebase
209upstream/master`. The fix is to abort the rebase and use the correct branch instead:
210
211```
212git rebase --abort
213git rebase -i upstream/master
214```
215
216<details><summary>Click here to see an example of rebasing over the wrong branch</summary>
217
218![Interactive rebase over the wrong branch](img/other-peoples-commits.png)
219
220</details>
221
6a06907d
XL
222### Quick note about submodules
223
224When updating your local repository with `git pull`, you may notice that sometimes
225Git says you have modified some files that you have never edited. For example,
226running `git status` gives you something like (note the `new commits` mention):
227
228```
229On branch master
230Your branch is up to date with 'origin/master'.
231
232Changes not staged for commit:
233 (use "git add <file>..." to update what will be committed)
234 (use "git restore <file>..." to discard changes in working directory)
2b03887a 235 modified: src/llvm-project (new commits)
6a06907d 236 modified: src/tools/cargo (new commits)
6a06907d
XL
237
238no changes added to commit (use "git add" and/or "git commit -a")
239```
240
9ffffee4
FG
241These changes are not changes to files: they are changes to submodules (more on this
242[later](#git-submodules)). To get rid of those, run `./x.py --help`, which will automatically update
243the submodules.
244
245Some submodules are not actually needed; for example, `src/llvm-project` doesn't need to be checked
246out if you're using `download-ci-llvm`. To avoid having to keep fetching its history, you can use
247`git submodule deinit -f src/llvm-project`, which will also avoid it showing as modified again.
248
249Note that, as of <!-- date-check --> Aug 2022,
f2b60f7d
FG
250there is a [bug][#77620] if you use worktrees,
251submodules, and `x.py` in a commit hook.
252If you run into an error like the following,
253it's not anything you did wrong:
6a06907d
XL
254
255```
9ffffee4 256error: failed to read `/home/jyn/rustc-worktree/src/tools/cargo/Cargo.toml`
6a06907d
XL
257
258Caused by:
259 No such file or directory (os error 2)
260```
f2b60f7d
FG
261
262There is a workaround in [the issue][#77620-workaround].
6a06907d 263
136023e0
XL
264[#77620]: https://github.com/rust-lang/rust/issues/77620
265[#77620-workaround]: https://github.com/rust-lang/rust/issues/77620#issuecomment-705228229
6a06907d 266
136023e0 267## Rebasing and Conflicts
6a06907d
XL
268
269When you edit your code locally, you are making changes to the version of
270rust-lang/rust that existed when you created your feature branch. As such, when
271you submit your PR it is possible that some of the changes that have been made
272to rust-lang/rust since then are in conflict with the changes you've made.
273
274When this happens, you need to resolve the conflicts before your changes can be
275merged. First, get a local copy of the conflicting changes: Checkout your local
276master branch with `git checkout master`, then `git pull upstream master` to
277update it with the most recent changes.
278
279### Rebasing
280
281You're now ready to start the rebasing process. Checkout the branch with your
282changes and execute `git rebase master`.
283
284When you rebase a branch on master, all the changes on your branch are
285reapplied to the most recent version of master. In other words, Git tries to
286pretend that the changes you made to the old version of master were instead
287made to the new version of master. During this process, you should expect to
288encounter at least one "rebase conflict." This happens when Git's attempt to
289reapply the changes fails because your changes conflicted with other changes
290that have been made. You can tell that this happened because you'll see
291lines in the output that look like
292
293```
294CONFLICT (content): Merge conflict in file.rs
295```
296
297When you open these files, you'll see sections of the form
298
299```
300<<<<<<< HEAD
301Original code
302=======
303Your code
304>>>>>>> 8fbf656... Commit fixes 12345
305```
306
307This represents the lines in the file that Git could not figure out how to
308rebase. The section between `<<<<<<< HEAD` and `=======` has the code from
309master, while the other side has your version of the code. You'll need to
310decide how to deal with the conflict. You may want to keep your changes,
311keep the changes on master, or combine the two.
312
313Generally, resolving the conflict consists of two steps: First, fix the
314particular conflict. Edit the file to make the changes you want and remove the
315`<<<<<<<`, `=======` and `>>>>>>>` lines in the process. Second, check the
316surrounding code. If there was a conflict, its likely there are some logical
317errors lying around too! It's a good idea to run `x.py check` here to make sure
318there are no glaring errors.
319
320Once you're all done fixing the conflicts, you need to stage the files that had
321conflicts in them via `git add`. Afterwards, run `git rebase --continue` to let
322Git know that you've resolved the conflicts and it should finish the rebase.
cdc7bbd5 323
6a06907d
XL
324Once the rebase has succeeded, you'll want to update the associated branch on
325your fork with `git push --force-with-lease`.
326
327Note that `git push` will not work properly and say something like this:
328
329```
330 ! [rejected] issue-xxxxx -> issue-xxxxx (non-fast-forward)
331error: failed to push some refs to 'https://github.com/username/rust.git'
332hint: Updates were rejected because the tip of your current branch is behind
333hint: its remote counterpart. Integrate the remote changes (e.g.
334hint: 'git pull ...') before pushing again.
335hint: See the 'Note about fast-forwards' in 'git push --help' for details.
336```
337
338The advice this gives is incorrect! Because of Rust's
339["no-merge" policy](#no-merge-policy) the merge commit created by `git pull`
340will not be allowed in the final PR, in addition to defeating the point of the
341rebase! Use `git push --force-with-lease` instead.
342
136023e0
XL
343### Keeping things up to date
344
345The above section on [Rebasing](#rebasing) is a specific
346guide on rebasing work and dealing with merge conflicts.
347Here is some general advice about how to keep your local repo
348up-to-date with upstream changes:
349
350Using `git pull upstream master` while on your local master branch regularly
351will keep it up-to-date. You will also want to rebase your feature branches
352up-to-date as well. After pulling, you can checkout the feature branches
353and rebase them:
354
355```
356git checkout master
357git pull upstream master --ff-only # to make certain there are no merge commits
9c376795
FG
358git rebase master feature_branch
359git push --force-with-lease # (set origin to be the same as local)
136023e0
XL
360```
361
94222f64 362To avoid merges as per the [No-Merge Policy](#no-merge-policy), you may want to use
c295e0f8
XL
363`git config pull.ff only` (this will apply the config only to the local repo)
364to ensure that Git doesn't create merge commits when `git pull`ing, without
365needing to pass `--ff-only` or `--rebase` every time.
136023e0 366
9c376795 367You can also `git push --force-with-lease` from master to keep your fork's master in sync with
136023e0
XL
368upstream.
369
6a06907d
XL
370## Advanced Rebasing
371
372If your branch contains multiple consecutive rewrites of the same code, or if
373the rebase conflicts are extremely severe, you can use
374`git rebase --interactive master` to gain more control over the process. This
375allows you to choose to skip commits, edit the commits that you do not skip,
376change the order in which they are applied, or "squash" them into each other.
377
378Alternatively, you can sacrifice the commit history like this:
379
380```
381# squash all the changes into one commit so you only have to worry about conflicts once
382git rebase -i $(git merge-base master HEAD) # and squash all changes along the way
383git rebase master
384# fix all merge conflicts
385git rebase --continue
386```
387
388"Squashing" commits into each other causes them to be merged into a single
389commit. Both the upside and downside of this is that it simplifies the history.
390On the one hand, you lose track of the steps in which changes were made, but
391the history becomes easier to work with.
392
393You also may want to squash just the last few commits together, possibly
394because they only represent "fixups" and not real changes. For example,
395`git rebase --interactive HEAD~2` will allow you to edit the two commits only.
396
cdc7bbd5
XL
397### `git range-diff`
398
399After completing a rebase, and before pushing up your changes, you may want to
400review the changes between your old branch and your new one. You can do that
401with `git range-diff master @{upstream} HEAD`.
402
403The first argument to `range-diff`, `master` in this case, is the base revision
404that you're comparing your old and new branch against. The second argument is
405the old version of your branch; in this case, `@upstream` means the version that
406you've pushed to GitHub, which is the same as what people will see in your pull
407request. Finally, the third argument to `range-diff` is the *new* version of
408your branch; in this case, it is `HEAD`, which is the commit that is currently
409checked-out in your local repo.
410
411Note that you can also use the equivalent, abbreviated form `git range-diff
412master @{u} HEAD`.
413
414Unlike in regular Git diffs, you'll see a `-` or `+` next to another `-` or `+`
415in the range-diff output. The marker on the left indicates a change between the
416old branch and the new branch, and the marker on the right indicates a change
417you've committed. So, you can think of a range-diff as a "diff of diffs" since
418it shows you the differences between your old diff and your new diff.
419
420Here's an example of `git range-diff` output (taken from [Git's
421docs][range-diff-example-docs]):
422
423```
424-: ------- > 1: 0ddba11 Prepare for the inevitable!
4251: c0debee = 2: cab005e Add a helpful message at the start
4262: f00dbal ! 3: decafe1 Describe a bug
427 @@ -1,3 +1,3 @@
428 Author: A U Thor <author@example.com>
429
430 -TODO: Describe a bug
431 +Describe a bug
432 @@ -324,5 +324,6
433 This is expected.
434
435 -+What is unexpected is that it will also crash.
436 ++Unexpectedly, it also crashes. This is a bug, and the jury is
437 ++still out there how to fix it best. See ticket #314 for details.
438
439 Contact
4403: bedead < -: ------- TO-UNDO
441```
442
443(Note that `git range-diff` output in your terminal will probably be easier to
444read than in this example because it will have colors.)
445
446Another feature of `git range-diff` is that, unlike `git diff`, it will also
447diff commit messages. This feature can be useful when amending several commit
448messages so you can make sure you changed the right parts.
449
450`git range-diff` is a very useful command, but note that it can take some time
451to get used to its output format. You may also find Git's documentation on the
452command useful, especially their ["Examples" section][range-diff-example-docs].
453
454[range-diff-example-docs]: https://git-scm.com/docs/git-range-diff#_examples
455
6a06907d
XL
456## No-Merge Policy
457
458The rust-lang/rust repo uses what is known as a "rebase workflow." This means
459that merge commits in PRs are not accepted. As a result, if you are running
460`git merge` locally, chances are good that you should be rebasing instead. Of
461course, this is not always true; if your merge will just be a fast-forward,
462like the merges that `git pull` usually performs, then no merge commit is
463created and you have nothing to worry about. Running `git config merge.ff only`
136023e0 464(this will apply the config to the local repo).
6a06907d
XL
465once will ensure that all the merges you perform are of this type, so that you
466cannot make a mistake.
467
468There are a number of reasons for this decision and like all others, it is a
469tradeoff. The main advantage is the generally linear commit history. This
470greatly simplifies bisecting and makes the history and commit log much easier
471to follow and understand.
472
9c376795
FG
473## Tips for reviewing
474
475**NOTE**: This section is for *reviewing* PRs, not authoring them.
476
477### Hiding whitespace
478
479Github has a button for disabling whitespace changes that may be useful.
480You can also use `git diff -w origin/master` to view changes locally.
481
482![hide whitespace](./img/github-whitespace-changes.png)
483
484### Fetching PRs
485
486To checkout PRs locally, you can use `git fetch upstream pull/NNNNN/head && git checkout
487FETCH_HEAD`.
488
489You can also use github's cli tool. Github shows a button on PRs where you can copy-paste the
490command to check it out locally. See <https://cli.github.com/> for more info.
491
492![`gh` suggestion](./img/github-cli.png)
493
494### Moving large sections of code
495
496Git and Github's default diff view for large moves *within* a file is quite poor; it will show each
497line as deleted and each line as added, forcing you to compare each line yourself. Git has an option
498to show moved lines in a different color:
499
500```
501git log -p --color-moved=dimmed-zebra --color-moved-ws=allow-indentation-change
502```
503
504See [the docs for `--color-moved`](https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---color-movedltmodegt) for more info.
505
506### range-diff
507
508See [the relevant section for PR authors](#git-range-diff). This can be useful for comparing code
509that was force-pushed to make sure there are no unexpected changes.
510
6a06907d
XL
511## Git submodules
512
513**NOTE**: submodules are a nice thing to know about, but it *isn't* an absolute
514prerequisite to contribute to `rustc`. If you are using Git for the first time,
515you might want to get used to the main concepts of Git before reading this section.
516
517The `rust-lang/rust` repository uses [Git submodules] as a way to use other
518Rust projects from within the `rust` repo. Examples include Rust's fork of
2b03887a 519`llvm-project`, `cargo` and libraries like `stdarch` and `backtrace`.
6a06907d 520
17df50a5 521Those projects are developed and maintained in an separate Git (and GitHub)
6a06907d
XL
522repository, and they have their own Git history/commits, issue tracker and PRs.
523Submodules allow us to create some sort of embedded sub-repository inside the
524`rust` repository and use them like they were directories in the `rust` repository.
525
2b03887a
FG
526Take `llvm-project` for example. `llvm-project` is maintained in the [`rust-lang/llvm-project`]
527repository, but it is used in `rust-lang/rust` by the compiler for code generation and
528optimization. We bring it in `rust` as a submodule, in the `src/llvm-project` folder.
6a06907d
XL
529
530The contents of submodules are ignored by Git: submodules are in some sense isolated
2b03887a 531from the rest of the repository. However, if you try to `cd src/llvm-project` and then
6a06907d
XL
532run `git status`:
533
534```
2b03887a 535HEAD detached at 9567f08afc943
6a06907d
XL
536nothing to commit, working tree clean
537```
538
2b03887a 539As far as git is concerned, you are no longer in the `rust` repo, but in the `llvm-project` repo.
3c0e092e 540You will notice that we are in "detached HEAD" state, i.e. not on a branch but on a
6a06907d
XL
541particular commit.
542
543This is because, like any dependency, we want to be able to control which version to use.
544Submodules allow us to do just that: every submodule is "pinned" to a certain
545commit, which doesn't change unless modified manually. If you use `git checkout <commit>`
2b03887a
FG
546in the `llvm-project` directory and go back to the `rust` directory, you can stage this
547change like any other, e.g. by running `git add src/llvm-project`. (Note that if
5099ac24
FG
548you *don't* stage the change to commit, then you run the risk that running
549`x.py` will just undo your change by switching back to the previous commit when
550it automatically "updates" the submodules.)
551
552This version selection is usually done by the maintainers of the project, and
2b03887a 553looks like [this][llvm-update].
6a06907d
XL
554
555Git submodules take some time to get used to, so don't worry if it isn't perfectly
556clear yet. You will rarely have to use them directly and, again, you don't need
557to know everything about submodules to contribute to Rust. Just know that they
558exist and that they correspond to some sort of embedded subrepository dependency
559that Git can nicely and fairly conveniently handle for us.
560
561[Git submodules]: https://git-scm.com/book/en/v2/Git-Tools-Submodules
2b03887a
FG
562[`rust-lang/llvm-project`]: https://github.com/rust-lang/llvm-project
563[llvm-update]: https://github.com/rust-lang/rust/pull/99464/files