77 Commits

Author SHA1 Message Date
Nicolas Le Cam 75dda6c097 fix(git-diff): count one truncated line as singular
A hunk truncated at exactly one line over the cap rendered
`... (1 deletions truncated)`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 12:41:51 +02:00
Nicolas Le Cam 7409cb5633 fix(git-diff): decode quoted paths, and read the header kind before splitting
Four findings on the section header and the word-diff guard:

- Under the default `core.quotepath` git escapes a non-ASCII path one octal
  byte at a time. Unwrapping the quotes without decoding them printed
  `\303\251t\303\251.txt`, so a grep for the file by name found nothing.
- A single-path `diff --cc` / `diff --combined` header was split at its
  midpoint like a two-path one, reading a file called `dup dup` as `dup`.
- Prefixes were matched by name, so `--dst-prefix` fell through and the
  header carried the whole pair. Matching the halves against each other
  reads any prefix, named or not.
- `--word-diff=none` is the mode that turns a word diff back off, leaving an
  ordinary unified diff. Treating it as a word diff passed the raw diff
  through, so a defensive `--word-diff=none` lost every saving. Modes are
  last-one-wins, which is what that mode is for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 12:41:51 +02:00
Ilia Alshanetsky e59276e5c1 fix(git-diff): pass word diffs through, and stop guessing at path pairs
Four more from an adversarial pass, two of them undermining the previous
commit's own rationale.

