Skip to content
Draft
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
202 changes: 176 additions & 26 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,4 @@ at the [DEVELOPMENT.md](DEVELOPMENT.md) file.
<a href="https://github.com/gitbutlerapp/gitbutler/graphs/contributors">
<img src="https://contrib.rocks/image?repo=gitbutlerapp/gitbutler" />
</a>
test
4 changes: 4 additions & 0 deletions crates/but/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ legacy = [
"dep:gitbutler-branch",
"dep:gitbutler-reference",
"dep:gitbutler-oplog",
"dep:crossterm",
"dep:ratatui",
]

[dependencies]
Expand Down Expand Up @@ -102,6 +104,8 @@ gix.workspace = true
colored = "3.0.0"
serde_json.workspace = true
terminal_size = "0.4.3"
crossterm = { version = "0.28", optional = true }
ratatui = { version = "0.29", optional = true }
tracing.workspace = true
tracing-subscriber = { workspace = true, features = [
"env-filter",
Expand Down
1 change: 1 addition & 0 deletions crates/but/src/args/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub enum CommandName {
PublishReview,
ReviewTemplate,
Completions,
Lazy,
#[default]
Unknown,
}
19 changes: 19 additions & 0 deletions crates/but/src/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,25 @@ pub enum Subcommands {
target: String,
},

/// Launch the experimental Lazy TUI interface.
///
/// The Lazy TUI provides an interactive terminal interface for managing your
/// GitButler workspace, similar to lazygit but integrated with GitButler's
/// virtual branch workflow.
///
/// ## Features
///
/// - View and manage commits across branches
/// - Interactive commit operations (squash, reword, uncommit)
/// - File assignment and hunks management
/// - Oplog history navigation
/// - Upstream integration status
///
/// This is an experimental feature and may have rough edges.
#[cfg(feature = "legacy")]
#[clap(hide = true)]
Lazy,

/// Initializes a GitButler project from a git repository in the current directory.
///
/// If you have an existing Git repository and want to start using GitButler
Expand Down
2 changes: 2 additions & 0 deletions crates/but/src/command/legacy/describe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use gix::prelude::ObjectIdExt;

use crate::{CliId, IdMap, tui, utils::OutputChannel};

// new comment

pub(crate) fn describe_target(
project: &Project,
out: &mut OutputChannel,
Expand Down
2 changes: 2 additions & 0 deletions crates/but/src/command/legacy/forge/review.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ fn prompt_for_branch_selection(
return Ok(vec![]);
}

// another comment

// Display branches with numbers
println!("\nAvailable branches to publish:\n");
for (idx, (name, commit_count, reviews)) in all_branches.iter().enumerate() {
Expand Down
90 changes: 90 additions & 0 deletions crates/but/src/lazy/absorb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use anyhow::Result;

use super::app::{LazyApp, Panel};
use crate::{
OutputFormat,
command::legacy::absorb,
utils::OutputChannel,
};

impl LazyApp {
pub(super) fn open_absorb_modal(&mut self) {
if !matches!(self.active_panel, Panel::Status) {
self.command_log
.push("Switch to the Status panel to absorb changes".to_string());
return;
}

if !self.is_unassigned_header_selected() {
self.command_log
.push("Select 'Unassigned Files' to absorb all pending changes".to_string());
return;
}

if self.unassigned_files.is_empty() {
self.command_log
.push("No unassigned changes available to absorb".to_string());
return;
}

let (summary, _) = self.summarize_unassigned_files();
if summary.file_count == 0 {
self.command_log
.push("No unassigned changes available to absorb".to_string());
return;
}

self.absorb_summary = Some(summary.clone());
self.show_absorb_modal = true;
self.command_log.push(format!(
"Preparing to absorb {} unassigned file(s)",
summary.file_count
));
}

pub(super) fn cancel_absorb_modal(&mut self) {
self.reset_absorb_modal_state();
self.command_log.push("Canceled absorb".to_string());
}

pub(super) fn confirm_absorb_modal(&mut self) {
match self.perform_absorb() {
Ok(true) => self.reset_absorb_modal_state(),
Ok(false) => {}
Err(e) => self.command_log.push(format!("Failed to absorb: {}", e)),
}
}

fn perform_absorb(&mut self) -> Result<bool> {
if self.unassigned_files.is_empty() {
self.command_log
.push("No unassigned changes to absorb".to_string());
return Ok(false);
}

let project = gitbutler_project::get(self.project_id)?;
let mut out = OutputChannel::new_without_pager_non_json(OutputFormat::None);
self.command_log
.push("Running absorb on unassigned changes".to_string());
absorb::handle(&project, &mut out, None)?;

if let Some(summary) = self.absorb_summary.clone() {
self.command_log.push(format!(
"Absorbed {} file(s) (+{} / -{})",
summary.file_count, summary.total_additions, summary.total_removals
));
} else {
self.command_log
.push("Absorbed unassigned changes".to_string());
}

self.load_data_with_project(&project)?;
self.update_main_view();
Ok(true)
}

fn reset_absorb_modal_state(&mut self) {
self.show_absorb_modal = false;
self.absorb_summary = None;
}
}
Loading
Loading