Skip to content
Closed
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
9 changes: 8 additions & 1 deletion crates/but/src/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -781,9 +781,9 @@ pub enum Subcommands {
///
/// This command will:
/// - Add the repository to the global GitButler project registry
/// - Switch to the gitbutler/workspace branch (if not already on it)
/// - Set up a default target branch (the remote's HEAD)
/// - Add a gb-local remote if no push remote exists
/// - Optionally enter the temporary legacy gitbutler/workspace mode with --workspace
///
/// If you have an existing Git repository and want to start using GitButler
/// with it, you can run this command to set up the necessary configuration
Expand All @@ -807,6 +807,13 @@ pub enum Subcommands {
#[clap(long)]
#[cfg_attr(feature = "raw-clap-docs", clap(verbatim_doc_comment))]
init: bool,
/// Enter the temporary legacy gitbutler/workspace mode after setup.
///
/// This is a compatibility escape hatch for commands that still require managed
/// workspace mode. The long-term direction is that commands should work without it.
#[clap(long)]
#[clap(verbatim_doc_comment)]
workspace: bool,
},

/// Exit GitButler mode and return to normal Git workflow.
Expand Down
126 changes: 53 additions & 73 deletions crates/but/src/command/legacy/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ struct TargetInfo {
newly_set: bool,
}

/// Display a colorful splash screen with GitButler branding and helpful commands
fn display_splash_screen(out: &mut dyn std::fmt::Write) -> anyhow::Result<()> {
/// Display a colorful splash screen with GitButler branding and helpful commands.
fn display_splash_screen(out: &mut dyn std::fmt::Write, workspace: bool) -> anyhow::Result<()> {
let t = theme::get();
writeln!(out)?;
writeln!(
Expand Down Expand Up @@ -92,12 +92,21 @@ fn display_splash_screen(out: &mut dyn std::fmt::Write) -> anyhow::Result<()> {
t.command_suggestion.paint("$ but push"),
t.hint.paint("Push all branches")
)?;
writeln!(
out,
"{:<45} {}",
t.command_suggestion.paint("$ but teardown"),
t.hint.paint("Return to normal Git mode")
)?;
if workspace {
writeln!(
out,
"{:<45} {}",
t.command_suggestion.paint("$ but teardown"),
t.hint.paint("Return to normal Git mode")
)?;
} else {
writeln!(
out,
"{:<45} {}",
t.command_suggestion.paint("$ but setup --workspace"),
t.hint.paint("Enter temporary legacy workspace mode")
)?;
}
writeln!(out)?;

writeln!(
Expand Down Expand Up @@ -176,21 +185,12 @@ pub(crate) fn repo(
ctx: &mut Context,
repo_path: &Path,
out: &mut OutputChannel,
workspace: bool,
perm: &mut RepoExclusive,
) -> anyhow::Result<()> {
let t = theme::get();
let mut target_info: Option<TargetInfo> = None;

// what branch is head() pointing to?
let pre_head_name = {
let repo = ctx.repo.get()?;
let pre_head = repo.head()?;
pre_head
.referent_name()
.map(|n| n.shorten().to_string())
.unwrap_or_default()
};

// find or setup the gitbutler project
if let Some(out) = out.for_human() {
writeln!(
Expand Down Expand Up @@ -334,9 +334,10 @@ pub(crate) fn repo(
};

drop(repo);
but_api::legacy::virtual_branches::set_base_branch_with_perm(
let target_ref: gix::refs::FullName = format!("refs/remotes/{name}").try_into()?;
but_api::branch::set_default_target_with_perm(
ctx,
name.clone(),
target_ref.as_ref(),
Some(remote_name.clone()),
perm,
)?;
Expand Down Expand Up @@ -393,61 +394,45 @@ pub(crate) fn repo(
}
}

let head_name = {
let repo = ctx.repo.get()?;
let head = repo.head()?;
head.referent_name()
.map(|n| n.shorten().to_owned())
.unwrap_or_default()
};
if workspace {
if let Some(out) = out.for_human() {
writeln!(out)?;
writeln!(
out,
"{}",
t.hint
.paint("→ Entering temporary legacy managed workspace mode")
)?;
}

// switch to gitbutler/workspace if not already there
if !head_name.starts_with(b"gitbutler/") {
but_api::legacy::virtual_branches::switch_back_to_workspace_with_perm(ctx, perm)?;
}

// Install managed hooks to prevent accidental git commits
if let Ok(repo) = ctx.repo.get()
&& let Err(e) = gitbutler_repo::managed_hooks::install_managed_hooks(&repo)
&& let Some(out) = out.for_human()
{
writeln!(
out,
" {}",
t.attention.paint(format!(
"Warning: Failed to install GitButler managed hooks: {e}"
))
)?;
}

// if we switched - tell the user what this is all about
if pre_head_name != "gitbutler/workspace"
&& let Some(out) = out.for_human()
{
writeln!(
out,
"{}",
t.attention.paint(format!(
r#"
Setting up your project for GitButler tooling. Some things to note:

- Switching you to a special `gitbutler/workspace` branch to enable parallel branches
- Installing Git hooks to help manage commits on the workspace branch

To undo these changes and return to normal Git mode, either:

- Directly checkout a branch (`git checkout {pre_head_name}`)
- Run `but teardown`

More info: https://docs.gitbutler.com/workspace-branch
"#
))
)?;
if let Some(out) = out.for_human() {
writeln!(
out,
" {}",
t.success.paint("✓ Switched to gitbutler/workspace")
)?;
writeln!(
out,
" {}",
t.success.paint("✓ Installed managed workspace hooks")
)?;
writeln!(out)?;
writeln!(
out,
"{}",
t.attention.paint(
"This is a temporary compatibility mode. The long-term direction is that no command should require it."
)
)?;
writeln!(out)?;
}
}

// Display splash screen for human output
if let Some(out) = out.for_human() {
display_splash_screen(out)?;
display_splash_screen(out, workspace)?;
}

// Output JSON if requested
Expand All @@ -471,19 +456,14 @@ More info: https://docs.gitbutler.com/workspace-branch
/// - if the project is registered in GitButler
/// - if there is a remote
/// - if there is a default target branch set
/// - if we're on a gitbutler/* branch
pub fn check_project_setup(ctx: &Context, perm: &RepoShared) -> anyhow::Result<bool> {
let (repo, ws, _) = ctx.workspace_and_db_with_perm(perm)?;

// check if we're on a gitbutler/* branch
let head = repo.head()?;
let head_name = head
.referent_name()
.map(|n| n.shorten().to_owned())
.unwrap_or_default();
if !head_name.starts_with(b"gitbutler/") {
anyhow::bail!("Not currently on a gitbutler/* branch.");
}

// When on gitbutler/edit, the project was already set up when entering edit mode.
// The workspace graph built from gitbutler/edit doesn't expose the target ref or
Expand Down
2 changes: 1 addition & 1 deletion crates/but/src/command/legacy/status/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ fn print_hint(
let has_uncommitted_files = !status_ctx.worktree_changes.is_empty();

let hint_text = if not_on_workspace {
"Hint: run `but setup` to switch back to GitButler managed mode."
"Hint: run `but setup --workspace` to switch back to GitButler managed mode."
} else if !status_ctx.has_branches {
"Hint: run `but branch new` to create a new branch to work on"
} else if has_uncommitted_files {
Expand Down
6 changes: 5 additions & 1 deletion crates/but/src/command/legacy/teardown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,11 @@ pub(crate) fn teardown(
)?;
writeln!(out)?;
writeln!(out, "{}", t.info.paint("To return to GitButler mode, run:"))?;
writeln!(out, " {}", t.command_suggestion.paint("but setup"))?;
writeln!(
out,
" {}",
t.command_suggestion.paint("but setup --workspace")
)?;
writeln!(out)?;
}

Expand Down
16 changes: 11 additions & 5 deletions crates/but/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1190,7 +1190,7 @@ async fn match_subcommand(
.map_err(CliError::from)
}
#[cfg(feature = "legacy")]
Subcommands::Setup { init } => {
Subcommands::Setup { init, workspace } => {
let repo =
match but_api::legacy::projects::add_project_best_effort(args.current_dir.clone())?
{
Expand All @@ -1206,10 +1206,16 @@ async fn match_subcommand(
};
let mut ctx = but_ctx::Context::from_repo(repo)?;
let mut guard = ctx.exclusive_worktree_access();
command::legacy::setup::repo(&mut ctx, &args.current_dir, out, guard.write_permission())
.context("Failed to set up GitButler project.")
.emit_metrics(metrics_ctx)
.map_err(CliError::from)
command::legacy::setup::repo(
&mut ctx,
&args.current_dir,
out,
workspace,
guard.write_permission(),
)
.context("Failed to set up GitButler project.")
.emit_metrics(metrics_ctx)
.map_err(CliError::from)
}
#[cfg(feature = "legacy")]
Subcommands::Teardown { checkout_to } => {
Expand Down
4 changes: 3 additions & 1 deletion crates/but/src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ pub fn init_ctx(
&mut ctx,
&args.current_dir,
out,
false,
guard.write_permission(),
)?;
// Retry finding the project after setup
Expand Down Expand Up @@ -160,6 +161,7 @@ pub fn init_ctx(
&mut ctx,
&args.current_dir,
out,
false,
guard.write_permission(),
)?;
// Re-find and re-check the project after setup
Expand Down Expand Up @@ -424,7 +426,7 @@ fn prompt_for_setup(out: &mut OutputChannel, message: &str) -> SetupPromptResult
if let Some(mut inout) = out.prepare_for_terminal_input() {
_ = writeln!(
progress,
"In order to manage projects with GitButler, we need to do some changes:\n\n - Switch you to a special `gitbutler/workspace` branch to enable parallel branches\n - Install Git hooks to help manage the tooling\n\nYou can go back to normal git workflows at any time by either:\n\n - Running `but teardown`\n - Manually checking out a normal branch with `git checkout <branch>`\n",
"In order to manage projects with GitButler, we need to configure this repository:\n\n - Add it to the GitButler project registry\n - Configure a default target branch\n\nTo enter the temporary legacy managed workspace mode later, run:\n\n - `but setup --workspace`\n",
);

if let Ok(Confirm::Yes) =
Expand Down
Loading
Loading