Skip to content

Conversation

@schacon
Copy link
Member

@schacon schacon commented Aug 27, 2025

No description provided.

schacon added 29 commits August 27, 2025 08:06
Adding a but config that by default shows some of the important config 
Values like name and email.
Introduce an explicit "Undo" subcommand (alias: undo) and an "Oplog" subcommand (alias: oplog) in the CLI to expose operation history and restore functionality. Add the new gitbutler-oplog workspace, wire the new modules into main, and implement a new undo module that finds the previous snapshot, acquires exclusive worktree access, and restores the repository to that snapshot. This change was needed to replace a generic "revert" behavior with a clearer "but restore"/undo flow so users can inspect the oplog and reliably roll back the last operation.
Log: add --short option to display branch topology with commit counts

Add a --short (-s) flag to the Log subcommand to provide a concise view of the branch topology that shows only branch names and the number of commits (upstream vs local) instead of full commit details. This was needed to support a compact, high-level overview mode for users who only want topology and commit counts.

Changes:
- Add short: bool to the Log CLI subcommand.
- Extend commit_graph signature to accept a short flag and route Log to the new signature.
- Implement commit_graph_short to render branch topology with commit counts and simplified symbols, and return early when short is requested.
The status output now prints base information, then iterates stacks to show each branch as a section containing upstream/local commits and its assigned files, followed by an Unassigned Changes section for files not assigned to any stack. This reorganizes the previous single print_group into focused helpers (print_base_info, print_branch_sections, print_unassigned_section) and groups assignments by file so the UI matches the requested layout.
Make base/behind calculations and display conditional on the -b/--base flag so the status command only shows base branch and behind counts when explicitly requested. This avoids unnecessary computation and clutter in the default status output.

Also update status rendering to correctly connect stacked branches visually: compute prefixes and connectors, track nesting and branch counts, and print closing connectors so stacked branches appear as a connected graph rather than independent entries. Command argument parsing and call sites were adjusted to pass the new flag into the status worktree function.
Show assigned files only on top branch and fix graph gaps

Prevent assigned files from appearing on every stacked branch by only printing assignments for the first (topmost) branch in a stack. Files are associated with the stack rather than individual branches, so this change filters assignments by stack_id only for the first branch and avoids duplicating file listings across stacked branches.

Also simplify connector logic, tighten prefix/connector printing, and fix graph closing/spacing so connectors and nesting lines render correctly (fill gaps and ensure proper trailing lines between stacks).
Show stack-assigned files above commit list

