Movatterモバイル変換


[0]ホーム

URL:


Keyboard shortcuts

Press or to navigate between chapters

PressS or/ to search in the book

Press? to show this help

PressEsc to hide this help

Rust Compiler Development Guide

    Using Git

    The Rust project usesGit to manage its source code. In order tocontribute, you'll need some familiarity with its features so that your changescan be incorporated into the compiler.

    The goal of this page is to cover some of the more common questions andproblems new contributors face. Although some Git basics will be covered here,if you find that this is still a little too fast for you, it might make senseto first read some introductions to Git, such as the Beginner and Gettingstarted sections ofthis tutorial from Atlassian. GitHub alsoprovidesdocumentation andguides for beginners, or you can consult themore in depthbook from Git.

    This guide is incomplete. If you run into trouble with git that this page doesn't help with,pleaseopen an issue so we can document how to fix it.

    Prerequisites

    We'll assume that you've installed Git, forkedrust-lang/rust, and cloned theforked repo to your PC. We'll use the command line interface to interactwith Git; there are also a number of GUIs and IDE integrations that cangenerally do the same things.

    If you've cloned your fork, then you will be able to reference it withoriginin your local repo. It may be helpful to also set up a remote for the officialrust-lang/rust repo via

    git remote add upstream https://github.com/rust-lang/rust.git

    if you're using HTTPS, or

    git remote add upstream git@github.com:rust-lang/rust.git

    if you're using SSH.

    NOTE: This page is dedicated to workflows forrust-lang/rust, but will likely beuseful when contributing to other repositories in the Rust project.

    Standard Process

    Below is the normal procedure that you're likely to use for most minor changesand PRs:

    1. Ensure that you're making your changes on top of master:git checkout master.
    2. Get the latest changes from the Rust repo:git pull upstream master --ff-only.(seeNo-Merge Policy for more info about this).
    3. Make a new branch for your change:git checkout -b issue-12345-fix.
    4. Make some changes to the repo and test them.
    5. Stage your changes viagit add src/changed/file.rs src/another/change.rsand then commit them withgit commit. Of course, making intermediate commitsmay be a good idea as well. Avoidgit add ., as it makes it too easy tounintentionally commit changes that should not be committed, such as submoduleupdates. You can usegit status to check if there are any files you forgotto stage.
    6. Push your changes to your fork:git push --set-upstream origin issue-12345-fix(After adding commits, you can usegit push and after rebasing orpulling-and-rebasing, you can usegit push --force-with-lease).
    7. Open a PR from your fork torust-lang/rust's master branch.

    If you end up needing to rebase and are hitting conflicts, seeRebasing.If you want to track upstream while working on long-running feature/issue, seeKeeping things up to date.

    If your reviewer requests changes, the procedure for those changes looks muchthe same, with some steps skipped:

    1. Ensure that you're making changes to the most recent version of your code:git checkout issue-12345-fix.
    2. Make, stage, and commit your additional changes just like before.
    3. Push those changes to your fork:git push.

    Troubleshooting git issues

    You don't need to clonerust-lang/rust from scratch if it's out of date!Even if you think you've messed it up beyond repair, there are ways to fixthe git state that don't require downloading the whole repository again.Here are some common issues you might run into:

    I made a merge commit by accident.

    Git has two ways to update your branch with the newest changes: merging and rebasing.Rustuses rebasing. If you make a merge commit, it's not too hard to fix:git rebase -i upstream/master.

    SeeRebasing for more about rebasing.

    I deleted my fork on GitHub!

    This is not a problem from git's perspective. If you rungit remote -v,it will say something like this:

    $ git remote -vorigin  git@github.com:jyn514/rust.git (fetch)origin  git@github.com:jyn514/rust.git (push)upstream        https://github.com/rust-lang/rust (fetch)upstream        https://github.com/rust-lang/rust (fetch)

    If you renamed your fork, you can change the URL like this:

    git remote set-url origin <URL>

    where the<URL> is your new fork.

    I changed a submodule by accident

    Usually people notice this when rustbot posts a comment on github thatcargo has been modified:

    rustbot submodule comment

    You might also notice conflicts in the web UI:

    conflict in src/tools/cargo

    The most common cause is that you rebased after a change and rangit add . without first runningx to update the submodules. Alternatively, you might have runcargo fmt instead ofx fmtand modified files in a submodule, then committed the changes.

    To fix it, do the following things (if you changed a submodule other than cargo,replacesrc/tools/cargo with the path to that submodule):

    1. See which commit has the accidental changes:git log --stat -n1 src/tools/cargo
    2. Revert the changes to that commit:git checkout <my-commit>~ src/tools/cargo. Type~literally but replace<my-commit> with the output from step 1.
    3. Tell git to commit the changes:git commit --fixup <my-commit>
    4. Repeat steps 1-3 for all the submodules you modified.
      • If you modified the submodule in several different commits, you will need to repeat steps 1-3for each commit you modified. You'll know when to stop when thegit log command shows a committhat's not authored by you.
    5. Squash your changes into the existing commits:git rebase --autosquash -i upstream/master
    6. Push your changes.

    I see "error: cannot rebase" when I try to rebase

    These are two common errors to see when rebasing:

    error: cannot rebase: Your index contains uncommitted changes.error: Please commit or stash them.
    error: cannot rebase: You have unstaged changes.error: Please commit or stash them.

    (Seehttps://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F#_the_three_states for the difference between the two.)

    This means you have made changes since the last time you made a commit. To be able to rebase, eithercommit your changes, or make a temporary commit called a "stash" to have them still not be committedwhen you finish rebasing. You may want to configure git to make this "stash" automatically, whichwill prevent the "cannot rebase" error in nearly all cases:

    git config --global rebase.autostash true

    Seehttps://git-scm.com/book/en/v2/Git-Tools-Stashing-and-Cleaning for more info about stashing.

    I see 'Untracked Files: src/stdarch'?

    This is left over from the move to thelibrary/ directory.Unfortunately,git rebase does not follow renames for submodules, so youhave to delete the directory yourself:

    rm -r src/stdarch

    I see<<< HEAD?

    You were probably in the middle of a rebase or merge conflict. SeeConflicts for how to fix the conflict. If you don't care about the changesand just want to get a clean copy of the repository back, you can usegit reset:

    # WARNING: this throws out any local changes you've made! Consider resolving the conflicts instead.git reset --hard master

    failed to push some refs

    git push will not work properly and say something like this:

     ! [rejected]        issue-xxxxx -> issue-xxxxx (non-fast-forward)error: failed to push some refs to 'https://github.com/username/rust.git'hint: Updates were rejected because the tip of your current branch is behindhint: its remote counterpart. Integrate the remote changes (e.g.hint: 'git pull ...') before pushing again.hint: See the 'Note about fast-forwards' in 'git push --help' for details.

    The advice this gives is incorrect! Because of Rust's"no-merge" policy the merge commit created bygit pullwill not be allowed in the final PR, in addition to defeating the point of therebase! Usegit push --force-with-lease instead.

    Git is trying to rebase commits I didn't write?

    If you see many commits in your rebase list, or merge commits, or commits by other people that youdidn't write, it likely means you're trying to rebase over the wrong branch. For example, you mayhave arust-lang/rust remoteupstream, but rangit rebase origin/master instead ofgit rebase upstream/master. The fix is to abort the rebase and use the correct branch instead:

    git rebase --abortgit rebase -i upstream/master
    Click here to see an example of rebasing over the wrong branch

    Interactive rebase over the wrong branch

    Quick note about submodules

    When updating your local repository withgit pull, you may notice that sometimesGit says you have modified some files that you have never edited. For example,runninggit status gives you something like (note thenew commits mention):

    On branch masterYour branch is up to date with 'origin/master'.Changes not staged for commit:  (use "git add <file>..." to update what will be committed)  (use "git restore <file>..." to discard changes in working directory)modified:   src/llvm-project (new commits)modified:   src/tools/cargo (new commits)no changes added to commit (use "git add" and/or "git commit -a")

    These changes are not changes to files: they are changes to submodules (more on thislater).To get rid of those:

    git submodule update

    Some submodules are not actually needed; for example,src/llvm-project doesn't need to be checkedout if you're usingdownload-ci-llvm. To avoid having to keep fetching its history, you can usegit submodule deinit -f src/llvm-project, which will also avoid it showing as modified again.

    Rebasing and Conflicts

    When you edit your code locally, you are making changes to the version ofrust-lang/rust that existed when you created your feature branch. As such, whenyou submit your PR it is possible that some of the changes that have been madeto rust-lang/rust since then are in conflict with the changes you've made.When this happens, you need to resolve the conflicts before your changes can bemerged. To do that, you need to rebase your work on top of rust-lang/rust.

    Rebasing

    To rebase your feature branch on top of the newest version of the master branchof rust-lang/rust, checkout your branch, and then run this command:

    git pull --rebase https://github.com/rust-lang/rust.git master

    If you are met with the following error:

    error: cannot pull with rebase: Your index contains uncommitted changes.error: please commit or stash them.

    it means that you have some uncommitted work in your working tree. In thatcase, rungit stash before rebasing, and thengit stash pop after youhave rebased and fixed all conflicts.

    When you rebase a branch on master, all the changes on your branch arereapplied to the most recent version of master. In other words, Git tries topretend that the changes you made to the old version of master were insteadmade to the new version of master. During this process, you should expect toencounter at least one "rebase conflict." This happens when Git's attempt toreapply the changes fails because your changes conflicted with other changesthat have been made. You can tell that this happened because you'll seelines in the output that look like

    CONFLICT (content): Merge conflict in file.rs

    When you open these files, you'll see sections of the form

    <<<<<<< HEADOriginal code=======Your code>>>>>>> 8fbf656... Commit fixes 12345

    This represents the lines in the file that Git could not figure out how torebase. The section between<<<<<<< HEAD and======= has the code frommaster, while the other side has your version of the code. You'll need todecide how to deal with the conflict. You may want to keep your changes,keep the changes on master, or combine the two.

    Generally, resolving the conflict consists of two steps: First, fix theparticular conflict. Edit the file to make the changes you want and remove the<<<<<<<,======= and>>>>>>> lines in the process. Second, check thesurrounding code. If there was a conflict, its likely there are some logicalerrors lying around too! It's a good idea to runx check here to make surethere are no glaring errors.

    Once you're all done fixing the conflicts, you need to stage the files that hadconflicts in them viagit add. Afterwards, rungit rebase --continue to letGit know that you've resolved the conflicts and it should finish the rebase.

    Once the rebase has succeeded, you'll want to update the associated branch onyour fork withgit push --force-with-lease.

    Keeping things up to date

    Theabove section is a specificguide on rebasing work and dealing with merge conflicts.Here is some general advice about how to keep your local repoup-to-date with upstream changes:

    Usinggit pull upstream master while on your local master branch regularlywill keep it up-to-date. You will also want to keep your feature branchesup-to-date as well. After pulling, you can checkout the feature branchesand rebase them:

    git checkout mastergit pull upstream master --ff-only # to make certain there are no merge commitsgit rebase master feature_branchgit push --force-with-lease # (set origin to be the same as local)

    To avoid merges as per theNo-Merge Policy, you may want to usegit config pull.ff only (this will apply the config only to the local repo)to ensure that Git doesn't create merge commits whengit pulling, withoutneeding to pass--ff-only or--rebase every time.

    You can alsogit push --force-with-lease from master to double-check that yourfeature branches are in sync with their state on the Github side.

    Advanced Rebasing

    Squash your commits

    "Squashing" commits into each other causes them to be merged into a singlecommit. Both the upside and downside of this is that it simplifies the history.On the one hand, you lose track of the steps in which changes were made, butthe history becomes easier to work with.

    If there are no conflicts and you are just squashing to clean up the history,usegit rebase --interactive --keep-base master. This keeps the fork pointof your PR the same, making it easier to review the diff of what happenedacross your rebases.

    Squashing can also be useful as part of conflict resolution.If your branch contains multiple consecutive rewrites of the same code, or ifthe rebase conflicts are extremely severe, you can usegit rebase --interactive master to gain more control over the process. Thisallows you to choose to skip commits, edit the commits that you do not skip,change the order in which they are applied, or "squash" them into each other.

    Alternatively, you can sacrifice the commit history like this:

    # squash all the changes into one commit so you only have to worry about conflicts oncegit rebase -i --keep-base master  # and squash all changes along the waygit rebase master# fix all merge conflictsgit rebase --continue

    You also may want to squash just the last few commits together, possiblybecause they only represent "fixups" and not real changes. For example,git rebase --interactive HEAD~2 will allow you to edit the two commits only.

    git range-diff

    After completing a rebase, and before pushing up your changes, you may want toreview the changes between your old branch and your new one. You can do thatwithgit range-diff master @{upstream} HEAD.

    The first argument torange-diff,master in this case, is the base revisionthat you're comparing your old and new branch against. The second argument isthe old version of your branch; in this case,@upstream means the version thatyou've pushed to GitHub, which is the same as what people will see in your pullrequest. Finally, the third argument torange-diff is thenew version ofyour branch; in this case, it isHEAD, which is the commit that is currentlychecked-out in your local repo.

    Note that you can also use the equivalent, abbreviated formgit range-diff master @{u} HEAD.

    Unlike in regular Git diffs, you'll see a- or+ next to another- or+in the range-diff output. The marker on the left indicates a change between theold branch and the new branch, and the marker on the right indicates a changeyou've committed. So, you can think of a range-diff as a "diff of diffs" sinceit shows you the differences between your old diff and your new diff.

    Here's an example ofgit range-diff output (taken fromGit'sdocs):

    -:  ------- > 1:  0ddba11 Prepare for the inevitable!1:  c0debee = 2:  cab005e Add a helpful message at the start2:  f00dbal ! 3:  decafe1 Describe a bug    @@ -1,3 +1,3 @@     Author: A U Thor <author@example.com>    -TODO: Describe a bug    +Describe a bug    @@ -324,5 +324,6      This is expected.    -+What is unexpected is that it will also crash.    ++Unexpectedly, it also crashes. This is a bug, and the jury is    ++still out there how to fix it best. See ticket #314 for details.      Contact3:  bedead < -:  ------- TO-UNDO

    (Note thatgit range-diff output in your terminal will probably be easier toread than in this example because it will have colors.)

    Another feature ofgit range-diff is that, unlikegit diff, it will alsodiff commit messages. This feature can be useful when amending several commitmessages so you can make sure you changed the right parts.

    git range-diff is a very useful command, but note that it can take some timeto get used to its output format. You may also find Git's documentation on thecommand useful, especially their"Examples" section.

    No-Merge Policy

    The rust-lang/rust repo uses what is known as a "rebase workflow." This meansthat merge commits in PRs are not accepted. As a result, if you are runninggit merge locally, chances are good that you should be rebasing instead. Ofcourse, this is not always true; if your merge will just be a fast-forward,like the merges thatgit pull usually performs, then no merge commit iscreated and you have nothing to worry about. Runninggit config merge.ff only(this will apply the config to the local repo)once will ensure that all the merges you perform are of this type, so that youcannot make a mistake.

    There are a number of reasons for this decision and like all others, it is atradeoff. The main advantage is the generally linear commit history. Thisgreatly simplifies bisecting and makes the history and commit log much easierto follow and understand.

    Tips for reviewing

    NOTE: This section is forreviewing PRs, not authoring them.

    Hiding whitespace

    Github has a button for disabling whitespace changes that may be useful.You can also usegit diff -w origin/master to view changes locally.

    hide whitespace

    Fetching PRs

    To checkout PRs locally, you can usegit fetch upstream pull/NNNNN/head && git checkout FETCH_HEAD.

    You can also use github's cli tool. Github shows a button on PRs where you can copy-paste thecommand to check it out locally. Seehttps://cli.github.com/ for more info.

    gh suggestion

    Moving large sections of code

    Git and Github's default diff view for large moveswithin a file is quite poor; it will show eachline as deleted and each line as added, forcing you to compare each line yourself. Git has an optionto show moved lines in a different color:

    git log -p --color-moved=dimmed-zebra --color-moved-ws=allow-indentation-change

    Seethe docs for--color-moved for more info.

    range-diff

    Seethe relevant section for PR authors. This can be useful for comparing codethat was force-pushed to make sure there are no unexpected changes.

    Ignoring changes to specific files

    Many large files in the repo are autogenerated. To view a diff that ignores changes to those files,you can use the following syntax (e.g. Cargo.lock):

    git log -p ':!Cargo.lock'

    Arbitrary patterns are supported (e.g.:!compiler/*). Patterns use the same syntax as.gitignore, with: prepended to indicate a pattern.

    Git submodules

    NOTE: submodules are a nice thing to know about, but itisn't an absoluteprerequisite to contribute torustc. If you are using Git for the first time,you might want to get used to the main concepts of Git before reading this section.

    Therust-lang/rust repository usesGit submodules as a way to use otherRust projects from within therust repo. Examples include Rust's fork ofllvm-project,cargo and libraries likestdarch andbacktrace.

    Those projects are developed and maintained in an separate Git (and GitHub)repository, and they have their own Git history/commits, issue tracker and PRs.Submodules allow us to create some sort of embedded sub-repository inside therust repository and use them like they were directories in therust repository.

    Takellvm-project for example.llvm-project is maintained in therust-lang/llvm-projectrepository, but it is used inrust-lang/rust by the compiler for code generation andoptimization. We bring it inrust as a submodule, in thesrc/llvm-project folder.

    The contents of submodules are ignored by Git: submodules are in some sense isolatedfrom the rest of the repository. However, if you try tocd src/llvm-project and thenrungit status:

    HEAD detached at 9567f08afc943nothing to commit, working tree clean

    As far as git is concerned, you are no longer in therust repo, but in thellvm-project repo.You will notice that we are in "detached HEAD" state, i.e. not on a branch but on aparticular commit.

    This is because, like any dependency, we want to be able to control which version to use.Submodules allow us to do just that: every submodule is "pinned" to a certaincommit, which doesn't change unless modified manually. If you usegit checkout <commit>in thellvm-project directory and go back to therust directory, you can stage thischange like any other, e.g. by runninggit add src/llvm-project. (Note that ifyoudon't stage the change to commit, then you run the risk that runningx will just undo your change by switching back to the previous commit whenit automatically "updates" the submodules.)

    This version selection is usually done by the maintainers of the project, andlooks likethis.

    Git submodules take some time to get used to, so don't worry if it isn't perfectlyclear yet. You will rarely have to use them directly and, again, you don't needto know everything about submodules to contribute to Rust. Just know that theyexist and that they correspond to some sort of embedded subrepository dependencythat Git can nicely and fairly conveniently handle for us.

    Hard-resetting submodules

    Sometimes you might run into (when you rungit status)

    Changes not staged for commit:  (use "git add <file>..." to update what will be committed)  (use "git restore <file>..." to discard changes in working directory)  (commit or discard the untracked or modified content in submodules)        modified:   src/llvm-project (new commits, modified content)

    and when you try to rungit submodule update it breaks horribly with errors like

    error: RPC failed; curl 92 HTTP/2 stream 7 was not closed cleanly: CANCEL (err 8)error: 2782 bytes of body are still expectedfetch-pack: unexpected disconnect while reading sideband packetfatal: early EOFfatal: fetch-pack: invalid index-pack outputfatal: Fetched in submodule path 'src/llvm-project', but it did not contain 5a5152f653959d14d68613a3a8a033fb65eec021. Direct fetching of that commit failed.

    If you see(new commits, modified content) you can run

    git submodule foreach git reset --hard

    and then trygit submodule update again.

    Deinit git submodules

    If that doesn't work, you can try to deinit all git submodules...

    git submodule deinit -f --all

    Unfortunately sometimes your local git submodules configuration can becomecompletely messed up for some reason.

    Overcomingfatal: not a git repository: <submodule>/../../.git/modules/<submodule>

    Sometimes, for some forsaken reason, you might run into

    fatal: not a git repository: src/gcc/../../.git/modules/src/gcc

    In this situation, for the given submodule path, i.e.<submodule_path> = src/gcc in this example, you need to:

    1. rm -rf <submodule_path>/.git
    2. rm -rf .git/modules/<submodule_path>/config
    3. rm -rf .gitconfig.lock if somehow the.gitconfig lock is orphaned.

    Then do something like./x fmt to have bootstrap manage the submodulecheckouts for you.

    Ignoring commits duringgit blame

    Some commits contain large reformatting changes that don't otherwise change functionality. They canbe instructed to be ignored bygit blame through.git-blame-ignore-revs:

    1. Configuregit blame to use.git-blame-ignore-revs as the list of commits to ignore:git config blame.ignorerevsfile .git-blame-ignore-revs
    2. Add suitable commits that you wish to be ignored bygit blame.

    Please include a comment for the commit that you add to.git-blame-ignore-revs so people caneasily figure outwhy a commit is ignored.


    [8]ページ先頭

    ©2009-2025 Movatter.jp