src/doc/rustc-dev-guide/src/git.md MARKDOWN 691 lines View on github.com → Search inside
1# Using Git23The Rust project uses [Git] to manage its source code.4In order to contribute, you'll need some familiarity with its features so that your changes5can be incorporated into the compiler.67[Git]: https://git-scm.com89The goal of this page is to cover some of the more common questions and10problems new contributors face.11Although some Git basics will be covered here,12if you find that this is still a little too fast for you, it might make sense13to first read some introductions to Git, such as the Beginner and Getting14started sections of [this tutorial from Atlassian][atlassian-git].15GitHub also provides [documentation] and [guides] for beginners, or you can consult the16more in depth [book from Git].1718This guide is incomplete.19If 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.2122[open an issue]: https://github.com/rust-lang/rustc-dev-guide/issues/new23[book from Git]: https://git-scm.com/book/en/v2/24[atlassian-git]: https://www.atlassian.com/git/tutorials/what-is-version-control25[documentation]: https://docs.github.com/en/get-started/quickstart/set-up-git26[guides]: https://guides.github.com/introduction/git-handbook/2728## Prerequisites2930We'll assume that you've installed Git, forked [rust-lang/rust], and cloned the31forked repo to your PC.32We'll use the command line interface to interact33with Git; there are also a number of GUIs and IDE integrations that can34generally do the same things.3536[rust-lang/rust]: https://github.com/rust-lang/rust3738If you've cloned your fork, then you will be able to reference it with `origin` in your local repo.39It may be helpful to also set up a remote for the official rust-lang/rust repo via4041```console42git remote add upstream https://github.com/rust-lang/rust.git43```4445if you're using HTTPS, or4647```console48git remote add upstream git@github.com:rust-lang/rust.git49```5051if you're using SSH.5253**NOTE:** This page is dedicated to workflows for `rust-lang/rust`, but will likely be54useful when contributing to other repositories in the Rust project.555657## Standard Process5859Below is the normal procedure that you're likely to use for most minor changes and PRs:6061 1. Ensure that you're making your changes on top of `main`: `git checkout main`.62 2. Get the latest changes from the Rust repo: `git pull upstream main --ff-only`.63 (see [No-Merge Policy][no-merge-policy] for more info about this).64 3. Make a new branch for your change: `git checkout -b issue-12345-fix`.65 4. Make some changes to the repo and test them.66 5. Stage your changes via `git add src/changed/file.rs src/another/change.rs`67 and then commit them with `git commit`.68 Of course, making intermediate commits may be a good idea as well.69 Avoid `git add .`, as it makes it too easy to70 unintentionally commit changes that should not be committed, such as submodule updates.71 You can use `git status` to check if there are any files you forgot to stage.72 6. Push your changes to your fork: `git push --set-upstream origin issue-12345-fix`73 (After adding commits, you can use `git push` and after rebasing or74pulling-and-rebasing, you can use `git push --force-with-lease`).75 7. [Open a PR][ghpullrequest] from your fork to `rust-lang/rust`'s `main` branch.7677[ghpullrequest]: https://guides.github.com/activities/forking/#making-a-pull-request7879If you end up needing to rebase and are hitting conflicts, see [Rebasing](#rebasing).80If you want to track upstream while working on long-running feature/issue, see81[Keeping things up to date][no-merge-policy].8283If your reviewer requests changes, the procedure for those changes looks much84the same, with some steps skipped:8586 1. Ensure that you're making changes to the most recent version of your code:87 `git checkout issue-12345-fix`.88 2. Make, stage, and commit your additional changes just like before.89 3. Push those changes to your fork: `git push`.9091 [no-merge-policy]: #keeping-things-up-to-date9293## Troubleshooting git issues9495You don't need to clone `rust-lang/rust` from scratch if it's out of date!96Even if you think you've messed it up beyond repair, there are ways to fix97the git state that don't require downloading the whole repository again.98Here are some common issues you might run into:99100### I made a merge commit by accident.101102Git has two ways to update your branch with the newest changes: merging and rebasing.103Rust [uses rebasing][no-merge-policy].104If you make a merge commit, it's not too hard to fix: `git rebase -i upstream/main`.105106See [Rebasing](#rebasing) for more about rebasing.107108### I deleted my fork on GitHub!109110This is not a problem from git's perspective.111If you run `git remote -v`,112it will say something like this:113114```console115$ git remote -v116origin  git@github.com:jyn514/rust.git (fetch)117origin  git@github.com:jyn514/rust.git (push)118upstream        https://github.com/rust-lang/rust (fetch)119upstream        https://github.com/rust-lang/rust (fetch)120```121122If you renamed your fork, you can change the URL like this:123124```console125git remote set-url origin <URL>126```127128where the `<URL>` is your new fork.129130### I changed a submodule by accident131132Usually people notice this when rustbot posts a comment on github that `cargo` has been modified:133134![rustbot submodule comment](./img/rustbot-submodules.png)135136You might also notice conflicts in the web UI:137138![conflict in src/tools/cargo](./img/submodule-conflicts.png)139140The most common cause is that you rebased after a change and ran `git add .` without first running141`x` to update the submodules.142 Alternatively, you might have run `cargo fmt` instead of `x fmt`143and modified files in a submodule, then committed the changes.144145To fix it, do the following things (if you changed a submodule other than cargo,146replace `src/tools/cargo` with the path to that submodule):1471481. See which commit has the accidental changes: `git log --stat -n1 src/tools/cargo`1492. Revert the changes to that commit: `git checkout <my-commit>~ src/tools/cargo`.150   Type `~` literally but replace `<my-commit>` with the output from step 1.1513. Tell git to commit the changes: `git commit --fixup <my-commit>`1524. Repeat steps 1-3 for all the submodules you modified.153    - If you modified the submodule in several different commits, you will need to repeat steps 1-3154    for each commit you modified.155    You'll know when to stop when the `git log` command shows a commit156    that's not authored by you.1575. Squash your changes into the existing commits: `git rebase --autosquash -i upstream/main`1586. [Push your changes](#standard-process).159160### I see "error: cannot rebase" when I try to rebase161162These are two common errors to see when rebasing:163```console164error: cannot rebase: Your index contains uncommitted changes.165error: Please commit or stash them.166```167```console168error: cannot rebase: You have unstaged changes.169error: Please commit or stash them.170```171172(See <https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F#_the_three_states> for the difference between the two.)173174This means you have made changes since the last time you made a commit.175To be able to rebase, either176commit your changes, or make a temporary commit called a "stash" to have them still not be committed177when you finish rebasing.178You may want to configure git to make this "stash" automatically, which179will prevent the "cannot rebase" error in nearly all cases:180181```console182git config --global rebase.autostash true183```184185See <https://git-scm.com/book/en/v2/Git-Tools-Stashing-and-Cleaning> for more info about stashing.186187### I see 'Untracked Files: src/stdarch'?188189This is left over from the move to the `library/` directory.190Unfortunately, `git rebase` does not follow renames for submodules, so you191have to delete the directory yourself:192193```console194rm -r src/stdarch195```196197### I see `<<< HEAD`?198199You were probably in the middle of a rebase or merge conflict.200See [Conflicts](#rebasing-and-conflicts) for how to fix the conflict.201If you don't care about the changes202and just want to get a clean copy of the repository back, you can use `git reset`:203204```console205# WARNING: this throws out any local changes you've made! Consider resolving the conflicts instead.206git reset --hard main207```208209### failed to push some refs210211`git push` will not work properly and say something like this:212213```console214 ! [rejected]        issue-xxxxx -> issue-xxxxx (non-fast-forward)215error: failed to push some refs to 'https://github.com/username/rust.git'216hint: Updates were rejected because the tip of your current branch is behind217hint: its remote counterpart. Integrate the remote changes (e.g.218hint: 'git pull ...') before pushing again.219hint: See the 'Note about fast-forwards' in 'git push --help' for details.220```221222The advice this gives is incorrect!223Because of Rust's ["no-merge" policy](#no-merge-policy), the merge commit created by `git pull`224will not be allowed in the final PR, in addition to defeating the point of the rebase!225Use `git push --force-with-lease` instead.226227### Git is trying to rebase commits I didn't write?228229If you see many commits in your rebase list, or merge commits, or commits by other people that you230didn't write, it likely means you're trying to rebase over the wrong branch.231For example, you may232have a `rust-lang/rust` remote `upstream`, but ran `git rebase origin/main` instead of `git rebase233upstream/main`.234The fix is to abort the rebase and use the correct branch instead:235236```console237git rebase --abort238git rebase --interactive upstream/main239```240241<details><summary>Click here to see an example of rebasing over the wrong branch</summary>242243![Interactive rebase over the wrong branch](img/other-peoples-commits.png)244245</details>246247### Quick note about submodules248249When updating your local repository with `git pull`, you may notice that sometimes250Git says you have modified some files that you have never edited.251For example,252running `git status` gives you something like (note the `new commits` mention):253254```console255On branch main256Your branch is up to date with 'origin/main'.257258Changes not staged for commit:259  (use "git add <file>..." to update what will be committed)260  (use "git restore <file>..." to discard changes in working directory)261	modified:   src/llvm-project (new commits)262	modified:   src/tools/cargo (new commits)263264no changes added to commit (use "git add" and/or "git commit -a")265```266267These changes are not changes to files: they are changes to submodules (more on this [later](#git-submodules)).268To get rid of those:269270```console271git submodule update272```273274Some submodules are not actually needed; for example, `src/llvm-project` doesn't need to be checked275out if you're using `download-ci-llvm`.276 To avoid having to keep fetching its history, you can use277`git submodule deinit -f src/llvm-project`, which will also avoid it showing as modified again.278279## Rebasing and Conflicts280281When you edit your code locally, you are making changes to the version of282rust-lang/rust that existed when you created your feature branch.283As such, when you submit your PR, it is possible that some of the changes that have been made284to rust-lang/rust since then are in conflict with the changes you've made.285When this happens, you need to resolve the conflicts before your changes can be merged.286To do that, you need to rebase your work on top of rust-lang/rust.287288### Rebasing289290To rebase your feature branch on top of the newest version of the `main` branch291of rust-lang/rust, checkout your branch, and then run this command:292293```console294git pull --rebase https://github.com/rust-lang/rust.git main295```296297> If you are met with the following error:298> ```console299> error: cannot pull with rebase: Your index contains uncommitted changes.300> error: please commit or stash them.301> ```302> it means that you have some uncommitted work in your working tree. In that303> case, run `git stash` before rebasing, and then `git stash pop` after you304> have rebased and fixed all conflicts.305306When you rebase a branch on main, all the changes on your branch are307reapplied to the most recent version of `main`.308In other words, Git tries to309pretend that the changes you made to the old version of `main` were instead310made to the new version of `main`.311During this process, you should expect to encounter at least one "rebase conflict".312This happens when Git's attempt to313reapply the changes fails because your changes conflicted with other changes that have been made.314You can tell that this happened because you'll see lines in the output that look like315316```console317CONFLICT (content): Merge conflict in file.rs318```319320When you open these files, you'll see sections of the form321322```console323<<<<<<< HEAD324Original code325=======326Your code327>>>>>>> 8fbf656... Commit fixes 12345328```329330This represents the lines in the file that Git could not figure out how to rebase.331The section between `<<<<<<< HEAD` and `=======` has the code from332`main`, while the other side has your version of the code.333You'll need to decide how to deal with the conflict.334You may want to keep your changes,335keep the changes on `main`, or combine the two.336337Generally, resolving the conflict consists of two steps: First, fix the particular conflict.338Edit the file to make the changes you want and remove the339`<<<<<<<`, `=======` and `>>>>>>>` lines in the process.340Second, check the surrounding code.341If there was a conflict, its likely there are some logical errors lying around too!342It's a good idea to run `x check` here to make sure there are no glaring errors.343344Once you're all done fixing the conflicts, you need to stage the files that had345conflicts in them via `git add`.346Afterwards, run `git rebase --continue` to let347Git know that you've resolved the conflicts and it should finish the rebase.348349Once the rebase has succeeded, you'll want to update the associated branch on350your fork with `git push --force-with-lease`.351352### Keeping things up to date353354The [above section](#rebasing) is a specific355guide on rebasing work and dealing with merge conflicts.356Here is some general advice about how to keep your local repo up-to-date with upstream changes:357358Using `git pull upstream main` while on your local `main` branch regularly will keep it up-to-date.359You will also want to keep your feature branches up-to-date as well.360After pulling, you can checkout the feature branches and rebase them:361362```console363git checkout main364git pull upstream main --ff-only # to make certain there are no merge commits365git rebase main feature_branch366git push --force-with-lease # (set origin to be the same as local)367```368369To avoid merges as per the [No-Merge Policy](#no-merge-policy), you may want to use370`git config pull.ff only` (this will apply the config only to the local repo)371to ensure that Git doesn't create merge commits when `git pull`ing, without372needing to pass `--ff-only` or `--rebase` every time.373374You can also `git push --force-with-lease` from main to double-check that your375feature branches are in sync with their state on the GitHub side.376377## Advanced Rebasing378379### Squash your commits380381"Squashing" commits into each other causes them to be merged into a single commit.382Both the upside and downside of this is that it simplifies the history.383On the one hand, you lose track of the steps in which changes were made, but384the history becomes easier to work with.385386The easiest way to squash your commits in a PR on the `rust-lang/rust` repository is to use the `@bors squash` command in a comment on the PR.387By default, [bors] combines all commit messages of the PR into the squashed commit message.388To customize the commit message, use `@bors squash msg="<commit message>"`.389For example, `@bors squash msg="Improve diagnostics for missing lifetime parameter"`.390391If you want to squash commits using local git operations, read on below.392393If there are no conflicts and you are just squashing to clean up the history,394use `git rebase --interactive --keep-base main`.395This keeps the fork point of your PR the same, making it easier to review the diff of what happened396across your rebases.397398Squashing can also be useful as part of conflict resolution.399If your branch contains multiple consecutive rewrites of the same code, or if400the rebase conflicts are extremely severe, you can use401`git rebase --interactive main` to gain more control over the process.402This allows you to choose to skip commits, edit the commits that you do not skip,403change the order in which they are applied, or "squash" them into each other.404405Alternatively, you can sacrifice the commit history like this:406407```console408# squash all the changes into one commit so you only have to worry about conflicts once409git rebase --interactive --keep-base main  # and squash all changes along the way410git rebase main411# fix all merge conflicts412git rebase --continue413```414415You also may want to squash just the last few commits together, possibly416because they only represent "fixups" and not real changes.417For example,418`git rebase --interactive HEAD~2` will allow you to edit the two commits only.419420[bors]: https://github.com/rust-lang/bors421422### `git range-diff`423424After completing a rebase, and before pushing up your changes, you may want to425review the changes between your old branch and your new one.426You can do that with `git range-diff main @{upstream} HEAD`.427428The first argument to `range-diff`, `main` in this case, is the base revision429that you're comparing your old and new branch against.430The second argument is431the old version of your branch; in this case, `@upstream` means the version that432you've pushed to GitHub, which is the same as what people will see in your pull request.433Finally, the third argument to `range-diff` is the *new* version of434your branch; in this case, it is `HEAD`, which is the commit that is currently435checked-out in your local repo.436437Note that you can also use the equivalent, abbreviated form `git range-diff main @{u} HEAD`.438439Unlike in regular Git diffs, you'll see a `-` or `+` next to another `-` or `+`440in the range-diff output.441The marker on the left indicates a change between the442old branch and the new branch, and the marker on the right indicates a change you've committed.443So, you can think of a range-diff as a "diff of diffs" since444it shows you the differences between your old diff and your new diff.445446Here's an example of `git range-diff` output (taken from [Git's docs][range-diff-example-docs]):447448```console449-:  ------- > 1:  0ddba11 Prepare for the inevitable!4501:  c0debee = 2:  cab005e Add a helpful message at the start4512:  f00dbal ! 3:  decafe1 Describe a bug452    @@ -1,3 +1,3 @@453     Author: A U Thor <author@example.com>454455    -TODO: Describe a bug456    +Describe a bug457    @@ -324,5 +324,6458      This is expected.459460    -+What is unexpected is that it will also crash.461    ++Unexpectedly, it also crashes. This is a bug, and the jury is462    ++still out there how to fix it best. See ticket #314 for details.463464      Contact4653:  bedead < -:  ------- TO-UNDO466```467468(Note that `git range-diff` output in your terminal will probably be easier to469read than in this example because it will have colors.)470471Another feature of `git range-diff` is that, unlike `git diff`, it will also diff commit messages.472This feature can be useful when amending several commit473messages so you can make sure you changed the right parts.474475`git range-diff` is a very useful command, but note that it can take some time476to get used to its output format.477You may also find Git's documentation on the478command useful, especially their ["Examples" section][range-diff-example-docs].479480[range-diff-example-docs]: https://git-scm.com/docs/git-range-diff#_examples481482## No-Merge Policy483484The rust-lang/rust repo uses what is known as a "rebase workflow".485This means that merge commits in PRs are not accepted.486As a result, if you are running487`git merge` locally, chances are good that you should be rebasing instead.488Of course, this is not always true; if your merge will just be a fast-forward,489like the merges that `git pull` usually performs, then no merge commit is490created and you have nothing to worry about.491Running `git config merge.ff only` (this will apply the config to the local repo)492once will ensure that all the merges you perform are of this type, so that you493cannot make a mistake.494495There are a number of reasons for this decision, and like all others, it is a tradeoff.496The main advantage is the generally linear commit history.497This greatly simplifies bisecting and makes the history and commit log much easier498to follow and understand.499500## Tips for reviewing501502**NOTE**: This section is for *reviewing* PRs, not authoring them.503504### Hiding whitespace505506GitHub has a button for disabling whitespace changes that may be useful.507You can also use `git diff -w origin/main` to view changes locally.508509![hide whitespace](./img/github-whitespace-changes.png)510511### Fetching PRs512513To checkout PRs locally, you can use `git fetch upstream pull/NNNNN/head && git checkout514FETCH_HEAD`.515516You can also use github's cli tool.517GitHub shows a button on PRs where you can copy-paste the command to check it out locally.518See <https://cli.github.com/> for more info.519520![`gh` suggestion](./img/github-cli.png)521522### Using GitHub dev523524As an alternative to the GitHub web UI, GitHub Dev provides a web-based editor for browsing525repository and PRs.526It can be opened by replacing `github.com` with `github.dev` in the URL527or by pressing `.` on a GitHub page.528See [the docs for github.dev editor](https://docs.github.com/en/codespaces/the-githubdev-web-based-editor)529for more details.530531### Moving large sections of code532533Git and GitHub's default diff view for large moves *within* a file is quite poor; it will show each534line as deleted and each line as added, forcing you to compare each line yourself.535Git has an option to show moved lines in a different color:536537```console538git log -p --color-moved=dimmed-zebra --color-moved-ws=allow-indentation-change539```540541See [the docs for `--color-moved`](https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---color-movedltmodegt) for more info.542543### range-diff544545See [the relevant section for PR authors](#git-range-diff).546This can be useful for comparing code547that was force-pushed to make sure there are no unexpected changes.548549### Ignoring changes to specific files550551Many large files in the repo are autogenerated.552To view a diff that ignores changes to those files,553you can use the following syntax (e.g. Cargo.lock):554555```console556git log -p ':!Cargo.lock'557```558559Arbitrary patterns are supported (e.g. `:!compiler/*`). Patterns use the same syntax as560`.gitignore`, with `:` prepended to indicate a pattern.561562## Git submodules563564**NOTE**: submodules are a nice thing to know about, but it *isn't* an absolute565prerequisite to contribute to `rustc`.566If you are using Git for the first time,567you might want to get used to the main concepts of Git before reading this section.568569The `rust-lang/rust` repository uses [Git submodules] as a way to use other570Rust projects from within the `rust` repo.571Examples include Rust's fork of572`llvm-project`, `cargo`, and libraries like `stdarch` and `backtrace`.573574Those projects are developed and maintained in a separate Git (and GitHub)575repository, and they have their own Git history/commits, issue tracker and PRs.576Submodules allow us to create some sort of embedded sub-repository inside the577`rust` repository and use them like they were directories in the `rust` repository.578579Take `llvm-project` for example.580`llvm-project` is maintained in the [`rust-lang/llvm-project`]581repository, but it is used in `rust-lang/rust` by the compiler for code generation and optimization.582We bring it in `rust` as a submodule, in the `src/llvm-project` folder.583584The contents of submodules are ignored by Git: submodules are in some sense isolated585from the rest of the repository.586However, if you try to `cd src/llvm-project` and then run `git status`:587588```console589HEAD detached at 9567f08afc943590nothing to commit, working tree clean591```592593As far as git is concerned, you are no longer in the `rust` repo, but in the `llvm-project` repo.594You will notice that we are in "detached HEAD" state, i.e. not on a branch but on a595particular commit.596597This is because, like any dependency, we want to be able to control which version to use.598Submodules allow us to do just that: every submodule is "pinned" to a certain599commit, which doesn't change unless modified manually.600If you use `git checkout <commit>`601in the `llvm-project` directory and go back to the `rust` directory, you can stage this602change like any other, e.g. by running `git add src/llvm-project`. (Note that if603you *don't* stage the change to commit, then you run the risk that running604`x` will just undo your change by switching back to the previous commit when605it automatically "updates" the submodules.)606607This version selection is usually done by the maintainers of the project, and608looks like [this][llvm-update].609610Git submodules take some time to get used to, so don't worry if it isn't perfectly clear yet.611You will rarely have to use them directly and, again, you don't need612to know everything about submodules to contribute to Rust.613Just know that they exist and that they correspond to some sort of embedded subrepository dependency614that Git can nicely and fairly conveniently handle for us.615616### Hard-resetting submodules617618Sometimes you might run into (when you run `git status`)619620```console621Changes not staged for commit:622  (use "git add <file>..." to update what will be committed)623  (use "git restore <file>..." to discard changes in working directory)624  (commit or discard the untracked or modified content in submodules)625        modified:   src/llvm-project (new commits, modified content)626```627628and when you try to run `git submodule update` it breaks horribly with errors like629630```console631error: RPC failed; curl 92 HTTP/2 stream 7 was not closed cleanly: CANCEL (err 8)632error: 2782 bytes of body are still expected633fetch-pack: unexpected disconnect while reading sideband packet634fatal: early EOF635fatal: fetch-pack: invalid index-pack output636fatal: Fetched in submodule path 'src/llvm-project', but it did not contain 5a5152f653959d14d68613a3a8a033fb65eec021. Direct fetching of that commit failed.637```638639If you see `(new commits, modified content)` you can run640641```console642git submodule foreach git reset --hard643```644645and then try `git submodule update` again.646647### Deinit git submodules648649If that doesn't work, you can try to deinit all git submodules...650651```console652git submodule deinit -f --all653```654655Unfortunately sometimes your local git submodules configuration can become656completely messed up for some reason.657658### Overcoming `fatal: not a git repository: <submodule>/../../.git/modules/<submodule>`659660Sometimes, for some forsaken reason, you might run into661662```console663fatal: not a git repository: src/gcc/../../.git/modules/src/gcc664```665666In this situation, for the given submodule path, i.e. `<submodule_path> =667src/gcc` in this example, you need to:6686691. `rm -rf <submodule_path>/.git`6702. `rm -rf .git/modules/<submodule_path>/config`6713. `rm -rf .gitconfig.lock` if somehow the `.gitconfig` lock is orphaned.672673Then do something like `./x fmt` to have bootstrap manage the submodule checkouts for you.674675## Ignoring commits during `git blame`676677Some commits contain large reformatting changes that don't otherwise change functionality.678They can be instructed to be ignored by `git blame` through679[`.git-blame-ignore-revs`](https://github.com/rust-lang/rust/blob/HEAD/.git-blame-ignore-revs):6806811. Configure `git blame` to use `.git-blame-ignore-revs` as the list of commits to ignore: `git682   config blame.ignorerevsfile .git-blame-ignore-revs`6832. Add suitable commits that you wish to be ignored by `git blame`.684685Please include a comment for the commit that you add to `.git-blame-ignore-revs` so people can686easily figure out *why* a commit is ignored.687688[Git submodules]: https://git-scm.com/book/en/v2/Git-Tools-Submodules689[`rust-lang/llvm-project`]: https://github.com/rust-lang/llvm-project690[llvm-update]: https://github.com/rust-lang/rust/pull/99464/files

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.