Show assigned files for the stack before printing the branch commit list so that files (which are associated with the stack, not individual branches) appear above the commits for the topmost branch. This reorders the output: assigned files are displayed first (only for the stack's first branch) and commits (upstream then local) are shown afterwards, improving clarity of the status view.
Fix branch graph ASCII and show base commit

Adjust the branch graph printing to match the requested ASCII layout and include the repository base commit. Changes include: cloning project path where needed, passing project to print_branch_sections, correcting the final nesting line from └─╯ to ├─╯, and appending code to compute and print the 7-char base SHA as “● <sha> (base)”. These updates ensure the visual stack/tree output matches the prompt and that the graph explicitly shows the common merge base.
Fix broken graph rendering for second branch

Correct the stack-closing logic in status display to avoid incorrect nesting and a broken graph for the second branch. Remove unused mutable nesting, simplify the closure behavior for last vs non-last stacks, and ensure proper blank-line separation so the rendered graph lines (like "├─╯") appear correctly for the second branch.
Add a new branch subcommand and implementation to create virtual branches. This introduces a `Branch::New` clap subcommand (with optional base id) and a new module that can create either an empty virtual branch or a stacked branch based on an existing branch id. The change wires the new command into main, adds required workspace/cargo entries for gitbutler-reference, and includes small formatting/cleanup fixes in status output for consistent formatting and line breaks.
Avoid showing the same branch/stack twice in `but status` by filtering out applied stacks early and deduplicating stacks by ID. Build a HashSet of applied ref names for efficient lookup, skip any meta entries that are applied, and filter out UI stack entries with duplicate IDs. When listing all stacks, merge unapplied and applied stacks and deduplicate using a HashMap keyed by stack ID so each stack appears only once in the output.
better targeting
All unassigned files and all files assigned to the stack will be 
committed to the first stack. If there is more than one stack, list out 
the stacks and prompt the user which one they want to commit to. You can
do `but commit -m 'my cache testing changes'` with a `-m` or `--message`
to supply a message on the command line, or if you don't, it will open 
$EDITOR or read the `core.editor` git config and launch that editor with
a tempfile for you to supply the message. In that tempfile it will list 
all files to be committed starting with a # as a comment, which it will 
strip back out again to use as the message.
Fix commit implementation
Add unassigned test file
Fix but commit to handle modified files with hunk headers
Introduce a new subcommand `New` (alias `new`) and corresponding CLI parsing to allow inserting a blank commit before a specified commit or at the top of a stack when given a branch. This implements a new module crates/but/src/new/mod.rs which resolves the target commit/branch, locates the containing stack, and calls gitbutler_branch_actions::insert_blank_commit with appropriate offsets to create the blank commit. The change wires the command into args, main dispatch, and registers metrics for the new operation.
When displaying commit lists in but status and but log, blank commit messages were shown as empty lines, making output ambiguous. Introduce format_commit_message helper functions in both log and status modules that return the first line of the commit message or "(blank message)" when the first line is empty. Replace existing inline logic with calls to this helper to ensure consistent, readable output for commits with empty messages.
The next_help_heading attributes on Subcommands were unused; remove them to clean up args.rs. Replace the hardcoded grouped help text in print_grouped_help() with logic that builds groups from an array and pulls each subcommand's oneline description from the clap spec (via CommandFactory). This avoids duplicating descriptions, centralizes subcommand metadata, and makes group ordering explicit.
Avoid hardcoding a MISC list by automatically collecting and printing any
subcommands that aren't included in the explicit groups. This adds a
HashSet to track printed commands, filters out hidden commands, and
prints remaining commands under a MISC heading so help output stays
accurate and requires less manual maintenance.
Provide shorthand 'but st' as an alias for the Status command so users can type the shorter alias instead of the full 'but status'. This improves CLI ergonomics by matching common git shorthand and making the command easier to type.

- Add #[clap(alias = "st")] to the Status variant in args.rs to register the alias with clap.
Add a --files / -f option to the status command to list files modified in each commit, each annotated with a CliId shortcode for rubbing.

This change was needed so users can inspect per-commit file changes directly from `but status -f` and reference files by a stable shortcode. It adds the CLI flag, threads the flag through to status.worktree, implements file-id creation in id::mod, computes per-commit file diffs using the repository via git2, and prints colored status characters and underlined shortcode IDs when the flag is enabled.
Ensure all CliId representations use only letters g–z by replacing the previous base-36 (0-9, a-z) encoding with a base-20 alphabet of "ghijklmnopqrstuvwxyz". This change updates the hash() conversion to use 20 characters, uses the new hash() output when formatting Commit and Unassigned IDs (writing "gg" for Unassigned), and matches prefixes against the hashed oid instead of the raw SHA. These adjustments are needed to enforce the requirement that CliId values contain no digits or letters a-f.
Treat the unassigned CLI ID as the special area "00" instead of the previous "gg". This change maps CliId::Unassigned to the canonical "00" representation when formatted, restoring the intended special-area handling after it was lost.
Distinguish committed files from uncommitted/unassigned ones by adding a CommittedFile variant to CliId, generating hashes that include the commit OID so identical paths in different commits get unique shortcodes. Update display in status to use the committed file IDs, and add handling in rub to reject or explicitly bail on unsupported operations involving committed files (extracting/moving between commits/branches) with clear error messages. This was needed so files with the same path but different contexts (unassigned, assigned to a branch, or present in a specific commit) receive distinct short IDs and to prevent invalid rub operations against committed files.
Hide the duplicate StatusFiles subcommand (alias "stf") from the public CLI help while keeping its functionality. The subcommand provides an overview with modified files in each commit (equivalent to `status -f`) and a `--base`/`-b` flag to show base branch and behind count information.

This change adds a hidden StatusFiles variant to the CLI args and wires it into main so it still executes status::worktree(..., true). Hiding the alias prevents it from appearing in help output while preserving behavior for scripts or advanced users.
Add plumbing to allow uncommitting files from commits via the `rub` subcommand so the UI's "uncommit" action (alt-click) can call into the CLI. This includes:

- Add status::all_committed_files to enumerate files inside commits across stacks/branches so CLI id completion and lookup can find committed-file ids.
- Wire committed-file ids into id resolution paths so `rub <committed-file> -> Unassigned` is routed to the new handler.
- Add a rub::uncommit::file_from_commit stub that prints intent and returns a not-yet-implemented error (safe fallback while UI integration is enabled).
- Update help grouping to expose `rub` in the STACK OPERATIONS group and register the new module.

This change was needed to enable the UI "uncommit" option to invoke the CLI; it provides id resolution and a clear placeholder for the actual Git operations that will be implemented later.
Increase shortcode space by keeping the first character restricted to letters g–z but allowing the second character to be digits or lowercase letters (0–9, a–z). Previously both characters were chosen from g–z (20 options each); the new scheme uses 20 options for the first char and 36 options for the second, producing a much larger set of unique shortcodes.

This change updates the ID generation logic in crates/but/src/id/mod.rs: it computes the first character from the g–z set, then derives the second character from the 0–9+a–z set and formats the two-character shortcode. The modification is needed to provide a bigger range of shortcodes while preserving the original first-character constraint.
Support identifying branches and commits by branch name (exact or partial) and by partial SHA prefixes in addition to the existing CliId lookup. This change adds helper functions to find branches by name and commits by SHA prefix, extends the CliId.from_str resolution to try branch-name and SHA matching before/alongside existing prefix/exact CliId matching, and de-duplicates results. It also improves ambiguity error messages in rub to include branch names and suggest using longer SHAs or full branch names to disambiguate.
Clarify why a commit might not be found and suggest running 'but status' to refresh state. Provide more context in errors for source/target lookups, and include the short commit id in squash/undo errors so users know which commit is affected. Also wrap stack lookup errors with commit context to help diagnose failures when commits were removed by Git operations like squash or rebase.
Add simple colored status decorations to commits to indicate whether a commit is remote-only (R), pushed/both (P), or local-only (L). This makes it easier to visually distinguish commits that exist only upstream, those already pushed and present locally, and local-only commits. The legend is printed in the branch section, upstream commits are marked R or P depending on presence locally, and local commits skip ones already shown upstream and are marked L.
Introduce a new 'Unapply' branch subcommand and implement unapply_branch to unapply a virtual branch (or branch by CLI ID/full name). This change adds the CLI variant, wires the handler into main, and implements logic to resolve CLI IDs, locate the associated stack, call unapply_stack, and print user-facing messages. Along the way, several formatting and minor refactors were applied in status and args modules (function signature formatting, wrapping long calls, JSON output shaping, and whitespace cleanup) to keep code style consistent.
The branch command failed to create a stacked branch from an existing virtual branch. This change implements creating a new virtual branch based on the target branch by locating the target stack, creating a BranchCreateRequest, and calling create_virtual_branch. It removes unused imports and the previous create_virtual_branch_from_branch/unapply_stack usage. Note: proper stacking relationships and full integration with the stacking system remain TODO and will require further work.
@vercel
Copy link

vercel bot commented Aug 27, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
gitbutler-components Ready Ready Preview Comment Aug 27, 2025 3:11pm
gitbutler-web Ready Ready Preview Comment Aug 27, 2025 3:11pm

Make newly created virtual branches properly stack on the selected target branch's head instead of just creating an unlinked branch. The change looks up the target stack and its details, determines the appropriate head commit (local or upstream), creates the new virtual branch, and then sets the new branch's head to the target branch's head using VirtualBranchesHandle and the repository accessor so the stacking relationship is established.
@Caleb-T-Owens Caleb-T-Owens changed the title sc-but-config-1 sc-but-exp-1 Aug 27, 2025
When attempting to assign a file that is locked by hunks on other commits, the code previously printed a debug-style rejection list and then misleadingly reported success. Instead, fail fast with a user-friendly error that explains the file is locked to other commit(s) and suggests using git to modify commits or move the changes. This prevents confusing output and makes the failure reason clear.
Add an -o/--only option to the commit command so only files assigned to the target stack are committed and unassigned files are excluded when requested. This was needed to provide finer control over commits (commit only assigned files) instead of always including unassigned files.

Changes:
- CLI: add --only (-o) boolean flag to commit args.
- Commit logic: respect the only flag and skip adding unassigned files when true.
- Main wiring: pass the only flag from CLI into commit handler.
Remove the --stack option and accept an optional branch ID or name to derive the stack to commit to. This lets users run commands like `but commit -m "message" x4` or `but commit my-branch-name`.

Changes:
- CLI: replace `--stack`/-s with a positional/optional `branch` argument.
- commit API: rename `stack_hint` to `branch_hint` and thread it through callers.
- Selection logic: update select_stack to accept a CommandContext, match exact branch names first, then attempt to parse CLI IDs and resolve branch CLI IDs to stacks, and improve error messages to reference branches.
- Main: pass `branch` through to the commit handler.
Committing to a stacked branch was unstacking it because the commit routine let the parent be auto-detected, which could detach the stack. This change finds the intended target branch (honoring an optional branch hint via exact name or CLI id parsing), uses that branch's tip as the explicit parent for create_commit_simple, and thus preserves the stack relationship.

- Resolve target stack and branch selection: try branch hint first, fall back to stack HEAD.
- Parse CLI branch hints to match branch names when needed.
- Pass branch tip as parent_id to the simple commit engine to avoid unstacking.
Ensure each `but rub` command records an oplog entry by creating a snapshot before performing state-changing operations. The change imports oplog types and adds a helper create_snapshot that uses the project's exclusive worktree access and CommandContext::create_snapshot. Snapshot creation calls (with appropriate OperationKind values) are added across all relevant match arms (moves, assigns, amends, undo, squash, etc.). Errors from snapshot creation are ignored to avoid interrupting command execution.
Ensure "but undo" only reverts the most recent non-restore command instead of stepping back multiple times. Previously the code always inspected the second snapshot which could point to a restore operation and cause undo to jump back further; now we fetch more snapshots, skip the current one, and select the first snapshot whose operation is not a RestoreFromSnapshot. If no such snapshot exists, we print a message and exit. This prevents undo from reverting more than the last actual user operation.
Simplify the undo command to use the oplog head (the last operation before the current state) as the target to restore. The previous logic fetched many snapshots and attempted to skip restore operations to find a prior non-restore operation; this was more complex than necessary. Fetch only the last snapshot and treat it as the target, remove unused OperationKind import and the verbose current-operation printout. This makes undo behavior clearer and reduces snapshot traversal complexity.
The undo command was intended to restore the second-most recent operation (i.e., step back one more change), but it only listed one snapshot and selected the most recent. Change to request two snapshots and validate that at least two exist, then select snapshots[1] as the target. This ensures the command restores the previous oplog entry instead of the current head.
❯ but st
╭  jy [my-stacked-branch4]
│  lz A t.md
│
● L rg 6f9a7dd hite
● L vy 31f83d6 test
● L qr 07f46fb move network info
│
│
● b2f17f4 (base)

00 Unassigned Changes
l9 M hello_world.rb
g6 A t1.md
rj A t2.md
ix A t3.md
you can run but rub g6-ix jy and it will rub all three of those files. or but rub g6,ix jy and it will only rub those two.

Rust Undo Command Simplified: Snapshot Restoration
Allow commands to accept multiple source identifiers by parsing ranges (start-end), comma-separated lists, or single IDs. Previously the ids() function enforced exactly one source and returned a single CliId; now it returns a Vec<CliId> for sources and a single target. This change adds parse_sources, parse_range, and parse_list helpers, uses status/committed lists to resolve ranges, and improves error messages for not-found or ambiguous items.
Use the same display order as the status command when resolving a start..end file range. The previous implementation searched an unordered file list (and fell back to committed files), which could omit files that appear between the endpoints in the UI. This change adds get_all_files_in_display_order() to build the file list in the exact order shown by status (grouping assignments by file, iterating assigned files per stack, then unassigned files) and uses it to compute ranges. This ensures ranges include all files shown between the endpoints in the status view.
Allow the oplog command to return JSON when the json flag is set. Previously the json parameter was unused; this change wires the flag into show_oplog, prints an empty JSON array when no snapshots are found, and emits pretty-printed JSON for the snapshots. When not in JSON mode, the existing human-readable, colorized output is preserved. Additionally, minor refactoring fixes variable indentation and ensures snapshot details are handled correctly for both output modes.
Add key/value handling to the Config subcommand so `but config <key> [value]` can get or set configuration values (e.g., user.name/user.email). Implement config::handle to dispatch: setting writes to the local repo config, getting uses existing lookup logic (including user.name/email helpers) and prints JSON when requested; fall back to the original show() behavior when no key is provided. Update CLI args to accept optional key and value and wire the new handler into main.

This change was needed so that commands like `but config user.name <name>` actually set the name and `but config user.name` returns the effective value using the same lookup order as other config reads. It preserves existing behavior for showing all configuration and improves UX by supporting direct get/set operations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants