From 328bd35e7da69c68fe75d8ef81565f2e6878dac2 Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Thu, 24 Sep 2020 10:10:52 -0400 Subject: [PATCH] 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! --- src/SUMMARY.md | 1 + src/git.md | 203 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 src/git.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 5bd03878..eaefb2ed 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -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) diff --git a/src/git.md b/src/git.md new file mode 100644 index 00000000..07e30c2d --- /dev/null +++ b/src/git.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.