-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
84fa321
commit 1decf69
Showing
11 changed files
with
2,213 additions
and
1 deletion.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# re:patch | ||
|
||
[![Latest Version]][crates.io] | ||
|
||
re:patch is a line-oriented find-and-replace tool with a [`git add | ||
--patch`][git-add-patch]-like interface. Regular expressions and capture groups | ||
are supported, and re:patch can be used with files and directories. Only Linux | ||
is currently supported. | ||
|
||
> [!WARNING] | ||
> This tool is still in development. While it Works For Me™, it does not yet | ||
> have any tests. It's recommended to only use this in directories that are | ||
> version controlled. | ||
[crates.io]: https://crates.io/crates/repatch | ||
[Latest Version]: https://img.shields.io/crates/v/repatch.svg | ||
[git-add-patch]: https://git-scm.com/docs/git-add#Documentation/git-add.txt---patch | ||
|
||
### Install | ||
|
||
You can install from source or through crates.io. You must have a recent | ||
[rust/cargo][rust] toolchain installed. | ||
|
||
``` | ||
# install the latest release from crates.io | ||
cargo install repatch | ||
# install from source | ||
git clone https://github.com/stevenengler/repatch.git | ||
cd repatch && cargo install --path . | ||
``` | ||
|
||
[rust]: https://www.rust-lang.org/tools/install | ||
|
||
### Example | ||
|
||
<picture> | ||
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/example-dark.png"> | ||
<img width="730" alt="Command-line usage example." src="docs/assets/example-light.png"> | ||
</picture> | ||
|
||
### Notes | ||
|
||
Similar to [ripgrep][ripgrep], gitignore rules are respected and hidden | ||
files/directories are ignored. | ||
|
||
The editor used to edit patches can be configured using environment variables | ||
or the git configuration. The search priority is `VISUAL`, `EDITOR`, | ||
`GIT_EDITOR`, and `git config core.editor`. Otherwise vim is used. Like `sudo | ||
-e` the editor value is split by whitespace characters and executed, and is not | ||
interpreted by a shell. | ||
|
||
Patches shown in the terminal will have ANSI escape sequences replaced with | ||
safe versions. | ||
|
||
Like most text editors, files are replaced and not edited in-place. This means | ||
that the file owner or other metadata may change after editing. The new file | ||
will have the same read/write/execute permissions as the original file. You | ||
will also need enough temporary disk space for this second file. For example if | ||
you're editing a 10 GB file, you must have at least 10 GB of disk space free so | ||
that the new file can be written before the original file is deleted. | ||
|
||
Large files (larger than the amount of available memory) are supported as long | ||
as they have a sufficient number of lines. For example a 10 GB file with 10,000 | ||
lines should work fine, but a 10 GB file with a single line might exhaust the | ||
system memory and would not look very nice in the terminal. | ||
|
||
[ripgrep]: https://github.com/BurntSushi/ripgrep | ||
|
||
### Acknowledgements | ||
|
||
Most of the heavy lifting is done by the [ripgrep][ripgrep] family of crates, | ||
[clap][clap], and [diffy][diffy]. | ||
|
||
[clap]: https://docs.rs/clap/latest/clap/ | ||
[diffy]: https://docs.rs/diffy/latest/diffy/ |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Publishing a new release | ||
|
||
1. Update the code. | ||
|
||
```bash | ||
# make sure we don't include personal information (such as our | ||
# home directory name) in the release | ||
cd /tmp | ||
|
||
# make sure we don't include any untracked files in the release | ||
git clone [email protected]:stevenengler/repatch.git | ||
cd repatch | ||
|
||
# update the version | ||
vim Cargo.toml | ||
cargo update --package repatch | ||
|
||
# check for errors | ||
git diff | ||
cargo publish --dry-run --allow-dirty | ||
|
||
# add and commit version changes with commit message, for example | ||
# "Updated version to '0.2.1'" | ||
git add --patch | ||
git commit | ||
git push | ||
``` | ||
|
||
2. After CI tests finish on GitHub, mark it as a new release. | ||
|
||
3. Publish the crate. | ||
|
||
```bash | ||
# make sure there are no untracked or changed files | ||
git status | ||
# publish | ||
cargo publish --dry-run | ||
cargo publish | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
use std::path::PathBuf; | ||
|
||
use clap::Parser; | ||
|
||
const VERSION_STR: &str = concat!("re:patch ", env!("CARGO_PKG_VERSION")); | ||
|
||
/// re:patch is a line-oriented find-and-replace tool with a `git add --patch`-like interface. | ||
/// Directories are searched recursively. Hidden files/directories and binary files are ignored, as | ||
/// well as files/directories specified in gitignore rules. Regular expressions with capture groups | ||
/// are supported. | ||
#[derive(Debug, Parser)] | ||
#[command(version, name = "re:patch", max_term_width = 120, help_expected = true)] | ||
#[command(before_help(VERSION_STR))] | ||
pub struct Args { | ||
/// Regex to search for, optionally with capture groups. | ||
pub find: String, | ||
/// Text to replace `<FIND>` with. Capture group indices and names are supported. | ||
pub replace: String, | ||
/// Paths (files and/or directories) to search recursively. | ||
#[clap(required = true)] | ||
pub paths: Vec<PathBuf>, | ||
/// Case-insensitive search. | ||
#[clap(long, short)] | ||
pub ignore_case: bool, | ||
/// Ignore filesystem-related errors while searching ("no such file", "permission denied", etc). | ||
#[clap(long)] | ||
pub ignore_errors: bool, | ||
/// Generate diffs with `<N>` lines of context; also accepts "infinite". | ||
#[clap(long, default_value_t, value_name = "N")] | ||
pub context: Context, | ||
/// Show the changes without modifying any files. | ||
/// | ||
/// This does not generate valid patch files and is meant only for terminal output. ANSI escape | ||
/// sequences are replaced in the generated patches. | ||
#[clap(long, conflicts_with_all(["apply"]))] | ||
pub show: bool, | ||
/// Apply and write all changes automatically without any user input or confirmation. | ||
#[clap(long)] | ||
pub apply: bool, | ||
} | ||
|
||
#[derive(Copy, Clone, Debug)] | ||
pub enum Context { | ||
Num(u64), | ||
Infinite, | ||
} | ||
|
||
impl std::str::FromStr for Context { | ||
type Err = std::num::ParseIntError; | ||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
Ok(match s { | ||
"infinite" => Self::Infinite, | ||
x => Self::Num(x.parse()?), | ||
}) | ||
} | ||
} | ||
|
||
impl std::fmt::Display for Context { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
Self::Num(x) => write!(f, "{x}"), | ||
Self::Infinite => write!(f, "infinite"), | ||
} | ||
} | ||
} | ||
|
||
impl Default for Context { | ||
fn default() -> Self { | ||
Self::Num(5) | ||
} | ||
} |
Oops, something went wrong.