From 0ffa47cc1e81695e5e79a052dd79885e5283f90d Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 8 Jun 2023 20:50:18 +1000 Subject: [PATCH] Add instructions for doing mass renames easily (#6865) --- book/src/SUMMARY.md | 3 +- book/src/dev/mass-renames.md | 113 +++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 book/src/dev/mass-renames.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 6840019a2..77a8a8e35 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -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) diff --git a/book/src/dev/mass-renames.md b/book/src/dev/mass-renames.md new file mode 100644 index 000000000..cd9eda6de --- /dev/null +++ b/book/src/dev/mass-renames.md @@ -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`](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; +```