117 lines
3.8 KiB
Markdown
117 lines
3.8 KiB
Markdown
# Doing Mass Renames in Zebra Code
|
|
|
|
Sometimes we want to rename a Rust type or function, or change a log message.
|
|
|
|
But our types and functions are also used in our documentation,
|
|
so the compiler can sometimes miss when their names are changed.
|
|
|
|
Our log messages are also used in our integration tests,
|
|
so changing them can lead to unexpected test failures or hangs.
|
|
|
|
## Universal Renames with `sed`
|
|
|
|
You can use `sed` to rename all the instances of a name in Zebra's code, documentation, and tests:
|
|
```sh
|
|
git ls-tree --full-tree -r --name-only HEAD | \
|
|
xargs sed -i -e 's/OldName/NewName/g' -e 's/OtherOldName/OtherNewName/g'
|
|
```
|
|
|
|
Or excluding specific paths:
|
|
```sh
|
|
git ls-tree --full-tree -r --name-only HEAD | \
|
|
grep -v -e 'path-to-skip' -e 'other-path-to-skip' | \
|
|
xargs sed -i -e 's/OldName/NewName/g' -e 's/OtherOldName/OtherNewName/g'
|
|
```
|
|
|
|
`sed` also supports regular expressions to replace a pattern with another pattern.
|
|
|
|
Here's how to make a PR with these replacements:
|
|
1. Run the `sed` commands
|
|
2. Run `cargo fmt --all` after doing all the replacements
|
|
3. Put the commands in the commit message and pull request, so the reviewer can check them
|
|
|
|
Here's how to review that PR:
|
|
1. Check out two copies of the repository, one with the PR, and one without:
|
|
```sh
|
|
cd zebra
|
|
git fetch --all
|
|
# clear the checkout so we can use main elsewhere
|
|
git checkout main^
|
|
# Use the base branch or commit for the PR, which is usually main
|
|
git worktree add ../zebra-sed main
|
|
git worktree add ../zebra-pr origin/pr-branch-name
|
|
```
|
|
|
|
2. Run the scripts on the repository without the PR:
|
|
```sh
|
|
cd ../zebra-sed
|
|
# run the scripts in the PR or commit message
|
|
git ls-tree --full-tree -r --name-only HEAD | \
|
|
grep -v -e 'path-to-skip' -e 'other-path-to-skip' | \
|
|
xargs sed -i -e 's/OldName/NewName/g' -e 's/OtherOldName/OtherNewName/g'
|
|
cargo fmt --all
|
|
```
|
|
|
|
3. Automatically check that they match
|
|
```sh
|
|
cd ..
|
|
git diff zebra-sed zebra-pr
|
|
```
|
|
|
|
If there are no differences, then the PR can be approved.
|
|
|
|
If there are differences, then post them as a review in the PR,
|
|
and ask the author to re-run the script on the latest `main`.
|
|
|
|
## Interactive Renames with `fastmod`
|
|
|
|
You can use `fastmod` to rename some instances, but skip others:
|
|
```sh
|
|
fastmod --hidden --fixed-strings "OldName" "NewName" [paths to change]
|
|
```
|
|
|
|
Using the `--hidden` flag does renames in `.github` workflows, issue templates, and other configs.
|
|
|
|
`fastmod` also supports regular expressions to replace a pattern with another pattern.
|
|
|
|
Here's how to make a PR with these replacements:
|
|
1. Run the `fastmod` commands, choosing which instances to replace
|
|
2. Run `cargo fmt --all` after doing all the replacements
|
|
3. Put the commands in the commit message and pull request, so the reviewer can check them
|
|
4. If there are a lot of renames:
|
|
- use `sed` on any directories or files that are always renamed, and put them in the first PR,
|
|
- do a cleanup using `fastmod` in the next PR.
|
|
|
|
Here's how to review that PR:
|
|
1. Manually review each replacement (there's no shortcut)
|
|
|
|
## Using `rustdoc` links to detect name changes
|
|
|
|
When you're referencing a type or function in a doc comment,
|
|
use a `rustdoc` link to refer to it.
|
|
|
|
This makes the documentation easier to navigate,
|
|
and our `rustdoc` lint will detect any typos or name changes.
|
|
|
|
```rust
|
|
//! This is what `rustdoc` links look like:
|
|
//! - [`u32`] type or trait
|
|
//! - [`drop()`] function
|
|
//! - [`Clone::clone()`] method
|
|
//! - [`Option::None`] enum variant
|
|
//! - [`Option::Some(_)`](Option::Some) enum variant with data
|
|
//! - [`HashMap`](std::collections::HashMap) fully-qualified path
|
|
//! - [`BTreeSet<String>`](std::collections::BTreeSet) fully-qualified path with generics
|
|
```
|
|
|
|
If a type isn't imported in the module or Rust prelude,
|
|
then it needs a fully-qualified path in the docs, or an unused import:
|
|
```rust
|
|
// For rustdoc
|
|
#[allow(unused_imports)]
|
|
use std::collections::LinkedList;
|
|
|
|
//! Link to [`LinkedList`].
|
|
struct Type;
|
|
```
|