Skip to content

Commit

Permalink
Add instructions for doing mass renames easily (#6865)
Browse files Browse the repository at this point in the history
  • Loading branch information
teor2345 authored Jun 8, 2023
1 parent d9add4a commit 0ffa47c
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 1 deletion.
3 changes: 2 additions & 1 deletion book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@
- [Network Architecture](dev/diagrams/zebra-network.md)
- [Continuous Integration](dev/continuous-integration.md)
- [Continuous Delivery](dev/continuous-delivery.md)
- [zebra-checkpoints](dev/zebra-checkpoints.md)
- [Generating Zebra Checkpoints](dev/zebra-checkpoints.md)
- [Doing Mass Renames](dev/mass-renames.md)
- [API Reference](api.md)
113 changes: 113 additions & 0 deletions book/src/dev/mass-renames.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# 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 's/OldName/NewName/g'
```

Or excluding specific paths:
```sh
git ls-tree --full-tree -r --name-only HEAD | \
grep -v 'path-to-skip' | \
xargs sed -i 's/OldName/NewName/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 | \
xargs sed -i 's/OldName/NewName/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 --fixed-strings "OldName" "NewName" [paths to change]
```

`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;
```

0 comments on commit 0ffa47c

Please sign in to comment.