Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ repos:
- id: end-of-file-fixer

- repo: https://github.com/crate-ci/typos
rev: v1.46.2
rev: v1.46.3
hooks:
- id: typos

Expand All @@ -40,6 +40,11 @@ repos:
- id: check-renovate
additional_dependencies: ['json5']

- repo: https://github.com/kreuzberg-dev/pre-commit-hooks
rev: v1.2.3
hooks:
- id: actionlint

- repo: local
hooks:
- id: taplo-fmt
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## Unreleased

### Enhancements

- Add opt-out for the working-tree keeper via `--no-stash`, `PREK_NO_STASH`, and a
`no_stash` top-level config key. The autostash behavior is undesirable when
working with several agents or tools at once on the same code — the keeper's
recovery path runs `git checkout -- <root>`, which can clobber uncommitted work
in flight from concurrent processes.

## 0.4.3

Released on 2026-05-27.
Expand Down
1 change: 1 addition & 0 deletions crates/prek-consts/src/env_vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ impl EnvVars {
pub const PREK_NO_CONCURRENCY: &'static str = "PREK_NO_CONCURRENCY";
pub const PREK_MAX_CONCURRENCY: &'static str = "PREK_MAX_CONCURRENCY";
pub const PREK_NO_FAST_PATH: &'static str = "PREK_NO_FAST_PATH";
pub const PREK_NO_STASH: &'static str = "PREK_NO_STASH";
pub const PREK_UV_SOURCE: &'static str = "PREK_UV_SOURCE";
pub const PREK_NATIVE_TLS: &'static str = "PREK_NATIVE_TLS";
pub const SSL_CERT_FILE: &'static str = "SSL_CERT_FILE";
Expand Down
1 change: 1 addition & 0 deletions crates/prek/src/cli/hook_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ pub(crate) async fn hook_impl(
flag(run_args.fail_fast, run_args.no_fail_fast),
false,
false,
false,
run_args.extra,
false,
printer,
Expand Down
9 changes: 9 additions & 0 deletions crates/prek/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,15 @@ pub(crate) struct RunArgs {
#[arg(long, hide = true, overrides_with = "fail_fast")]
pub(crate) no_fail_fast: bool,

/// Do not clean unstaged changes via the working-tree keeper before running hooks.
///
/// Equivalent to setting `PREK_NO_STASH=1` or `no_stash: true` in the project
/// configuration file. Useful when several agents or tools are editing the same
/// repository concurrently, where the keeper's recovery path can clobber
/// uncommitted work in flight from other processes.
#[arg(long)]
pub(crate) no_stash: bool,

/// Do not run the hooks, but print the hooks that would have been run.
#[arg(long)]
pub(crate) dry_run: bool,
Expand Down
38 changes: 32 additions & 6 deletions crates/prek/src/cli/run/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub(crate) async fn run(
show_diff_on_failure: bool,
fail_fast: Option<bool>,
dry_run: bool,
no_stash: bool,
refresh: bool,
extra_args: RunExtraArgs,
verbose: bool,
Expand All @@ -78,18 +79,43 @@ pub(crate) async fn run(
// Ensure we are in a git repository.
LazyLock::force(&GIT_ROOT).as_ref()?;

let should_stash = !all_files && files.is_empty() && directories.is_empty();
let workspace_root = Workspace::find_root(config.as_deref(), &CWD)?;
let selectors = Selectors::load(&includes, &skips, &workspace_root)?;
let mut workspace =
Workspace::discover(store, workspace_root, config, Some(&selectors), refresh)?;

// `--no-stash` flag, `PREK_NO_STASH=<boolish>` env var, or the top-level
// `no_stash: true` key in the root project's config disables the
// working-tree keeper entirely. Useful when downstream hooks re-stage files
// and conflict with prek's stash restore on large diffs.
// Resolution precedence (highest wins): CLI flag > env var > config > default-false.
// Use `all_projects()` (the unfiltered set) instead of `projects()` so that
// a root-level `no_stash: true` is honoured even when the CLI selector
// narrows the run to a nested project and the root project is absent from
// the filtered set.
let config_no_stash = workspace
.all_projects()
.iter()
.find(|p| p.is_root())
.and_then(|p| p.config().no_stash)
.unwrap_or(false);
// CLI flag wins outright. Otherwise an explicit env var value (including
// `PREK_NO_STASH=0`) overrides the config; absent any env var we fall back
// to the config value (default false).
let no_stash = if no_stash {
true
} else if let Some(env_value) = EnvVars::var_as_bool(EnvVars::PREK_NO_STASH) {
env_value
} else {
config_no_stash
};
let should_stash = !all_files && files.is_empty() && directories.is_empty() && !no_stash;

// Check if we have unresolved merge conflict files and fail fast.
if should_stash && git::has_unmerged_paths().await? {
anyhow::bail!("You have unmerged paths. Resolve them before running prek");
}

let workspace_root = Workspace::find_root(config.as_deref(), &CWD)?;
let selectors = Selectors::load(&includes, &skips, &workspace_root)?;
let mut workspace =
Workspace::discover(store, workspace_root, config, Some(&selectors), refresh)?;

if should_stash {
workspace.check_configs_staged().await?;
}
Expand Down
1 change: 1 addition & 0 deletions crates/prek/src/cli/try_repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ pub(crate) async fn try_repo(
run_args.show_diff_on_failure,
flag(run_args.fail_fast, run_args.no_fail_fast),
run_args.dry_run,
run_args.no_stash,
refresh,
run_args.extra,
verbose,
Expand Down
6 changes: 6 additions & 0 deletions crates/prek/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,12 @@ pub(crate) struct Config {
/// Set to true to have prek stop running hooks after the first failure.
/// Default is false.
pub fail_fast: Option<bool>,
/// Set to true to skip prek's working-tree keeper (stash/restore of unstaged
/// changes before running hooks). Equivalent to passing `--no-stash` or
/// setting `PREK_NO_STASH=1`. Useful when hook chains aggressively re-stage
/// files and conflict with prek's patch-based restore on large diffs.
/// Default is false.
pub no_stash: Option<bool>,
/// The minimum version of prek required to run this configuration.
#[serde(deserialize_with = "deserialize_and_validate_minimum_version", default)]
pub minimum_prek_version: Option<String>,
Expand Down
1 change: 1 addition & 0 deletions crates/prek/src/hook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,7 @@ mod tests {
files: None,
exclude: None,
fail_fast: None,
no_stash: None,
minimum_prek_version: None,
orphan: None,
_unused_keys: {},
Expand Down
1 change: 1 addition & 0 deletions crates/prek/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
args.show_diff_on_failure,
flag(args.fail_fast, args.no_fail_fast),
args.dry_run,
args.no_stash,
cli.globals.refresh,
args.extra,
cli.globals.verbose > 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ Ok(
files: None,
exclude: None,
fail_fast: None,
no_stash: None,
minimum_prek_version: None,
orphan: None,
_unused_keys: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ Config {
files: None,
exclude: None,
fail_fast: None,
no_stash: None,
minimum_prek_version: None,
orphan: None,
_unused_keys: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Config {
files: None,
exclude: None,
fail_fast: None,
no_stash: None,
minimum_prek_version: None,
orphan: None,
_unused_keys: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Config {
files: None,
exclude: None,
fail_fast: None,
no_stash: None,
minimum_prek_version: None,
orphan: None,
_unused_keys: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Config {
files: None,
exclude: None,
fail_fast: None,
no_stash: None,
minimum_prek_version: None,
orphan: None,
_unused_keys: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Config {
files: None,
exclude: None,
fail_fast: None,
no_stash: None,
minimum_prek_version: None,
orphan: None,
_unused_keys: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Config {
files: None,
exclude: None,
fail_fast: None,
no_stash: None,
minimum_prek_version: None,
orphan: None,
_unused_keys: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Config {
files: None,
exclude: None,
fail_fast: None,
no_stash: None,
minimum_prek_version: None,
orphan: None,
_unused_keys: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Config {
files: None,
exclude: None,
fail_fast: None,
no_stash: None,
minimum_prek_version: None,
orphan: None,
_unused_keys: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Config {
files: None,
exclude: None,
fail_fast: None,
no_stash: None,
minimum_prek_version: None,
orphan: None,
_unused_keys: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ Config {
fail_fast: Some(
true,
),
no_stash: None,
minimum_prek_version: None,
orphan: None,
_unused_keys: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ Config {
fail_fast: Some(
true,
),
no_stash: None,
minimum_prek_version: None,
orphan: None,
_unused_keys: {},
Expand Down
Loading
Loading