]>
Commit | Line | Data |
---|---|---|
6a06907d XL |
1 | # Using Git |
2 | ||
3 | <!-- toc --> | |
4 | ||
5 | The Rust project uses [Git] to manage its source code. In order to | |
6 | contribute, you'll need some familiarity with its features so that your changes | |
7 | can be incorporated into the compiler. | |
8 | ||
9 | [Git]: https://git-scm.com | |
10 | ||
11 | The goal of this page is to cover some of the more common questions and | |
12 | problems new contributors face. Although some Git basics will be covered here, | |
13 | if you find that this is still a little too fast for you, it might make sense | |
14 | to first read some introductions to Git, such as the Beginner and Getting | |
15 | started sections of [this tutorial from Atlassian][atlassian-git]. GitHub also | |
16 | provides [documentation] and [guides] for beginners, or you can consult the | |
17 | more in depth [book from Git]. | |
18 | ||
19 | [book from Git]: https://git-scm.com/book/en/v2/ | |
20 | [atlassian-git]: https://www.atlassian.com/git/tutorials/what-is-version-control | |
21 | [documentation]: https://docs.github.com/en/github/getting-started-with-github/set-up-git | |
22 | [guides]: https://guides.github.com/introduction/git-handbook/ | |
23 | ||
24 | ## Prerequisites | |
25 | ||
26 | We'll assume that you've installed Git, forked [rust-lang/rust], and cloned the | |
27 | forked repo to your PC. We'll use the command line interface to interact | |
28 | with Git; there are also a number of GUIs and IDE integrations that can | |
29 | generally do the same things. | |
30 | ||
31 | [rust-lang/rust]: https://github.com/rust-lang/rust | |
32 | ||
33 | If you've cloned your fork, then you will be able to reference it with `origin` | |
34 | in your local repo. It may be helpful to also set up a remote for the official | |
35 | rust-lang/rust repo via | |
36 | ||
37 | ```sh | |
38 | git remote add upstream https://github.com/rust-lang/rust.git | |
39 | ``` | |
40 | ||
41 | if you're using HTTPS, or | |
42 | ||
43 | ```sh | |
44 | git remote add upstream git@github.com:rust-lang/rust.git | |
45 | ``` | |
46 | ||
47 | if you're using SSH. | |
48 | ||
49 | ## Standard Process | |
50 | ||
51 | Below is the normal procedure that you're likely to use for most minor changes | |
52 | and PRs: | |
53 | ||
54 | 1. Ensure that you're making your changes on top of master: | |
55 | `git checkout master`. | |
56 | 2. Get the latest changes from the Rust repo: `git pull upstream master`. | |
57 | 3. Make a new branch for your change: `git checkout -b issue-12345-fix`. | |
58 | 4. Make some changes to the repo and test them. | |
59 | 5. Stage your changes via `git add src/changed/file.rs src/another/change.rs` | |
60 | and then commit them with `git commit`. Of course, making intermediate commits | |
61 | may be a good idea as well. Avoid `git add .`, as it makes it too easy to | |
62 | unintentionally commit changes that should not be committed, such as submodule | |
63 | updates. You can use `git status` to check if there are any files you forgot | |
64 | to stage. | |
65 | 6. Push your changes to your fork: `git push --set-upstream origin issue-12345-fix`. | |
66 | 7. [Open a PR][ghpullrequest] from your fork to rust-lang/rust's master branch. | |
67 | ||
68 | [ghpullrequest]: https://guides.github.com/activities/forking/#making-a-pull-request | |
69 | ||
70 | If your reviewer requests changes, the procedure for those changes looks much | |
71 | the same, with some steps skipped: | |
72 | ||
73 | 1. Ensure that you're making changes to the most recent version of your code: | |
74 | `git checkout issue-12345-fix`. | |
75 | 2. Make, stage, and commit your additional changes just like before. | |
76 | 3. Push those changes to your fork: `git push`. | |
77 | ||
78 | ## Troubleshooting git issues | |
79 | ||
80 | You don't need to clone `rust-lang/rust` from scratch if it's out of date! | |
81 | Even if you think you've messed it up beyond repair, there are ways to fix | |
82 | the git state that don't require downloading the whole repository again. | |
83 | Here are some common issues you might run into: | |
84 | ||
85 | ### I deleted my fork on GitHub! | |
86 | ||
87 | This is not a problem from git's perspective. If you run `git remote -v`, | |
88 | it will say something like this: | |
89 | ||
90 | ``` | |
91 | $ git remote -v | |
92 | origin https://github.com//rust-lang/rust (fetch) | |
93 | origin https://github.com//rust-lang/rust (push) | |
94 | personal https://github.com/jyn514/rust (fetch) | |
95 | personal https://github.com/jyn514/rust (push) | |
96 | ``` | |
97 | ||
98 | If you renamed your fork, you can change the URL like this: | |
99 | ||
100 | ```console | |
101 | git remote set-url personal <URL> | |
102 | ``` | |
103 | ||
104 | where the `<URL>` is your new fork. | |
105 | ||
106 | ### I see 'Untracked Files: src/stdarch'? | |
107 | ||
108 | This is left over from the move to the `library/` directory. | |
109 | Unfortunately, `git rebase` does not follow renames for submodules, so you | |
110 | have to delete the directory yourself: | |
111 | ||
112 | ```console | |
113 | rm -r src/stdarch | |
114 | ``` | |
115 | ||
116 | ### I see `<<< HEAD`? | |
117 | ||
118 | You were probably in the middle of a rebase or merge conflict. See | |
119 | [Conflicts](#conflicts) for how to fix the conflict. If you don't care about the changes | |
120 | and just want to get a clean copy of the repository back, you can use `git reset`: | |
121 | ||
122 | ```console | |
123 | # WARNING: this throws out any local changes you've made! Consider resolving the conflicts instead. | |
124 | git reset --hard master | |
125 | ``` | |
126 | ||
127 | ### Quick note about submodules | |
128 | ||
129 | When updating your local repository with `git pull`, you may notice that sometimes | |
130 | Git says you have modified some files that you have never edited. For example, | |
131 | running `git status` gives you something like (note the `new commits` mention): | |
132 | ||
133 | ``` | |
134 | On branch master | |
135 | Your branch is up to date with 'origin/master'. | |
136 | ||
137 | Changes not staged for commit: | |
138 | (use "git add <file>..." to update what will be committed) | |
139 | (use "git restore <file>..." to discard changes in working directory) | |
140 | modified: src/tools/cargo (new commits) | |
141 | modified: src/tools/rls (new commits) | |
142 | modified: src/tools/rustfmt (new commits) | |
143 | ||
144 | no changes added to commit (use "git add" and/or "git commit -a") | |
145 | ``` | |
146 | ||
147 | These changes are not changes to files: they are changes to submodules (more on | |
148 | this [later](#git-submodules)). To get rid of those, run `git submodule update` | |
149 | (or run any `x.py` command, which will automatically update the submodules). | |
150 | Note that there is (as of <!-- date: 2021-01 --> January 2021) a bug if you use | |
151 | worktrees, submodules, and x.py in a commit hook. If you run into an error | |
152 | like: | |
153 | ||
154 | ``` | |
155 | error: failed to read `/home/joshua/rustc-worktree/src/tools/miri/cargo-miri/Cargo.toml` | |
156 | ||
157 | Caused by: | |
158 | No such file or directory (os error 2) | |
159 | ``` | |
160 | it's not anything you did wrong. There is a workaround at [#77620]. | |
161 | ||
162 | [#77620]: https://github.com/rust-lang/rust/issues/77620#issuecomment-705228229 | |
163 | ||
164 | ## Conflicts | |
165 | ||
166 | When you edit your code locally, you are making changes to the version of | |
167 | rust-lang/rust that existed when you created your feature branch. As such, when | |
168 | you submit your PR it is possible that some of the changes that have been made | |
169 | to rust-lang/rust since then are in conflict with the changes you've made. | |
170 | ||
171 | When this happens, you need to resolve the conflicts before your changes can be | |
172 | merged. First, get a local copy of the conflicting changes: Checkout your local | |
173 | master branch with `git checkout master`, then `git pull upstream master` to | |
174 | update it with the most recent changes. | |
175 | ||
176 | ### Rebasing | |
177 | ||
178 | You're now ready to start the rebasing process. Checkout the branch with your | |
179 | changes and execute `git rebase master`. | |
180 | ||
181 | When you rebase a branch on master, all the changes on your branch are | |
182 | reapplied to the most recent version of master. In other words, Git tries to | |
183 | pretend that the changes you made to the old version of master were instead | |
184 | made to the new version of master. During this process, you should expect to | |
185 | encounter at least one "rebase conflict." This happens when Git's attempt to | |
186 | reapply the changes fails because your changes conflicted with other changes | |
187 | that have been made. You can tell that this happened because you'll see | |
188 | lines in the output that look like | |
189 | ||
190 | ``` | |
191 | CONFLICT (content): Merge conflict in file.rs | |
192 | ``` | |
193 | ||
194 | When you open these files, you'll see sections of the form | |
195 | ||
196 | ``` | |
197 | <<<<<<< HEAD | |
198 | Original code | |
199 | ======= | |
200 | Your code | |
201 | >>>>>>> 8fbf656... Commit fixes 12345 | |
202 | ``` | |
203 | ||
204 | This represents the lines in the file that Git could not figure out how to | |
205 | rebase. The section between `<<<<<<< HEAD` and `=======` has the code from | |
206 | master, while the other side has your version of the code. You'll need to | |
207 | decide how to deal with the conflict. You may want to keep your changes, | |
208 | keep the changes on master, or combine the two. | |
209 | ||
210 | Generally, resolving the conflict consists of two steps: First, fix the | |
211 | particular conflict. Edit the file to make the changes you want and remove the | |
212 | `<<<<<<<`, `=======` and `>>>>>>>` lines in the process. Second, check the | |
213 | surrounding code. If there was a conflict, its likely there are some logical | |
214 | errors lying around too! It's a good idea to run `x.py check` here to make sure | |
215 | there are no glaring errors. | |
216 | ||
217 | Once you're all done fixing the conflicts, you need to stage the files that had | |
218 | conflicts in them via `git add`. Afterwards, run `git rebase --continue` to let | |
219 | Git know that you've resolved the conflicts and it should finish the rebase. | |
220 | Once the rebase has succeeded, you'll want to update the associated branch on | |
221 | your fork with `git push --force-with-lease`. | |
222 | ||
223 | Note that `git push` will not work properly and say something like this: | |
224 | ||
225 | ``` | |
226 | ! [rejected] issue-xxxxx -> issue-xxxxx (non-fast-forward) | |
227 | error: failed to push some refs to 'https://github.com/username/rust.git' | |
228 | hint: Updates were rejected because the tip of your current branch is behind | |
229 | hint: its remote counterpart. Integrate the remote changes (e.g. | |
230 | hint: 'git pull ...') before pushing again. | |
231 | hint: See the 'Note about fast-forwards' in 'git push --help' for details. | |
232 | ``` | |
233 | ||
234 | The advice this gives is incorrect! Because of Rust's | |
235 | ["no-merge" policy](#no-merge-policy) the merge commit created by `git pull` | |
236 | will not be allowed in the final PR, in addition to defeating the point of the | |
237 | rebase! Use `git push --force-with-lease` instead. | |
238 | ||
239 | ## Advanced Rebasing | |
240 | ||
241 | If your branch contains multiple consecutive rewrites of the same code, or if | |
242 | the rebase conflicts are extremely severe, you can use | |
243 | `git rebase --interactive master` to gain more control over the process. This | |
244 | allows you to choose to skip commits, edit the commits that you do not skip, | |
245 | change the order in which they are applied, or "squash" them into each other. | |
246 | ||
247 | Alternatively, you can sacrifice the commit history like this: | |
248 | ||
249 | ``` | |
250 | # squash all the changes into one commit so you only have to worry about conflicts once | |
251 | git rebase -i $(git merge-base master HEAD) # and squash all changes along the way | |
252 | git rebase master | |
253 | # fix all merge conflicts | |
254 | git rebase --continue | |
255 | ``` | |
256 | ||
257 | "Squashing" commits into each other causes them to be merged into a single | |
258 | commit. Both the upside and downside of this is that it simplifies the history. | |
259 | On the one hand, you lose track of the steps in which changes were made, but | |
260 | the history becomes easier to work with. | |
261 | ||
262 | You also may want to squash just the last few commits together, possibly | |
263 | because they only represent "fixups" and not real changes. For example, | |
264 | `git rebase --interactive HEAD~2` will allow you to edit the two commits only. | |
265 | ||
266 | ## No-Merge Policy | |
267 | ||
268 | The rust-lang/rust repo uses what is known as a "rebase workflow." This means | |
269 | that merge commits in PRs are not accepted. As a result, if you are running | |
270 | `git merge` locally, chances are good that you should be rebasing instead. Of | |
271 | course, this is not always true; if your merge will just be a fast-forward, | |
272 | like the merges that `git pull` usually performs, then no merge commit is | |
273 | created and you have nothing to worry about. Running `git config merge.ff only` | |
274 | once will ensure that all the merges you perform are of this type, so that you | |
275 | cannot make a mistake. | |
276 | ||
277 | There are a number of reasons for this decision and like all others, it is a | |
278 | tradeoff. The main advantage is the generally linear commit history. This | |
279 | greatly simplifies bisecting and makes the history and commit log much easier | |
280 | to follow and understand. | |
281 | ||
282 | ## Git submodules | |
283 | ||
284 | **NOTE**: submodules are a nice thing to know about, but it *isn't* an absolute | |
285 | prerequisite to contribute to `rustc`. If you are using Git for the first time, | |
286 | you might want to get used to the main concepts of Git before reading this section. | |
287 | ||
288 | The `rust-lang/rust` repository uses [Git submodules] as a way to use other | |
289 | Rust projects from within the `rust` repo. Examples include Rust's fork of | |
290 | `llvm-project` and many devtools such as `cargo`, `rust-analyzer` and `rustfmt`. | |
291 | ||
292 | Those projects are developped and maintained in an separate Git (and GitHub) | |
293 | repository, and they have their own Git history/commits, issue tracker and PRs. | |
294 | Submodules allow us to create some sort of embedded sub-repository inside the | |
295 | `rust` repository and use them like they were directories in the `rust` repository. | |
296 | ||
297 | Take `miri` for example. `miri` is maintained in the [`rust-lang/miri`] repository, | |
298 | but it is used in `rust-lang/rust` by the compiler for const evaluation. We bring it | |
299 | in `rust` as a submodule, in the `src/tools/miri` folder. | |
300 | ||
301 | The contents of submodules are ignored by Git: submodules are in some sense isolated | |
302 | from the rest of the repository. However, if you try to `cd src/tools/miri` and then | |
303 | run `git status`: | |
304 | ||
305 | ``` | |
306 | HEAD detached at 3fafb835 | |
307 | nothing to commit, working tree clean | |
308 | ``` | |
309 | ||
310 | As far as git is concerned, you are no longer in the `rust` repo, but in the `miri` repo. | |
311 | You will notice that we are in "detatched HEAD" state, i.e. not on a branch but on a | |
312 | particular commit. | |
313 | ||
314 | This is because, like any dependency, we want to be able to control which version to use. | |
315 | Submodules allow us to do just that: every submodule is "pinned" to a certain | |
316 | commit, which doesn't change unless modified manually. If you use `git checkout <commit>` | |
317 | in the `miri` directory and go back to the `rust` directory, you can stage this | |
318 | change like any other. This is usually done by the maintainers of the | |
319 | project, and looks like [this][miri-update]. | |
320 | ||
321 | Git submodules take some time to get used to, so don't worry if it isn't perfectly | |
322 | clear yet. You will rarely have to use them directly and, again, you don't need | |
323 | to know everything about submodules to contribute to Rust. Just know that they | |
324 | exist and that they correspond to some sort of embedded subrepository dependency | |
325 | that Git can nicely and fairly conveniently handle for us. | |
326 | ||
327 | [Git submodules]: https://git-scm.com/book/en/v2/Git-Tools-Submodules | |
328 | [`rust-toolstate`]: https://rust-lang-nursery.github.io/rust-toolstate/ | |
329 | [`rust-lang/miri`]: https://github.com/rust-lang/miri | |
330 | [miri-update]: https://github.com/rust-lang/rust/pull/77500/files |