v0.4.0 — Watch mode, dirty filter & man page - #5
Conversation
Add repo_is_dirty() and print_status_table() in display.c. The latter renders the header, the (optionally filtered) rows and the summary line. Under --dirty, clean+in-sync repos are hidden but still counted, and the summary appends "(N hidden)". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
watch.c: live in-place refresh of the status table on the alternate screen buffer using raw ANSI escapes and termios (no ncurses). Refreshes every N seconds (default 3, -w 10 to change), quits on q/Ctrl-C, and always restores the terminal on exit incl. SIGINT/SIGTERM. repo.c: free_repo_collection() so the watch loop can rescan each tick. main.c/gitools.h: -w/--watch [n] and --dirty parsing, opt_watch*, opt_dirty_only globals, validations (rejects non-tty and fetch/pull/-s), and the run_watch() branch. config.c: watch_interval and dirty_only keys (CLI overrides). Makefile: build watch.c. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
README, CHANGELOG and gitlsrc.example cover -w/--watch, --dirty and the new watch_interval/dirty_only config keys. integration.sh gains tests for the --dirty filter (hidden count, config dirty_only) and the watch-mode guards (non-tty, fetch combo, bad interval). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add keybindings to the watch loop so the whole tree can be acted on without leaving the live view: f fetch, p pull, s switch (prompts for a branch name in raw mode, with Backspace/Esc), r refresh now, q quit. Keys reuse the existing two-phase scan by toggling opt_fetch/opt_pull/ opt_switch for a single tick, then resetting them. process_all_repos() now runs the network phase silently under watch mode (spinner and inter-phase line gated on !opt_watch) so it doesn't corrupt the alternate-screen render; the loop shows its own transient progress and a footer note for the last action. main() resolves the git binary before entering watch mode so the fetch/pull keys work; git_available() exposes whether it was found. tests/unit.c gains the new globals so the unit binary links. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pressing 's' in watch mode now opens a picker listing the recently active branches across all scanned repos, ordered by most recent commit date. Type to filter, navigate with arrow keys (CSI decoding), and choose with Tab/Enter; Enter on a non-matching name still allows free-form input. collect_recent_branches()/free_recent_branches() in repo.c aggregate and de-duplicate local branches via libgit2, keeping the newest timestamp per name. The watch loop now frees the previous scan at the top of each tick so g_paths stays valid for the picker while waiting for input. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The picker no longer takes over the whole screen — it redraws in place at the anchor below the footer (clear-to-end, draw, cursor-up), like the old prompt, leaving the status table visible above it. The footer drops the 'last scan' clock in favour of the scanned directory. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
watch_pty.py drives gitls -w through a pseudo-terminal (interactive mode can't run under the plain-pipe integration harness): it checks alternate- screen enter/leave, cursor hide/show, the footer, the branch picker (recent-first ordering, drawn below the table), arrow+Tab selection, type-to-filter+Enter and Esc cancel. Wired into 'make test', skipped when python3 is unavailable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Long branch names (or any over-wide content) made a row exceed the terminal width and wrap, corrupting the table — and badly breaking the in-place redraw in watch mode. compute_col_widths() now shrinks the variable NAME/BRANCH columns to fit the terminal (write_col already truncates with '~'); piped output keeps full width so scripts are unaffected. Adds term_width() (ioctl TIOCGWINSZ, COLUMNS fallback) and ellipsize() for shortening over-long Scanned/footer paths with a leading ellipsis, plus unit tests for ellipsize. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
While an action runs the watch loop now spins the verb (Fetching all repos / Pulling clean repos / Switching to <branch>) using the existing spinner thread, instead of showing a static line. Over-long Scanned and footer paths are ellipsized to the terminal width. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The action frame no longer homes and clears the screen — that briefly blanked the table. The cursor already sits below the footer after the previous render, so the spinner now animates there with the table left intact above it; the table is refreshed in place once the action finishes (and the trailing clear-to-end trims any leftover rows). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When a refresh produced a narrower frame — e.g. switching to a shorter branch name shrinks the BRANCH column — the in-place redraw left the right-hand tail of the previous, wider rows on screen, showing a phantom second WHEN/STATUS column. Every rewritten line in watch mode now ends with an erase-to-end-of-line (\033[K) via a shared EOL() helper that is empty outside watch mode, so piped output stays clean. tests/watch_pty.py gains a tiny VT emulator and a regression test that renders the frames and asserts no stale columns remain after narrowing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (12)
📝 WalkthroughWalkthroughIntroduces ChangesWatch Mode and Dirty Filtering
Sequence DiagramsequenceDiagram
participant User
participant Main
participant Watch
participant Repo
participant Display
participant Terminal
User->>Main: run gitls -w
Main->>Watch: run_watch abs_dir
Watch->>Terminal: enable alternate screen and raw mode
loop refresh interval or key input
Watch->>Repo: find_repos and process_all_repos
alt key requests fetch pull or switch
Watch->>Repo: execute action
Watch->>Terminal: render spinner
end
Watch->>Display: compute widths and print status table
Watch->>Display: print footer
end
User->>Watch: press q or Ctrl-C
Watch->>Terminal: restore terminal state
Watch-->>Main: return
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (1)
README.md (1)
15-16: 📐 Maintainability & Code Quality | ⚡ Quick winDocumentation is comprehensive and accurate, but three fenced code blocks need language specifiers.
All feature descriptions, keybindings, config keys, defaults, and workflow explanations align precisely with the implementation. The dirty filter definition (staged/modified/untracked, ahead/behind, diverged, detached HEAD) matches
repo_is_dirty()in display.c exactly. The watch mode terminal behavior (alternate screen, restore on SIGINT/SIGTERM, raw ANSI) is well documented. However, three output examples lack language specifiers on their fenced code blocks (lines 168, 201, 237). Specifying a language improves content rendering by using the correct syntax highlighting for code.📝 Proposed fix: Add language specifiers to fenced code blocks
-Line 168: -``` +```text Scanned: /home/me/projects NAME BRANCH SYNC WHEN STATUS ... f fetch · p pull · s switch · r refresh · q quit interval 3s · /home/me/projects · switched to main -``` +``` -Line 201: -``` +```text ... status table ... f fetch · p pull · s switch · r refresh · q quit interval 3s · /home/me/projects switch all clean repos to: dev▏ ↑/↓ navigate · Tab/Enter select · Esc cancel ❱ develop hotfix -``` +``` -Line 237: -``` +```text gitls --dirty ~/projects NAME BRANCH SYNC WHEN STATUS ... 9 repos · 6 clean · 3 dirty · 1 behind (7 hidden) -``` +```Also applies to: 71-72, 95-102, 156-229, 230-249, 318-330
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@README.md` around lines 15 - 16, Three fenced code blocks in README examples are missing language specifiers; update the relevant triple-backtick fences (the examples showing the status table / watch mode and the gitls --dirty output) to include a language tag (e.g., ```text) so syntax highlighting renders correctly. Locate the blocks that currently start and end with plain ``` around the status-table/watch-mode snippets (the examples containing "Scanned: /home/me/projects", the "switch all clean repos to:" interactive snippet, and the "gitls --dirty ~/projects" output) and change their opening fences to ```text (also apply same change to the other nearby fenced blocks in the listed ranges to keep consistency). Ensure only the opening fence is changed to include the language specifier and close fences remain ``` as-is.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@config.c`:
- Around line 109-112: The watch_interval parsing branch (strcmp(key,
"watch_interval") ...) currently casts strtol() to int and only checks iv >= 1;
change it to parse into a long (e.g., long lv = strtol(val, &end, 10)), include
<errno.h>, check errno == ERANGE or *end != '\0' to reject invalid/overflowed
parses, then enforce bounds (ensure lv >= 1 and if lv > INT_MAX clamp to
INT_MAX) before assigning to opt_watch_interval (cast to int after clamping);
preserve the existing behavior for valid inputs and reject/ignore invalid ones.
In `@display.c`:
- Around line 17-26: The function term_width currently consults
getenv("COLUMNS") even when STDOUT is not a TTY, causing piped/scripted output
to be wrongly capped; change term_width so COLUMNS is only used when stdout is a
TTY (i.e., only after isatty(STDOUT_FILENO) succeeds), and return 0 immediately
when stdout is not a TTY; update the logic in the term_width function (refs:
term_width, isatty, ioctl/TIOCGWINSZ, getenv("COLUMNS"), ws.ws_col) to avoid
reading COLUMNS for non-TTY output.
In `@main.c`:
- Around line 175-177: The guard for watch mode only checks stdout TTY; update
the condition in main (the opt_watch block using isatty) to require both stdin
and stdout TTYs (use isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) and adjust
the fprintf message to indicate that -w requires an interactive terminal on both
stdin and stdout so redirected stdin won't bypass the check.
- Around line 65-66: Update the help/usage string in main.c to accurately
document the accepted options: add the long form "--watch" next to the short
"-w" entry (e.g. " -w, --watch [n] Watch mode: refresh the table every n
seconds (default: 3)\n") and restore the full description for the dirty option
to match behavior (e.g. " --dirty Only list repos that are not clean or
not in sync\n"); edit the same string literal/block that currently contains the
two lines shown so the printed --help matches the parser's accepted flags and
semantics.
- Around line 149-150: The current CLI parsing sets opt_dirty_only when
"--dirty" is present but never allows CLI to override a true value from
load_config(); update the argument handling so an explicit CLI decision wins:
either (A) add a negating flag (e.g., "--all" or "--no-dirty") and check
strcmp(argv[i], "--no-dirty") to set opt_dirty_only = false, or (B) track
whether the user supplied a dirty-related flag (e.g., a boolean
cli_dirty_specified) and apply CLI value after load_config(); modify the parsing
around strcmp(argv[i], "--dirty")/opt_dirty_only and ensure the help text
mentions the new opt-out flag, and add a regression test covering config with
dirty_only=true plus an explicit CLI opt-out to confirm CLI overrides config.
In `@tests/watch_pty.py`:
- Around line 179-187: The current Watcher.finish() calls os.waitpid(self.pid,
0) which can block forever; change it to a bounded wait: repeatedly call
os.waitpid(self.pid, os.WNOHANG) in a short loop up to a timeout (e.g. a few
seconds) checking for a non-zero pid return, sleeping briefly between polls; if
the timeout elapses and the child still exists, send a termination signal
(os.kill(self.pid, signal.SIGKILL) or SIGTERM first) and then reap it with
os.waitpid; update Watcher.finish to use os.WNOHANG, a timeout loop, and signal
handling (import signal) so the test cannot hang indefinitely.
In `@watch.c`:
- Around line 283-291: Install the cleanup hooks (call atexit(restore_terminal)
and set up sigaction with sa.sa_handler = on_signal for SIGINT and SIGTERM)
before making any terminal state changes; move the atexit and sigaction setup so
it runs prior to write_seq(ALT_SCREEN_ON), write_seq(CURSOR_HIDE), and
enter_raw_mode() to ensure restore_terminal runs on exit or signals.
---
Nitpick comments:
In `@README.md`:
- Around line 15-16: Three fenced code blocks in README examples are missing
language specifiers; update the relevant triple-backtick fences (the examples
showing the status table / watch mode and the gitls --dirty output) to include a
language tag (e.g., ```text) so syntax highlighting renders correctly. Locate
the blocks that currently start and end with plain ``` around the
status-table/watch-mode snippets (the examples containing "Scanned:
/home/me/projects", the "switch all clean repos to:" interactive snippet, and
the "gitls --dirty ~/projects" output) and change their opening fences to
```text (also apply same change to the other nearby fenced blocks in the listed
ranges to keep consistency). Ensure only the opening fence is changed to include
the language specifier and close fences remain ``` as-is.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b385fa17-a905-4c13-b97b-863e793332bc
📒 Files selected for processing (13)
CHANGELOG.mdMakefileREADME.mdconfig.cdisplay.cgitlsrc.examplegitools.hmain.crepo.ctests/integration.shtests/unit.ctests/watch_pty.pywatch.c
- main.c: add --no-dirty so an explicit CLI flag can override dirty_only from the config (the 'CLI overrides config' contract was one-way); fix --help to document -w/--watch and --no-dirty and clarify --dirty; require a TTY on stdin as well as stdout for -w (interactive raw input). - config.c: validate watch_interval like the -w CLI path — parse as long, reject ERANGE and clamp to INT_MAX (add <errno.h>). - display.c: term_width() returns 0 for non-TTY stdout instead of reading COLUMNS, so piped/scripted output keeps full width as documented. - watch.c: install atexit/sigaction cleanup hooks before switching to the alternate screen, so a signal in that window still restores the terminal. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
watch_pty.py Watcher.finish() now reaps with a bounded WNOHANG loop and SIGTERM/SIGKILL fallback so a stuck child can't hang the suite. Adds an integration test that --no-dirty overrides dirty_only=true from the config. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reorganise the README from a flat, addition-ordered list of equal-weight feature sections into a value-first layout: short pitch, a table of contents, Why/Install/Quick start, then Watch mode as the highlighted hero section (interactive keys, branch picker, behaviour notes). Fold fetch/pull/switch into a single 'Acting on all repos' section, centralise the status-table column and indicator reference, and move the full --help dump down into a 'Reference' section. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a troff man page covering the synopsis, subcommands, all options (incl. -w/--watch, --dirty/--no-dirty), watch-mode keys and branch picker, status indicators, configuration keys and environment. Lints clean with mandoc. Makefile: install/uninstall the page to $(MANDIR) as $(INSTALL_NAME).1 and honour DESTDIR for all install paths (binary, doc, man) so staged packaging works. RPM spec: package the man page and gitlsrc.example. README Reference section points at 'man gitls'. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bump version touchpoints for the 0.4.0 tag: Makefile fallback (used for tarball builds where 'git describe' is unavailable, e.g. Homebrew), RPM spec Version + changelog, CHANGELOG release date, and the Homebrew formula URL. The formula sha256 is filled in after the release tarball exists. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Target release: v0.4.0 (minor — backward-compatible features).
Two headline features for inspecting and acting on many repos at once, plus
robustness fixes, a restructured README and a man page.
✨ Watch mode (
-w/--watch)-w 10to change).ffetch,ppull,sswitch,rrefresh now,q/Ctrl-C quit. A spinner animates the action verb and the table stays on screen while it runs.sopens a branch picker: recently-active branches (most recent first), type-to-filter, ↑/↓ navigation, Tab/Enter to choose — drawn in place below the table.SIGINT/SIGTERM. Noncursesdependency — raw ANSI escapes +termiosonly. Requires an interactive terminal (stdin + stdout).🩹 Dirty filter (
--dirty/--no-dirty)(N hidden). Works one-shot and under-w.--no-dirtyopts out ofdirty_onlyfrom the config for a single run.⚙️ Config
~/.gitlsrckeyswatch_intervalanddirty_only; CLI flags override them.🐛 Fixes
📖 Docs & packaging
--helpmoved to a Reference section).gitls(1)man page;make install/uninstallhonourDESTDIR; RPM spec and Homebrew formula updated.✅ Testing
make test→ 27 unit + 45 integration + 17 PTY, clean build with-Wall -Wextra.tests/watch_pty.pydrives watch mode through a pseudo-terminal (with a tiny VT emulator), including a regression test for the stale-columns bug.Remaining for the 0.4.0 tag
v0.4.0, then bumpVersioninpackaging/gitls.specand theurl/sha256inpackaging/gitls.rbto the release tarball (sha256 only computable from the published artifact).🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
-w/--watch) with live in-place table refresh and interactive controls (fetch, pull, branch switching, and filtering)--dirty) to display only repositories that are not clean and in-sync~/.gitlsrcBug Fixes
Documentation