Add a section on using git.
This section addresses the biggest issues that new contributors, especially those with limited familiarity with git, are likely to face. This is still a WIP. Thanks to jyn for the recommended improvements!
This commit is contained in:
parent
5ee9d96c07
commit
328bd35e7d
|
|
@ -33,6 +33,7 @@
|
|||
- [About the compiler team](./compiler-team.md)
|
||||
- [Mastering @rustbot](./rustbot.md)
|
||||
- [Walkthrough: a typical contribution](./walkthrough.md)
|
||||
- [Using git](./git.md)
|
||||
- [Bug Fix Procedure](./bug-fix-procedure.md)
|
||||
- [Implementing new features](./implementing_new_features.md)
|
||||
- [Stability attributes](./stability.md)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,203 @@
|
|||
# Using git
|
||||
|
||||
The Rust project uses [git] to manage its source code. In order to
|
||||
contribute, you'll need some familiarity with its features so that your changes
|
||||
can be incorporated into the compiler.
|
||||
|
||||
[git]: https://git-scm.com
|
||||
|
||||
The goal of this page is to cover some of the more common questions and
|
||||
problems new contributors face. Although some git basics will be covered here,
|
||||
if you have never used git or GitHub before you may find that this is still a
|
||||
little too fast for you. In that case, it would make sense to first read some
|
||||
introductions to git, such as the Beginner and Getting started sections of
|
||||
[this tutorial from Atlassian][atlassian-git]. GitHub also provides
|
||||
[documentation] and [guides] for beginners.
|
||||
|
||||
[atlassian-git]: https://www.atlassian.com/git/tutorials/what-is-version-control
|
||||
[documentation]: https://docs.github.com/en/github/getting-started-with-github/set-up-git
|
||||
[guides]: https://guides.github.com/introduction/git-handbook/
|
||||
|
||||
Although this page should get you to a point where you can start contributing,
|
||||
learning more git is almost certainly a good use of your time if you want to
|
||||
keep contributing. There are many tutorials online for those folks that are
|
||||
newer which combine excellently with man pages and official documentation.
|
||||
|
||||
## Prequisites
|
||||
|
||||
We'll assume that you've installed git, forked [rust-lang/rust], and cloned the
|
||||
forked repo to your PC. We'll also use the command line interface to interact
|
||||
with git; there are also a number of GUIs and IDE integrations that can
|
||||
generally do the same things.
|
||||
|
||||
[rust-lang/rust]: https://github.com/rust-lang/rust
|
||||
|
||||
If you've cloned your fork, then you will be able to reference it with `origin`
|
||||
in your local repo. It may be helpful to also set up a remote for the official
|
||||
rust-lang/rust repo via
|
||||
|
||||
```sh
|
||||
git remote add rust https://github.com/rust-lang/rust.git
|
||||
```
|
||||
|
||||
if you're using HTTPS, or
|
||||
|
||||
```sh
|
||||
git remote add rust git@github.com:rust-lang/rust.git
|
||||
```
|
||||
|
||||
if you're using SSH.
|
||||
|
||||
## Standard Process
|
||||
|
||||
Below is the normal procedure that you're likely to use for most minor changes
|
||||
and 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 rust master`.
|
||||
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 via `git add src/changed/file.rs src/another/change.rs`
|
||||
and then commit them with `git commit`. Of course, making intermediate commits
|
||||
may be a good idea as well. Avoid `git add .`, as it makes it too easy to
|
||||
unintentionally commit changes that should not be committed, such as submodule
|
||||
updates. You can use `git status` to check if there are any files you forgot
|
||||
to stage.
|
||||
6. Push your changes to your fork: `git push -u origin issue-12345-fix`.
|
||||
7. [Open a PR][ghpullrequest] from your fork to rust-lang/rust's master branch.
|
||||
|
||||
[ghpullrequest]: https://guides.github.com/activities/forking/#making-a-pull-request
|
||||
|
||||
If your reviewer requests changes, the procedure for those changes looks much
|
||||
the 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`.
|
||||
|
||||
## Conflicts
|
||||
|
||||
When you edit your code locally, you are making changes to the version of
|
||||
rust-lang/rust that existed the last time you ran `git pull rust master` on
|
||||
your master branch. As such, when you submit your PR it is possible that some
|
||||
of the changes that have been made to rust-lang/rust since then are in conflict
|
||||
with the changes you've made; maybe someone else changed the same lines of
|
||||
code, or git cannot figure out how to merge your changes with the others for
|
||||
another reason.
|
||||
|
||||
When this happens, you need to resolve the conflicts before your changes can be
|
||||
merged. First, get a local copy of the conflicting changes. Checkout your local
|
||||
master branch with `git checkout master`. Then, `git pull rust master` to
|
||||
update it with the most recent changes.
|
||||
|
||||
### Rebasing
|
||||
|
||||
You're now ready to start the rebasing process. Check out the branch with your
|
||||
changes, and then execute `git rebase master`.
|
||||
|
||||
First, a little background: In git, commits are stored as "diffs" which are a
|
||||
record of all the changes that a commit made to its parent. When you rebase a
|
||||
branch, all the changes in the commits on that branch are reapplied on the
|
||||
branch you are rebasing on top of (in this case master). In other words, git
|
||||
tries to pretend that the changes you made to the old version of master were
|
||||
instead made to the new version of master.
|
||||
|
||||
During rebasing, you should expect to encounter at least one "rebase conflict."
|
||||
This happens when git's attempt to reapply the changes onto the more recent
|
||||
version of master fails because your changes conflicted with other changes that
|
||||
have been made since then. You can tell that this happened because you'll see
|
||||
lines 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
|
||||
|
||||
```
|
||||
<<<<<<< HEAD
|
||||
Original code
|
||||
=======
|
||||
Your code
|
||||
>>>>>>> 8fbf656... Commit fixes 12345
|
||||
```
|
||||
|
||||
This represents the lines in the file that git could not figure out how to
|
||||
rebase. The section between `<<<<<<< HEAD` and `=======` has the code from
|
||||
master, while the other side has your version of the code. You'll need to
|
||||
decide how to deal with the conflict. You may want to keep your changes,
|
||||
keep the changes on master, or combine the two.
|
||||
|
||||
Generally, resovling the conflict consists of two steps: First, fix the
|
||||
particular conflict. Edit the file to make the changes you want and remove the
|
||||
`<<<<<<<`, `=======` and `>>>>>>>` lines in the process. Second, check the
|
||||
surrounding code. If there was a conflict, its because someone else changed the
|
||||
same code you did. That means its likely there are some logical errors lying
|
||||
around too!
|
||||
|
||||
Once you're all done fixing the conflicts, you need to stage the files that had
|
||||
conflicts in them via `git add`. Afterwards, run `git rebase --continue` to let
|
||||
git know that you've resolved the conflicts and it should finish the rebase.
|
||||
Finally, once the rebase has succeeded, you'll want to update the associated
|
||||
branch on your fork with `git push -f`.
|
||||
|
||||
Note that `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 behind
|
||||
hint: 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 the "no-merge" policy, running
|
||||
`git pull` will create a merge commit, defeating the point of your rebase. Use
|
||||
`git push -f` instead.
|
||||
|
||||
## Advanced Rebasing
|
||||
|
||||
Sometimes, you may want to perform a more complicated rebase. There are two
|
||||
common scenarios that might call for this.
|
||||
|
||||
If your branch contains multiple consecutive rewrites of the same code, or if
|
||||
the rebase conflicts are extremely severe, it is possible that just trying to
|
||||
reapply the changes you made on top of the updated code will be too much of a
|
||||
headache. In this case, you can use the interactive rebase feature via
|
||||
`git rebase -i master` to gain more control over the process. This allows you
|
||||
to choose to skip commits because they represent changes you no longer need,
|
||||
edit the commits that you do not skip, or change the order in which they are
|
||||
applied.
|
||||
|
||||
The other common scenario is if you are asked to or want to "squash" multiple
|
||||
commits into each other. The most common example of this is likely if you
|
||||
forgot to run `x.py tidy` before committing. Your PR will need to be revised,
|
||||
but a single commit at the end with message "fixup tidy issue" is usually
|
||||
unhelpful, and it is easier for everyone else if you combine that commit with
|
||||
another that has a more meaningful commit message. In this case, you'll want to
|
||||
run `git rebase -i HEAD~2` to edit the last two commits and merge them
|
||||
together. Essentially, this reapplies the last two commits on top of your
|
||||
current branch; this is of course a no-op, since that is where they are
|
||||
already. However, by selecting the `-i` option, you give yourself the
|
||||
opportunity to edit the rebase first, just like before. This way you can
|
||||
request to have the most recent commit squashed into its parent.
|
||||
|
||||
## No-Merge Policy
|
||||
|
||||
The rust-lang/rust repo uses what is known as a "rebase workflow." This means
|
||||
that merge commits in PRs are not accepted. As a result, if you are running
|
||||
`git merge` locally, chances are good that you should be rebasing instead. Of
|
||||
course, this is not always true; if your merge will just be a fast-forward,
|
||||
like the merges that `git pull` usually performs, then no merge commit is
|
||||
created and you have nothing to worry about. Running `git config merge.ff only`
|
||||
once will prevent the creation of merge commits will help ensure you don't make
|
||||
a mistake here.
|
||||
|
||||
There are a number of reasons for this decision and like all others, it is a
|
||||
tradeoff. The main advantage is the (mostly) linear commit history. This
|
||||
greatly simplifies bisecting. TODO: There are other advantages to a rebase
|
||||
workflow, but I would like to focus on the ones that people in the Rust project
|
||||
consider most relevant, so I'm going to leave this unfinished for now.
|
||||
Loading…
Reference in New Issue