Skip to content
Merged
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
3 changes: 0 additions & 3 deletions docs/src/content/docs/merge.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,6 @@ Options:
- tracked: Stage tracked changes only (like git add -u)
- none: Stage nothing, commit only what's already in the index

--author <AUTHOR>
Commit author (Name <email>); committer stays the ambient identity

-h, --help
Print help (see a summary with '-h')

Expand Down
6 changes: 0 additions & 6 deletions docs/src/content/docs/step.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,6 @@ Options:
- tracked: Stage tracked changes only (like git add -u)
- none: Stage nothing, commit only what's already in the index

--author <AUTHOR>
Commit author (Name <email>); committer stays the ambient identity

--dry-run
Preview prompt, command, and generated message without committing

Expand Down Expand Up @@ -262,9 +259,6 @@ Options:
- tracked: Stage tracked changes only (like git add -u)
- none: Stage nothing, commit only what's already in the index

--author <AUTHOR>
Commit author (Name <email>); committer stays the ambient identity

--dry-run
Preview prompt, command, and generated message without squashing

Expand Down
3 changes: 0 additions & 3 deletions plugins/worktrunk/skills/worktrunk/reference/merge.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions plugins/worktrunk/skills/worktrunk/reference/step.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions skills/worktrunk/reference/merge.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions skills/worktrunk/reference/step.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -613,10 +613,6 @@ pub(crate) struct MergeArgs {
#[arg(long)]
pub(crate) stage: Option<crate::commands::commit::StageMode>,

/// Commit author (`Name <email>`); committer stays the ambient identity
#[arg(long, value_parser = clap::builder::NonEmptyStringValueParser::new())]
pub(crate) author: Option<String>,

/// Output format
///
/// JSON prints structured result to stdout after merge completes.
Expand Down
8 changes: 0 additions & 8 deletions src/cli/step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ pub struct CommitArgs {
#[arg(long)]
pub(crate) stage: Option<crate::commands::commit::StageMode>,

/// Commit author (`Name <email>`); committer stays the ambient identity
#[arg(long, value_parser = clap::builder::NonEmptyStringValueParser::new())]
pub(crate) author: Option<String>,

/// Preview prompt, command, and generated message without committing
#[arg(long, conflicts_with = "show_prompt")]
pub(crate) dry_run: bool,
Expand Down Expand Up @@ -47,10 +43,6 @@ pub struct SquashArgs {
#[arg(long)]
pub(crate) stage: Option<crate::commands::commit::StageMode>,

/// Commit author (`Name <email>`); committer stays the ambient identity
#[arg(long, value_parser = clap::builder::NonEmptyStringValueParser::new())]
pub(crate) author: Option<String>,

/// Preview prompt, command, and generated message without squashing
#[arg(long, conflicts_with = "show_prompt")]
pub(crate) dry_run: bool,
Expand Down
12 changes: 1 addition & 11 deletions src/commands/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ pub struct CommitOptions<'a> {
/// value supplied by the caller (`wt merge` resolves the guidance via
/// `approve_commit_template_append` up front and passes it through).
pub guidance: super::step::PreApprovedGuidance,
/// Explicit `--author` override (`Name <email>`), forwarded to `git
/// commit --author`. `None` uses git's ambient identity.
pub author: Option<&'a str>,
}

impl<'a> CommitOptions<'a> {
Expand All @@ -83,7 +80,6 @@ impl<'a> CommitOptions<'a> {
stage_mode: StageMode::All,
show_no_squash_note: false,
guidance: super::step::PreApprovedGuidance::RunOwnGate,
author: None,
}
}
}
Expand Down Expand Up @@ -145,7 +141,6 @@ impl<'a> CommitGenerator<'a> {
show_progress: bool,
show_no_squash_note: bool,
stage_mode: StageMode,
author: Option<&str>,
) -> anyhow::Result<CommitOutcome> {
// Fail early if nothing is staged (avoids confusing LLM prompt with empty diff)
if !wt.has_staged_changes()? {
Expand Down Expand Up @@ -196,11 +191,7 @@ impl<'a> CommitGenerator<'a> {
let formatted_message = self.format_message_for_display(&commit_message);
eprintln!("{}", format_with_gutter(&formatted_message, None));

let mut commit_args = vec!["commit", "-m", commit_message.as_str()];
if let Some(author) = author {
commit_args.push("--author");
commit_args.push(author);
}
let commit_args = vec!["commit", "-m", commit_message.as_str()];
wt.run_command(&commit_args).context("Failed to commit")?;

let commit_sha = wt.run_command(&["rev-parse", "HEAD"])?.trim().to_string();
Expand Down Expand Up @@ -292,7 +283,6 @@ impl CommitOptions<'_> {
true, // show_progress
self.show_no_squash_note,
self.stage_mode,
self.author,
)?;

// Register post-commit hooks onto the caller's announcer (respects --no-hooks).
Expand Down
10 changes: 1 addition & 9 deletions src/commands/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,6 @@ pub struct MergeOptions<'a> {
pub yes: bool,
pub stage: Option<super::commit::StageMode>,
pub format: crate::cli::SwitchFormat,
/// Explicit `--author` override (`Name <email>`), forwarded to the
/// commit/squash steps' `git commit --author` and, for `--no-ff`, to the
/// `commit-tree` merge commit's `GIT_AUTHOR_NAME`/`GIT_AUTHOR_EMAIL`.
/// `None` uses git's ambient identity.
pub author: Option<&'a str>,
}

/// Build the frozen [`ApprovedHookPlan`] for the merge's covered hooks, gating
Expand Down Expand Up @@ -161,7 +156,6 @@ pub fn handle_merge(opts: MergeOptions<'_>) -> anyhow::Result<()> {
flags,
yes,
stage,
author,
..
} = opts;

Expand Down Expand Up @@ -371,7 +365,6 @@ pub fn handle_merge(opts: MergeOptions<'_>) -> anyhow::Result<()> {
options.stage_mode = stage_mode;
options.show_no_squash_note = true;
options.guidance = guidance.clone();
options.author = author;

let _ = options.commit(&mut announcer)?;
true // Committed directly
Expand All @@ -392,7 +385,6 @@ pub fn handle_merge(opts: MergeOptions<'_>) -> anyhow::Result<()> {
Some(stage_mode),
&mut announcer,
guidance,
author,
)?,
super::step::SquashResult::Squashed { .. }
)
Expand Down Expand Up @@ -453,7 +445,7 @@ pub fn handle_merge(opts: MergeOptions<'_>) -> anyhow::Result<()> {
});
if !ff {
// Create a merge commit on the target branch via commit-tree + update-ref
handle_no_ff_merge(Some(&target_branch), operations, &current_branch, author)?;
handle_no_ff_merge(Some(&target_branch), operations, &current_branch)?;
} else {
// Fast-forward push to target branch
handle_push(Some(&target_branch), PushKind::MergeFastForward, operations)?;
Expand Down
1 change: 0 additions & 1 deletion src/commands/relocate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,6 @@ pub fn validate_candidates(
false, // show_progress - already showing "Committing changes in..."
false, // show_no_squash_note
StageMode::None, // already staged above
None,
)?;
} else {
// is_main without --commit
Expand Down
2 changes: 0 additions & 2 deletions src/commands/step/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ pub fn step_commit(
stage: Option<StageMode>,
show_prompt: bool,
dry_run: bool,
author: Option<String>,
) -> anyhow::Result<Option<CommitOutcome>> {
// --show-prompt and --dry-run skip hooks and the commit itself; --dry-run still
// mirrors --stage against a temp index so the previewed prompt matches what a real
Expand Down Expand Up @@ -56,7 +55,6 @@ pub fn step_commit(
options.hooks = hooks;
options.stage_mode = stage_mode;
options.show_no_squash_note = false;
options.author = author.as_deref();

let mut announcer = HookAnnouncer::new(ctx.repo, false);
let outcome = options.commit(&mut announcer)?;
Expand Down
9 changes: 2 additions & 7 deletions src/commands/step/squash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ pub fn handle_squash(
stage: Option<StageMode>,
announcer: &mut HookAnnouncer<'_>,
pre_approved_guidance: PreApprovedGuidance,
author: Option<&str>,
) -> anyhow::Result<SquashResult> {
// Load config once, run LLM setup prompt, then reuse config
let mut config = UserConfig::load().context("Failed to load config")?;
Expand Down Expand Up @@ -224,7 +223,7 @@ pub fn handle_squash(
sha,
message,
stage_mode,
} = generator.commit_staged_changes(&wt, true, true, stage_mode, author)?;
} = generator.commit_staged_changes(&wt, true, true, stage_mode)?;
return Ok(SquashResult::Squashed {
sha,
message,
Expand Down Expand Up @@ -348,11 +347,7 @@ pub fn handle_squash(
}

// Commit with the generated message
let mut commit_args = vec!["commit", "-m", commit_message.as_str()];
if let Some(author) = author {
commit_args.push("--author");
commit_args.push(author);
}
let commit_args = vec!["commit", "-m", commit_message.as_str()];
if let Err(err) = repo.run_command(&commit_args) {
return Err(restore_after_failed_reset(
repo,
Expand Down
65 changes: 1 addition & 64 deletions src/commands/worktree/push.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,27 +525,6 @@ pub fn handle_push(
// No-fast-forward merge
// ---------------------------------------------------------------------------

/// Split a `git commit --author`-style spec (`Name <email>`) into its parts.
/// `commit-tree` has no `--author` flag, so its author identity is set via
/// the separate `GIT_AUTHOR_NAME` / `GIT_AUTHOR_EMAIL` environment variables.
///
/// As lenient as `git commit --author` itself: takes the name up to the
/// first `<` and the email up to the *first* `>` after it (matching git's
/// own author-ident parser exactly, not just "closes eventually"), so
/// trailing whitespace or junk after that closing `>` — including another
/// literal `>` — doesn't reject a spec here that squash/commit already
/// accepted earlier in the same `wt merge` run.
fn split_author_spec(spec: &str) -> anyhow::Result<(&str, &str)> {
let invalid = || anyhow::anyhow!("Invalid --author \"{spec}\": expected `Name <email>`");
let (name, rest) = spec.split_once('<').ok_or_else(invalid)?;
let (email, _trailing) = rest.split_once('>').ok_or_else(invalid)?;
let (name, email) = (name.trim(), email.trim());
if name.is_empty() || email.is_empty() {
return Err(invalid());
}
Ok((name, email))
}

/// Merge to target branch using `--no-ff` (creates a merge commit).
///
/// Uses git plumbing (`commit-tree` + [`advance_target`]) to create a merge
Expand All @@ -558,7 +537,6 @@ pub fn handle_no_ff_merge(
target: Option<&str>,
operations: Option<MergeOperations>,
feature_branch: &str,
author: Option<&str>,
) -> anyhow::Result<PushResult> {
let ctx = MergeContext::prepare(target, operations)?;

Expand Down Expand Up @@ -601,18 +579,9 @@ pub fn handle_no_ff_merge(
if ctx.repo.signs_commits()? {
commit_tree_args.push("--gpg-sign");
}
// `commit-tree` has no `--author` flag; author identity is read from
// `GIT_AUTHOR_NAME` / `GIT_AUTHOR_EMAIL` instead. Committer is left
// unset, so it falls through to the ambient identity, matching
// `git commit --author`'s own committer behavior.
let author_env = author.map(split_author_spec).transpose()?;
let env: &[(&str, &str)] = match &author_env {
Some((name, email)) => &[("GIT_AUTHOR_NAME", name), ("GIT_AUTHOR_EMAIL", email)],
None => &[],
};
let merge_sha = ctx
.repo
.run_command_with_env(&commit_tree_args, env)
.run_command(&commit_tree_args)
.context("Failed to create merge commit")?
.trim()
.to_string();
Expand Down Expand Up @@ -648,38 +617,6 @@ mod tests {
use crate::commands::worktree::types::MergeOperations;
use worktrunk::testing::TestRepo;

#[test]
fn split_author_spec_matches_git_commit_authors_own_leniency() {
// As lenient as `git commit --author`: trailing whitespace or junk
// after the closing `>` doesn't reject a spec that squash/commit
// already accepted earlier in the same `wt merge --no-ff` run.
assert_eq!(
split_author_spec("Bot <bot@example.com>").unwrap(),
("Bot", "bot@example.com")
);
assert_eq!(
split_author_spec("Bot <bot@example.com> ").unwrap(),
("Bot", "bot@example.com")
);
assert_eq!(
split_author_spec("Bot <bot@example.com> extra").unwrap(),
("Bot", "bot@example.com")
);
// git's ident parser closes at the *first* `>`, so a stray `>` in
// the trailing junk is discarded too, not folded into the email.
assert_eq!(
split_author_spec("Bot <bot@example.com> trailing >").unwrap(),
("Bot", "bot@example.com")
);

assert!(split_author_spec("not-an-ident").is_err());
assert!(
split_author_spec("<bot@example.com>").is_err(),
"empty name"
);
assert!(split_author_spec("Bot <>").is_err(), "empty email");
}

/// `advance_target` moves the branch and worktree together while leaving
/// non-overlapping uncommitted changes — staged entries included — exactly
/// in place, and refuses atomically otherwise: an overlapping dirty file
Expand Down
Loading
Loading