Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
5 changes: 1 addition & 4 deletions src/commands/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,7 @@ impl CommitOptions<'_> {
}

if self.stage_mode == StageMode::All {
let status = wt
.run_command(&["status", "--porcelain", "-z", "-uall"])
.context("Failed to get status")?;
warn_about_untracked_files(&status)?;
warn_about_untracked_files(&wt)?;
}

// Stage changes based on mode. Re-gated inside `stage`: the refusal
Expand Down
46 changes: 27 additions & 19 deletions src/commands/repository_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ use super::worktree::{RemovalPlan, SharedBranchCheckout};
use anyhow::{Context, bail};
use color_print::cformat;
use worktrunk::git::{
BranchDeletionMode, GitError, IntegrationReason, RefSnapshot, Repository, WorktreeInfo,
parse_porcelain_z, parse_untracked_files,
BranchDeletionMode, GitError, IntegrationReason, RefSnapshot, Repository, WorkingTree,
WorktreeInfo, parse_porcelain_z, parse_untracked_files,
};
use worktrunk::path::format_path_for_display;
use worktrunk::styling::{eprintln, format_with_gutter, suggest_command, warning_message};
use worktrunk::styling::{
eprintln, format_with_gutter, hint_message, suggest_command, warning_message,
};

/// Target for worktree removal.
#[derive(Debug)]
Expand All @@ -31,9 +33,6 @@ pub enum RemoveTarget {
/// CLI-only helpers implemented on [`Repository`] via an extension trait so we can keep orphan
/// implementations inside the binary crate.
pub trait RepositoryCliExt {
/// Warn about untracked files being auto-staged.
fn warn_if_auto_staging_untracked(&self) -> anyhow::Result<()>;

/// Prepare the removal of whichever worktree or branch [`RemoveTarget`]
/// names.
///
Expand Down Expand Up @@ -99,15 +98,6 @@ pub trait RepositoryCliExt {
}

impl RepositoryCliExt for Repository {
fn warn_if_auto_staging_untracked(&self) -> anyhow::Result<()> {
// `-uall` overrides the user's display preference and expands untracked
// directories so the warning names every path `git add -A` will stage.
let status = self
.run_command(&["status", "--porcelain", "-z", "-uall"])
.context("Failed to get status")?;
warn_about_untracked_files(&status)
}

#[allow(clippy::too_many_arguments)]
fn prepare_worktree_removal(
&self,
Expand Down Expand Up @@ -620,8 +610,18 @@ pub(crate) fn check_not_default_branch(
}

/// Warn about untracked files that will be auto-staged.
pub(crate) fn warn_about_untracked_files(status_output: &str) -> anyhow::Result<()> {
let files = parse_untracked_files(status_output);
///
/// The listing has at most `MAX_ROWS` rows. Past that many paths, the last row
/// is a hint counting the rest, which is always at least two paths.
pub(crate) fn warn_about_untracked_files(wt: &WorkingTree) -> anyhow::Result<()> {
const MAX_ROWS: usize = 10;

// `-uall` overrides the user's display preference and expands untracked
// directories so the warning counts every path `git add -A` will stage.
let status = wt
.run_command(&["status", "--porcelain", "-z", "-uall"])
.context("Failed to get status")?;
let files = parse_untracked_files(&status);
if files.is_empty() {
return Ok(());
}
Expand All @@ -633,8 +633,16 @@ pub(crate) fn warn_about_untracked_files(status_output: &str) -> anyhow::Result<
warning_message(format!("Auto-staging {count} untracked {path_word}:"))
);

let joined_files = files.join("\n");
eprintln!("{}", format_with_gutter(&joined_files, None));
let listed = if count > MAX_ROWS {
MAX_ROWS - 1
} else {
count
};
eprintln!("{}", format_with_gutter(&files[..listed].join("\n"), None));
if listed < count {
let omitted = count - listed;
eprintln!("{}", hint_message(format!("… and {omitted} other paths")));
}

Ok(())
}
Expand Down
4 changes: 2 additions & 2 deletions src/commands/step/squash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use super::super::command_executor::FailureStrategy;
use super::super::commit::{CommitGenerator, CommitOutcome, HookGate, StageMode};
use super::super::context::CommandEnv;
use super::super::hooks::{HookAnnouncer, execute_hook};
use super::super::repository_ext::RepositoryCliExt;
use super::super::repository_ext::warn_about_untracked_files;
use super::super::template_vars::TemplateVars;
use super::shared::print_dry_run;

Expand Down Expand Up @@ -160,7 +160,7 @@ pub fn handle_squash(

// Auto-stage changes before running pre-commit hooks so both beta and merge paths behave identically
if stage_mode == StageMode::All {
repo.warn_if_auto_staging_untracked()?;
warn_about_untracked_files(&wt)?;
}
wt.stage(stage_mode)?;

Expand Down
24 changes: 24 additions & 0 deletions tests/integration_tests/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2769,6 +2769,30 @@ fn test_step_commit_auto_staging_warns_about_untracked_files_hidden_by_user_conf
assert_hidden_untracked_auto_staging_warning(&output, "step commit");
}

/// Ten paths fill the listing's ten rows. One more lists nine and counts the
/// other two, so the hint never takes the row a single remaining path would
/// have used.
#[rstest]
fn test_step_commit_auto_staging_caps_untracked_listing(
repo: TestRepo,
#[values(10, 11)] count: usize,
) {
fs::create_dir(repo.root_path().join("generated")).unwrap();
for i in 1..=count {
fs::write(repo.root_path().join(format!("generated/{i:02}.txt")), "").unwrap();
}

let mut cmd = make_snapshot_cmd(&repo, "step", &["commit"], None);
cmd.env(
"WORKTRUNK_COMMIT__GENERATION__COMMAND",
"cat >/dev/null && echo 'feat: add generated files'",
);
assert_cmd_snapshot!(
format!("step_commit_auto_staging_caps_untracked_listing_{count}"),
cmd
);
}

#[rstest]
fn test_step_commit_with_stage_tracked_flag(repo: TestRepo) {
fs::write(repo.root_path().join("tracked.txt"), "initial").expect("Failed to write file");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
source: tests/integration_tests/merge.rs
info:
program: wt
args:
- step
- commit
env:
APPDATA: "[TEST_CONFIG_HOME]"
CLAUDE_CONFIG_DIR: "[TEST_CLAUDE_CONFIG]"
CLICOLOR_FORCE: "1"
COLUMNS: "500"
GIT_ALLOW_PROTOCOL: file
GIT_AUTHOR_DATE: "2025-01-01T00:00:00Z"
GIT_AUTHOR_EMAIL: test@example.com
GIT_AUTHOR_NAME: Test User
GIT_COMMITTER_DATE: "2025-01-01T00:00:00Z"
GIT_COMMITTER_EMAIL: test@example.com
GIT_COMMITTER_NAME: Test User
GIT_CONFIG_COUNT: "2"
GIT_CONFIG_GLOBAL: /nonexistent/wt/gitconfig
GIT_CONFIG_KEY_0: user.useConfigOnly
GIT_CONFIG_KEY_1: rerere.enabled
GIT_CONFIG_SYSTEM: /nonexistent/wt/gitconfig
GIT_CONFIG_VALUE_0: "true"
GIT_CONFIG_VALUE_1: "false"
GIT_TERMINAL_PROMPT: "0"
HOME: "[TEST_HOME]"
LANG: C
LC_ALL: C
LLVM_PROFILE_FILE: "[LLVM_PROFILE_FILE]"
OPENCODE_CONFIG_DIR: "[TEST_OPENCODE_CONFIG]"
PATH: "[PATH]"
TERM: alacritty
USERPROFILE: "[TEST_HOME]"
WORKTRUNK_APPROVALS_PATH: "[TEST_APPROVALS]"
WORKTRUNK_COMMIT__GENERATION__COMMAND: "cat >/dev/null && echo 'feat: add generated files'"
WORKTRUNK_CONFIG_PATH: "[TEST_CONFIG]"
WORKTRUNK_SYSTEM_CONFIG_PATH: "[TEST_SYSTEM_CONFIG]"
WORKTRUNK_TEST_BASH_INSTALLED: "0"
WORKTRUNK_TEST_CLAUDE_INSTALLED: "0"
WORKTRUNK_TEST_CODEX_INSTALLED: "0"
WORKTRUNK_TEST_DELAYED_STREAM_MS: "-1"
WORKTRUNK_TEST_EPOCH: "1735776000"
WORKTRUNK_TEST_FISH_INSTALLED: "0"
WORKTRUNK_TEST_GEMINI_INSTALLED: "0"
WORKTRUNK_TEST_MOCK_CONFIG_DIR: "[TEST_MOCK_CONFIG]"
WORKTRUNK_TEST_NUSHELL_ENV: "0"
WORKTRUNK_TEST_OPENCODE_INSTALLED: "0"
WORKTRUNK_TEST_PARENT_SHELL: ""
WORKTRUNK_TEST_POWERSHELL_ENV: "0"
WORKTRUNK_TEST_POWERSHELL_INSTALLED: "0"
WORKTRUNK_TEST_PROBE_TIMEOUT_MS: "60000"
WORKTRUNK_TEST_SKIP_URL_HEALTH_CHECK: "1"
WORKTRUNK_TEST_ZSH_INSTALLED: "0"
XDG_CONFIG_HOME: "[TEST_CONFIG_HOME]"
---
success: true
exit_code: 0
----- stdout -----

----- stderr -----
▲ Auto-staging 10 untracked paths:
  generated/01.txt
  generated/02.txt
  generated/03.txt
  generated/04.txt
  generated/05.txt
  generated/06.txt
  generated/07.txt
  generated/08.txt
  generated/09.txt
  generated/10.txt
◎ Generating commit message and committing changes... (10 files)
  feat: add generated files
✓ Committed changes @ [HASH]
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
source: tests/integration_tests/merge.rs
info:
program: wt
args:
- step
- commit
env:
APPDATA: "[TEST_CONFIG_HOME]"
CLAUDE_CONFIG_DIR: "[TEST_CLAUDE_CONFIG]"
CLICOLOR_FORCE: "1"
COLUMNS: "500"
GIT_ALLOW_PROTOCOL: file
GIT_AUTHOR_DATE: "2025-01-01T00:00:00Z"
GIT_AUTHOR_EMAIL: test@example.com
GIT_AUTHOR_NAME: Test User
GIT_COMMITTER_DATE: "2025-01-01T00:00:00Z"
GIT_COMMITTER_EMAIL: test@example.com
GIT_COMMITTER_NAME: Test User
GIT_CONFIG_COUNT: "2"
GIT_CONFIG_GLOBAL: /nonexistent/wt/gitconfig
GIT_CONFIG_KEY_0: user.useConfigOnly
GIT_CONFIG_KEY_1: rerere.enabled
GIT_CONFIG_SYSTEM: /nonexistent/wt/gitconfig
GIT_CONFIG_VALUE_0: "true"
GIT_CONFIG_VALUE_1: "false"
GIT_TERMINAL_PROMPT: "0"
HOME: "[TEST_HOME]"
LANG: C
LC_ALL: C
LLVM_PROFILE_FILE: "[LLVM_PROFILE_FILE]"
OPENCODE_CONFIG_DIR: "[TEST_OPENCODE_CONFIG]"
PATH: "[PATH]"
TERM: alacritty
USERPROFILE: "[TEST_HOME]"
WORKTRUNK_APPROVALS_PATH: "[TEST_APPROVALS]"
WORKTRUNK_COMMIT__GENERATION__COMMAND: "cat >/dev/null && echo 'feat: add generated files'"
WORKTRUNK_CONFIG_PATH: "[TEST_CONFIG]"
WORKTRUNK_SYSTEM_CONFIG_PATH: "[TEST_SYSTEM_CONFIG]"
WORKTRUNK_TEST_BASH_INSTALLED: "0"
WORKTRUNK_TEST_CLAUDE_INSTALLED: "0"
WORKTRUNK_TEST_CODEX_INSTALLED: "0"
WORKTRUNK_TEST_DELAYED_STREAM_MS: "-1"
WORKTRUNK_TEST_EPOCH: "1735776000"
WORKTRUNK_TEST_FISH_INSTALLED: "0"
WORKTRUNK_TEST_GEMINI_INSTALLED: "0"
WORKTRUNK_TEST_MOCK_CONFIG_DIR: "[TEST_MOCK_CONFIG]"
WORKTRUNK_TEST_NUSHELL_ENV: "0"
WORKTRUNK_TEST_OPENCODE_INSTALLED: "0"
WORKTRUNK_TEST_PARENT_SHELL: ""
WORKTRUNK_TEST_POWERSHELL_ENV: "0"
WORKTRUNK_TEST_POWERSHELL_INSTALLED: "0"
WORKTRUNK_TEST_PROBE_TIMEOUT_MS: "60000"
WORKTRUNK_TEST_SKIP_URL_HEALTH_CHECK: "1"
WORKTRUNK_TEST_ZSH_INSTALLED: "0"
XDG_CONFIG_HOME: "[TEST_CONFIG_HOME]"
---
success: true
exit_code: 0
----- stdout -----

----- stderr -----
▲ Auto-staging 11 untracked paths:
  generated/01.txt
  generated/02.txt
  generated/03.txt
  generated/04.txt
  generated/05.txt
  generated/06.txt
  generated/07.txt
  generated/08.txt
  generated/09.txt
↳ … and 2 other paths
◎ Generating commit message and committing changes... (11 files)
  feat: add generated files
✓ Committed changes @ [HASH]
Loading