`--word-diff`, `--word-diff-regex` and `--color-words` drop the marker column
and put `[-removed-]` / `{+added+}` inline, so a word-diff body line is
arbitrary content in the position `compact_diff` reads as a marker. The byte
slicing stopped the panic, but not the misparse: a line starting `+` counts as
an addition and consumes the wrong budget, one starting `\` is dropped as a
no-newline annotation, and a real `git show --word-diff=plain` row reading
`diff --git [-a/src/main.rs...` opens a new file section. There is no faithful
compaction of a word diff, so `run_diff`, `run_show` and `git stash show -p`
pass these modes through, the way `gh pr diff --patch` already does for mbox.
`rtk pipe --filter git-diff` has no flags to read and still misparses word-diff
text piped into it; `compact_diff` reads a unified or combined diff, and that is
now what its callers give it.

A hunk header lists one range per marker column, and a mismatch was charged
against nothing. `@@@ -1 +1 @@@` left the second parent untracked, so ` -lost`
drove the tracked budgets to zero and the hunk closed with the removal
discarded. `@@@ -1 -1 -1 +0,0 @@@` declared a third parent with no column to
spend it, so that budget never reached zero, the hunk never closed by count, and
the mbox signature and version became its body. The ranges are reconciled with
the columns now: a missing one gets `usize::MAX`, which keeps the hunk open to
the next header rather than dropping content, and an extra one is dropped.

`diff_header_path` split on the first ` b/`, which is inside the path for a file
under a directory named `x b` -- `diff --git a/x b/y b/x b/y` reported `y`. And
`--no-prefix` or a custom `--src-prefix` leaves no ` b/` at all, so `diff --git
x x` reported both paths as the section header. Anything but a rename names the
same path twice, so the halves are the same length and the separating space sits
dead centre; splitting there handles all three and does not care what the
prefixes are. A rename's halves differ in length and still fall through to the
` b/` split.

Ordinary unified diffs are byte-identical to the previous head: verified on a
26,561-line real diff of this repo and on a 390-line one.
2026-09-01 12:41:51 +02:00
Ilia Alshanetsky eac9b996d7 fix(git-diff): slice markers as bytes, and bound a combined hunk by every parent
Four problems from the review, one of them a crash.

`&line[..width]` slices by byte index. `--word-diff`, `--color-words` and
`--word-diff-regex` emit body lines with no marker column, so their content
lands where the markers are read, and a leading multi-byte character split
mid-character: `rtk git diff --word-diff` aborted with exit 101 and empty stdout
on a file holding `école`. `run_diff`, `run_show` and `git stash show -p` have no
`catch_unwind`, so the panic was fatal there. The markers are ASCII by
construction, so comparing bytes removes the boundary question.

A combined diff carries one marker column per parent, and a space means
opposite things depending on the line: on a change line the line is in that
parent unchanged, on a removal line it is absent from it. Collapsing the columns
into one add/delete pair lost that, so `old` never converged on real conflict
output and the hunk never closed by count -- the declared-length termination
this commit's predecessor is built around silently did not apply to `diff --cc`,
which is what git emits for every unmerged path. The reverse case closed early
and dropped the body: `@@@ -1,2 -1,4 +1,2 @@@` over two removals from the second
parent alone rendered as a bare hunk header, with no tally and no truncation
note. `HunkHeader` now tracks a budget per parent and ends the hunk when every
one of them and the result are spent.

The leading-context ring drained only on a hunk's first change line, so a hunk
that ends without one discarded what it held and rendered as a bare header.
Flushing wherever the hunk closes -- and at end of input -- keeps it consistent
with the buffering, inside the same diff-wide budget.

`diff_header_path` mishandled git's quoted paths. Under the default
`core.quotepath`, git escapes a non-ASCII path and wraps it in `"`, which
removes the ` b/` the plain form splits on, so the fallback returned the whole
remainder -- both paths -- as the section header. The quoted form is handled
explicitly now, and the doc comment names the distinction the code makes: git
quotes non-ASCII bytes, not spaces.
2026-09-01 12:41:51 +02:00
Ilia Alshanetsky 844d6fa3d1 fix(git-diff): end hunks at their declared length, keep context adjacent
A hunk ran until the next header, so any trailing text was read as its body.
`gh pr diff --patch` routes here, and GitHub's `.patch` is an mbox: each patch
carries a bare `---` before the diffstat and a `-- ` signature after the diff,
both at column 0. With hunk bodies now anchoring, a two-commit mbox with two
real deletions reported `grep -c "^-"` = 5. `@@ -a,b +c,d @@` declares its own
line counts, so the hunk now ends when they are consumed and anything after it
is outside every hunk. That covers trailing prose and truncated input too,
rather than the shapes we happen to know about, and a heuristic on `---` / `-- `
could not have worked: a deleted line whose content is `- ` renders as `-- `.

`--patch` also joins `has_non_diff_format_flag`, so an mbox passes through
whole. Someone asking for a patch wants one that applies.

Leading context keeps the last lines before the change rather than the first.
With `-U10` or `--function-context` the first three sit ten lines above it, and
emitting those tells the reader that ctx3 precedes the deletion when ctx10 does.
A three-slot ring buffer costs the same lines. The diff-wide budget is charged
when the buffer drains, so a line the ring evicted costs nothing.

Combined diffs carry one marker column per parent, so a line changed against
only one of them holds its marker in column 2. Those were counted as context:
neither in the `+N -M` tally nor reachable by an anchored grep. The tally reads
both columns now. The grep gap is real and cannot be closed without rewriting
the line, so FEATURES.md states it alongside the per-hunk cap. Against
`git diff --numstat` on three complete commit ranges the tally is now exact,
where master under-counted one of them by +2 -1.

`diff_header_path` keeps paths containing spaces: git does not quote them, so
`diff --cc my file.txt` needs the whole remainder, not the last token.

FEATURES.md said 15 lines for a 24-line block and showed the stat's first line
with a leading space it does not have. It now also carries a measured example
with real compression, since the two-file one saves 11% in a document
advertising 60-90%.
2026-09-01 12:41:51 +02:00
Ilia Alshanetsky 5d4b5492cf fix(git-diff): reset hunk state on combined diffs, exempt leading context
Dropping the `+++` / `---` guards assumed those headers only appear before the
first `@@`. That holds for `diff --git` sections. It does not hold for the
`diff --cc` sections git emits for unmerged paths: `diff --cc` never matched
the `diff --git` branch, so `in_hunk` stayed set from the previous section and
the `--- a/z.txt` / `+++ b/z.txt` headers of every later file were printed at
column 0 and counted in the tally. On a two-file conflict with no deletions,
`rtk git diff | grep -c "^-"` returned 1. The state reset now fires on any
`diff --` prefix, and the path falls back to the trailing token for the
`diff --cc <path>` shape, so the headers land before the first `@@` again.
Same repo now returns 0.

Leading context no longer counts against `max_lines`, so it cannot displace
change lines. Its previous justification was wrong: before this branch, leading
context was never emitted, so it could not have crowded anything out of
`max_hunk_lines`. The cost it does carry is on the global budget, and on a
five-commit diff of this repo it cost 57 change lines (144 shown, against 201
with the exemption). A diff-wide cap of `max_lines / 10` bounds what the
exemption gives back: without it, a diff of many small hunks would spend three
exempt lines on every one of them. The same diff comes out at 71.7% byte
reduction against 71.8% on master, which showed zero anchorable change lines.

`docs/usage/FEATURES.md` showed change lines indented under a
`src/main.rs (+5/-2)` header, which no version of rtk emits. It now carries
real captured output, plus the two limits of the anchored audit: the 100-line
per-hunk cap, and that the output is not an applyable patch.
2026-09-01 12:41:51 +02:00
Ilia Alshanetsky 60ec79fc4d fix(git-diff): stop dropping hunk content that starts with ++ or --
The `+++` and `---` guards ran inside the `in_hunk` branch, where they can only
match content: git emits the file headers before the first `@@`, and `in_hunk`
resets to false on every `diff --git`. So an added `++i;` or a removed
`-- sql comment` was dropped from the body and left out of the `+N -M` tally.
A diff doing both rendered as a bare hunk header with no body and no tally, and
`grep -c '^-'` answered 0 for a file that really did lose a line.

Leading context now has its own budget of three lines. The `hunk_shown > 0`
guard silenced every context line before a hunk's first change, and giving it
`max_hunk_lines` instead would let a long run of context crowd out the change
it is there to frame.

The truncation note splits by sign: `... (30 deletions, 30 additions
truncated)`. It stays indented so the anchored greps this output is meant to
support never count it, which means a reader who trusts `grep -c '^-'` past the
100-line hunk cap needs the note to say how many deletions it did not see. One
merged line count could not.

The comment on the column-0 change narrated the bug's history and sat on the
`@@` branch while describing the branch below it. It now states the invariant,
once, where the code it governs lives.
2026-09-01 12:41:51 +02:00
Ilia Alshanetsky 4e811fbb4e fix(git-diff): emit hunk lines at column 0 so ^- anchors again
compact_diff indented every hunk-body line two spaces to nest it under the
filename. The content was all there, but the indent moved it off column 0, so
`git diff | grep '^-'` matched nothing and an audit asking "was anything
removed?" got a confident, wrong "no" while --stat reported deletions.

Worth being precise about the failure, because the earlier write-up was wrong
on both counts: nothing was ever dropped, and added lines were broken
identically (`^+` also matched nothing). It is not a deletions-only defect and
not content loss. It is one indent breaking every anchored grep.

Emit hunk bodies in git's own unified shape instead. Context lines already
carry git's leading space, so they stay distinct from additions. rtk's own
annotations (the `+N -M` tally, `... (N lines truncated)`) keep the indent
precisely so these same greps never count them as diff lines.

The output still cannot be mistaken for an applyable patch: rtk drops the
`diff --git`/`---`/`+++` headers, so a patch parser rejects it either way.
The indent was never what made that safe.
2026-09-01 12:41:51 +02:00
Nicolas Le Cam 29f9bb7161 Merge pull request #3575 from rtk-ai/fix/git-log-grep-value-misdetected-as-patch-flag
fix(git): don't misdetect a value-taking option's argument as a patch  flag
2026-08-20 13:17:36 +02:00
Nicolas Le Cam f8d636dde1 fix(git): restore -- before requests_raw_log_output in run_log
Every other run_* function that inspects args for "--" (run_diff,
run_checkout) calls args_utils::restore_double_dash() first to
re-insert the "--" that clap's trailing_var_arg strips out. run_log
never did, so requests_raw_log_output()/log_arg_tokens()'s own
take_while(|arg| *arg != "--") check was dead code in production:
by the time run_log saw args, the literal "--" was already gone.

Concretely, `rtk git log -- -p` lost its "--" and -p (a file
literally named -p, a valid pathspec) was misdetected as the real
patch flag, routing to raw passthrough instead of RTK's filtered
log. Added an end-to-end regression test (real rtk binary, real git
repo) alongside the existing git_log_patch_output_matches_raw_git
test, since restore_double_dash reads live process args and can't
be exercised by calling run_log's internals directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 09:18:22 +02:00
Nicolas Le Cam ca8976730a fix(git): git log --stat/--numstat/etc. weren't requesting raw passthrough
requests_raw_log_output only recognized -p/-u/--patch variants as needing
the raw path. --stat, --numstat, --name-only, --name-status, --raw,
--shortstat, --dirstat, and --summary change git's raw output shape the
same way -p does, but weren't listed — RTK's injected --pretty=format +
---END--- markers can't coexist with them, so the diffstat/name-list block
got misparsed as the start of the next commit, mangling output silently
instead of just leaving it unfiltered.

Also share one log_arg_tokens() pass across run_log's flag/limit checks
instead of retokenizing per check, and drop a redundant "-n" match arm
already covered by consumes_next_token_as_value.
2026-08-20 01:00:23 +02:00
Nicolas Le Cam 9bbf55cd07 fix(git): value/limit/format detection for git log misdetects --grep values as flags
has_limit_flag, has_format_flag, wants_merges, and parse_user_limit scanned
args positionally with no awareness that a value belonging to --grep,
--author, etc. can itself look like a flag (-5, --pretty, --merges),
reproducing the same misdetection class this branch already fixed for -p.

Unify these into a single log_arg_tokens tokenizer shared by
requests_raw_log_output, real_flag_args, and parse_user_limit, which also
stops at the -- pathspec separator so a literal path like -5 after -- isn't
misread as a flag.
2026-08-20 00:39:18 +02:00
Nicolas Le Cam 84169e27da fix(git): add --diff-algorithm/--diff-filter to value-consuming options
Cross-checked git-log(1)'s full option list against
consumes_next_token_as_value() (using git log <opt> <token> -1 against
real git 2.53.0 to see whether <token> gets swallowed as the option's
value or leaks through as a positional arg). --diff-algorithm and
--diff-filter both take a required, separate-token value
(`git log --diff-algorithm -p` rejects -p as an invalid algorithm
rather than treating it as the patch flag) but were missing from the
list, so a genuine -p right after either was misdetected as the
option's own value and the raw-patch request went undetected.

Every other bracket/optional-value option checked (--format, --pretty,
--stat, -M, -C, -B, etc.) is correctly attached-value-only and stays
out of the list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 00:08:07 +02:00
Nicolas Le Cam 1a1b306e31 fix(git): --max-parents/--min-parents also only take an attached value
Audited every option in consumes_next_token_as_value() against real
git 2.53.0 behavior (git log <opt> <token> -1, checking whether the
token gets swallowed as the option's value or leaks through as a
positional arg). --max-parents and --min-parents behave like -U:
`git log --max-parents 2` fails with "ambiguous argument '2'", so a
real -p right after them was being misread as their value.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 00:04:32 +02:00
Nicolas Le Cam 705a2f8a90 fix(git): -U, --unified, --expand-tabs don't take a separate-token value
These options only accept an attached value (-U3, --unified=3,
--expand-tabs=4) — verified against git 2.53.0, where
`git log --expand-tabs 4` fails with "fatal: ambiguous argument '4'"
instead of treating 4 as the value. consumes_next_token_as_value()
was swallowing the next token for them anyway, so a real -p right
after one of these was misread as their value and the raw patch
request went undetected.

Addresses review feedback from @aeppling on #3575.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 00:01:41 +02:00
Nicolas Le Cam 57872f8d7e Merge pull request #2717 from guyoron1/fix/windows-gbk-encoding
fix(core): decode process output using Windows console code page
2026-08-17 00:08:08 +02:00
guyoron1 f496f59b77 fix(core): decode per line, cover OEM code pages, and centralize on exec_capture
Addresses the inline review on #2717.

decode_process_output
- Decode a line at a time instead of reinterpreting the whole buffer at
  the first bad byte. Valid UTF-8 lines keep their bytes; only lines that
  fail UTF-8 validation go through the code page, so one stray byte no
  longer mangles output that was almost entirely UTF-8. The line is the
  unit because a byte run is not one: GB18030's four-byte sequences embed
  bytes in the ASCII digit range, so any rule that ends a run below 0x80
  splits them. \n cannot appear as a trail byte in any encoding handled
  here, and a process does not switch encoding mid-line.
- A code page result is only accepted when it decodes cleanly, so a UTF-8
  line with a corrupt byte falls back to lossy UTF-8 rather than mojibake.
- Replace the hand-written code page table with the codepage crate, as
  suggested. That also fixes 54936, which was mapped to GBK and now
  correctly resolves to gb18030.
- Add oem_cp for the legacy OEM/DOS pages (437, 850, 852, …) that plain
  cmd.exe still defaults to in many locales. encoding_rs implements only
  WHATWG encodings, so codepage alone returns None for them.
- Fall back to GetACP when GetConsoleOutputCP reports no console, which
  is the piped case rtk normally runs in, and warn once instead of
  falling back to lossy silently.
- Cache the code page lookup in a OnceLock.
- The mapping and the walk take the code page as a parameter, so they are
  compiled and unit-tested on every platform rather than only Windows.

Call sites
- Route the remaining production sites through stream::exec_capture and
  exec_capture_stdin rather than decoding at each one, so future callers
  inherit decoding. git commit keeps inherited stdin via the _stdin
  variant. Test-only sites go back to from_utf8_lossy: they assert on
  rtk's own UTF-8 output, where a console code page has no meaning.
- Decode the streamed path (read_lines_lossy) too — the OEM/ANSI lines
  its comment describes were still going straight to U+FFFD.
- curl keeps its body on from_utf8_lossy: a response body is a network
  payload whose encoding comes from the HTTP charset, not the local
  console, and non-UTF-8 bodies already take the binary passthrough for
  #1087. Only curl's own stderr is code page decoded.

git commit summary parsing
- parse_commit_output sliced from byte 1, which panics when the first
  line starts with a multi-byte character — git prints hook output before
  its summary, and a lossily decoded line starts with a multi-byte
  U+FFFD. Locate the bracket pair with find instead, so both indices are
  character boundaries.

Verified: unit tests for the walk, GBK, gb18030, CP437/850, mixed lines,
truncated input and every byte value; a test pinning that output without
a code page stays byte-identical to from_utf8_lossy; and the Windows-only
lookup cross-compiled for x86_64-pc-windows-msvc.
2026-08-15 08:20:24 +03:00
Nicolas Le Cam 3cc80b2433 fix(git): don't misdetect a value-taking option's argument as a patch flag
requests_raw_log_output() matched bare -p/-u/--patch tokens anywhere in
the args, without checking whether the previous token was an option like
--grep or -S that consumes the next token as its value. `git log --grep
-p` searches commit messages for the literal string "-p" (verified
against real git 2.53.0: no diff output, identical to --grep=-p) but RTK
treated it as a patch request and skipped its own filtering/limit,
dumping raw uncapped git log output for what is actually a plain grep
search.

Skip the value token after any known value-taking git log/diff option
before checking for the patch flags.
2026-08-15 04:01:34 +02:00
Nicolas Le Cam 40e4f3aac9 fix(git): respect -- pathspec separator in git log patch detection
requests_raw_log_output() scanned the full args slice for patch flags
like -p, so `git log -- -p` (a literal pathspec named -p) was
misdetected as a raw patch request and skipped RTK's normal filtering.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 01:40:30 +02:00
guyoron1 b35ff3a374 fix(core): route all child-process output decoding through decode_process_output
Review feedback (KuSh): the helper existed but most filter modules still
called String::from_utf8_lossy directly, so non-UTF-8 console output
(e.g. GBK on Chinese-locale Windows) was still mangled for most commands.

Wire decode_process_output through the remaining child-output call sites:
git, go, aws, curl (non-binary paths only — raw binary passthrough for
#1087 is untouched), system read, and discover registry. File-content
decoding (dotnet TRX XML, hook trust snippets) intentionally keeps
from_utf8_lossy since console code pages don't apply there.
2026-08-14 22:00:32 +03:00
kingpy-bot 0baf07e2ae fix(git): pass patch logs through unchanged 2026-07-11 22:35:01 +08:00
kenwoodjw bb01d6c8ba feat: add git checkout support 2026-07-08 11:07:35 +08:00
Adrien Eppling b619ecfb24 test(git): update stash summary tests for comma-free format 2026-07-03 14:57:47 +02:00
aesoft 6c3d81f970 no commas 2026-07-03 14:06:23 +02:00
Adrien Eppling 0ac5f5c311 fix(git): compress git stash show summary line
Shorten "N files changed, X insertions(+), Y deletions(-)" to "N changed, X +, Y -"; numbers preserved, no signal loss.
2026-07-03 13:39:50 +02:00
Adrien Eppling add35e0928 fix(git): compact git stash show instead of forcing -p
Strip the diffstat decorations and cap the file list; savings are measured against the real command, not an inflated -p patch.
2026-07-03 12:14:01 +02:00
aesoft 9a52647f65 Merge pull request #2499 from hgunduzoglu/fix/git-worktree-list-exit-code
fix(git): propagate exit code on git worktree list failure
2026-06-26 13:58:31 +02:00
aesoft 29272484de Merge pull request #2496 from hgunduzoglu/fix/git-commit-false-success
fix(git): propagate exit code when commit fails instead of reporting ok
2026-06-26 13:24:34 +02:00
aesoft a9b2ef5b3c Update git.rs 2026-06-26 13:16:32 +02:00
aesoft adaf2b259c Update git.rs 2026-06-26 13:13:37 +02:00
Husam c04dec1fe1 test(git): cover exit-code propagation for git stash list/show failure
develop already propagates the exit code for failed git stash list/show
(via the empty-stdout guard). Add regression tests so the masking-failure
behavior (#2497) can't creep back.
2026-06-26 13:16:24 +03:00
aesoft d86f0073ec Merge pull request #2498 from hgunduzoglu/fix/git-status-compact-exit-code
fix(git): propagate exit code on git status failure in compact path
2026-06-26 11:49:23 +02:00
Husam 8ef08cf6f0 refactor(git): trim verbose comments on commit outcome to a concise note 2026-06-26 12:04:25 +03:00
Husam d8e1428110 refactor(git): drop explanatory comment on worktree list failure guard
Per review (#2498): self-explanatory guard; remove comment to avoid noise.
2026-06-26 11:35:37 +03:00
Husam ee5a675f40 refactor(git): drop explanatory comment on status failure guard
Per review (#2498): the guard is self-explanatory; remove the comment
to avoid noise.
2026-06-26 11:35:00 +03:00
Adrien Eppling 861a46dee5 fix(core): never-worse output guard so RTK never exceeds the raw command
RTK could emit more tokens than the underlying command on small inputs:
filters that add headers, summaries, re-indentation, or a tee hint, plus
synthetic no-result messages ("0 matches", "No stashes", "[docker] 0
containers") printed where the raw command emitted nothing. Both break the
Transparency principle and inflate tokens instead of saving them.

- core::guard::never_worse(raw, filtered) returns raw when the filtered form
  has more tokens (reuses tracking::estimate_tokens), so RTK output is never
  larger than the real command.
- runner::emit_guarded(filtered, hint, raw) composes body + tee hint, guards
  the whole, prints, and returns what was shown so printed == tracked.
- run_captured_filter guards the run_filtered* family centrally; per-site
  guards cover the remaining single-string filters.
- On empty raw, emit empty and preserve the exit code instead of a synthetic
  no-result message (the messages were cosmetic with no dependents; #2461
  reports the grep one as actively harmful).
- git stash show now propagates its exit code instead of masking a real
  failure as Ok(0).

Resolves #2551.
2026-06-23 21:53:35 +02:00
Husam ebaaf8db58 fix(git): propagate exit code on git worktree list failure
run_worktree list mode never checked result.success() — a failed
`git worktree list` (e.g. run outside a repo) was flattened to empty
output + exit 0. The has_action branch already guards on success; apply
the same guard to list mode and surface git's error with its exit code.

Part of #2497
2026-06-18 22:55:50 +03:00
Husam 756c2a4ce8 fix(git): propagate exit code on git status failure in compact path
The compact status path only caught the "not a git repository" error and
let every other failure (corrupt index, lock contention, broken refs)
fall through to a formatted "Clean working tree" + Ok(0), masking a real
git error. The non-compact path already guards on result.success(); apply
the same guard to the compact path, keeping the friendly not-a-repo
message and propagating git's real exit code otherwise.

Part of #2497
2026-06-18 22:53:49 +03:00
Husam e36dd8cbe7 fix(git): propagate exit code when commit fails instead of reporting ok
run_commit had a branch that printed 'ok (nothing to commit)' and fell
through to Ok(0) whenever git exited non-zero with a 'nothing to commit'
message — so a no-op or hook-aborted commit was reported as success with
exit 0. Gate strictly on output.status.success() via a CommitOutcome
helper: any failure surfaces git's message and propagates the real exit
code, matching native git. Same bug class as #1581 (push) and #1535
(stash).

Fixes #2494
2026-06-18 22:22:36 +03:00
aesoft 0a630fe9ac Merge pull request #2289 from rtk-ai/refacto/strip-output-decorators
refacto(cmds): strip decorator noise from filter output
2026-06-05 18:31:39 +02:00
Adrien Eppling 16d6599b4c refacto(cmds): strip decorator noise from filter output
Separator lines (═══, ---) and an emoji status marker cost tokens
without adding signal for the LLM — RTK output must never add noise
over raw. Semantic labels are kept; the emoji is swapped for plain
monochrome unicode.
2026-06-05 17:41:53 +02:00
aesoft 4f4a6a02ae Merge pull request #1266 from shalk/fix/commit-multibyte-panic-rebased
fix(git): fix panic on multibyte chars in commit output
2026-06-03 17:20:40 +02:00
Nicolas Le Cam 83cd93e7d7 chore(args): introduce a more generic solution for restoring double dashes in args
Fixes #1669
2026-05-31 17:06:26 +02:00
aesoft 26c88907d9 Merge pull request #2015 from okwn/contrib/rtk/git-log-merge-fix
fix: honor explicit -n N limit for git log on merge commits
2026-05-23 10:11:11 +02:00
aesoft f431fb9521 Update git.rs 2026-05-22 15:30:14 +02:00
Adrien Eppling 7753e487b3 fix(git): drop -uall from compact status so output never exceeds raw
The compact `git status` path ran `git status --porcelain -b -uall`. The
`-uall` flag expands fully-untracked directories into every file, while raw
`git status` collapses them (e.g. `node_modules/`). This made rtk output
larger than raw — measured ~29x on a 200-file untracked dir (5500B vs 191B) —
violating RTK's compress-or-match-raw invariant and inflating tokens.

Remove `-uall` so untracked directories collapse exactly like raw. This keeps
#991's actual fix intact: all modified/staged/renamed/conflict paths are still
shown with no grouped summaries or `... +N more` overflow markers (`-uall`
never affected those lines). Untracked files in partially-tracked dirs and any
paths git itself expands are still preserved by the formatter.

Measured (rtk vs raw git status):
- node_modules/ (200 files): 5500B (-2780%) -> 25B (+87% savings)
- normal (8 mod + 2 untracked): +71% -> +76%
- 17 modified (#991 case): unchanged at +58%, all 17 shown, 0 overflow markers

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 10:13:00 +02:00
okwn b3adcb18ef fix: honor explicit -n N limit for git log on merge commits
When user runs 'git log -1 --format='%H' HEAD' where HEAD is a merge
commit, rtk was adding --no-merges which filtered out the merge commit
itself and returned the second parent instead. This made 'git log -1'
return wrong SHAs for merge commits.

Fix: don't add --no-merges when user explicitly passes -n N or
--max-count=N. When a user specifies an exact count they expect exactly
that many commits, not filtered results. Also skip --no-merges if user
already passed --merges or --no-merges explicitly.

Fixes rtk-ai/rtk#2009.
2026-05-21 10:16:09 +00:00
aesoft 9c80934dab Merge branch 'develop' into fix/tee-hint-trunc-quality 2026-05-21 10:27:00 +02:00
aesoft b6054a5fef refacto(truncations): Set global CAPS for truncation
Following tee and hint refacto
Add global CAP constant to be inherited , to enable easier global configuration later
2026-05-20 17:29:14 +02:00
aesoft 90c285c380 Merge pull request #1895 from rtk-ai/fix/aggressive-filters-batch
fix(filters): aggresivity batch fix
2026-05-19 09:15:39 +02:00