mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-25 10:33:21 +03:00
refactor cmd
This commit is contained in:
parent
f23ff295fc
commit
f0f615c150
49
src-tauri/Cargo.lock
generated
49
src-tauri/Cargo.lock
generated
@ -182,13 +182,13 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.68"
|
||||
version = "0.1.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
|
||||
checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -565,7 +565,7 @@ dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -850,7 +850,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -929,7 +929,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.10.0",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -951,7 +951,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
|
||||
dependencies = [
|
||||
"darling_core 0.20.1",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1424,7 +1424,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1638,6 +1638,7 @@ name = "git-butler-tauri"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"clap",
|
||||
"colored 2.0.0",
|
||||
@ -3019,7 +3020,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3421,9 +3422,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.60"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
|
||||
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -3439,9 +3440,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.28"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
||||
checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -3651,7 +3652,7 @@ dependencies = [
|
||||
"quote",
|
||||
"refinery-core",
|
||||
"regex",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4191,7 +4192,7 @@ checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4213,7 +4214,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4262,7 +4263,7 @@ dependencies = [
|
||||
"darling 0.20.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4553,9 +4554,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.18"
|
||||
version = "2.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
|
||||
checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -5053,7 +5054,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5161,7 +5162,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5284,7 +5285,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5610,7 +5611,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -5644,7 +5645,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"syn 2.0.26",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
@ -58,6 +58,7 @@ colored = "2.0.0"
|
||||
dirs = "5.0.1"
|
||||
dialoguer = "0.10.4"
|
||||
diffy = "0.3.0"
|
||||
async-trait = "0.1.71"
|
||||
|
||||
[features]
|
||||
# by default Tauri runs in production mode
|
||||
|
@ -1,465 +1,9 @@
|
||||
use std::process::ExitCode;
|
||||
|
||||
use clap::Parser;
|
||||
use colored::Colorize;
|
||||
use dialoguer::{console::Term, theme::ColorfulTheme, Input, MultiSelect, Select};
|
||||
|
||||
use git2::Repository;
|
||||
mod cli;
|
||||
|
||||
use git_butler_tauri::{
|
||||
database, gb_repository, project_repository, projects, reader, sessions, storage, users,
|
||||
virtual_branches::{self, list_virtual_branches},
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Cli {
|
||||
command: String,
|
||||
}
|
||||
|
||||
struct ButlerCli {
|
||||
path: String,
|
||||
local_data_dir: String,
|
||||
project: projects::Project,
|
||||
gb_repository: gb_repository::Repository,
|
||||
sessions_db: sessions::Database,
|
||||
}
|
||||
|
||||
impl ButlerCli {
|
||||
fn from(path: &str, local_data_dir: &str) -> Self {
|
||||
let storage = storage::Storage::from_path(local_data_dir);
|
||||
let users_storage = users::Storage::new(storage.clone());
|
||||
|
||||
let projects_storage = projects::Storage::new(storage);
|
||||
let projects = projects_storage.list_projects().unwrap();
|
||||
let project = projects.into_iter().find(|p| p.path == path).unwrap();
|
||||
|
||||
let gb_repository = gb_repository::Repository::open(
|
||||
local_data_dir,
|
||||
project.id.clone(),
|
||||
projects_storage,
|
||||
users_storage,
|
||||
)
|
||||
.expect("failed to open repository");
|
||||
|
||||
let db_path = std::path::Path::new(&local_data_dir).join("database.sqlite3");
|
||||
let database = database::Database::open(db_path).unwrap();
|
||||
let sessions_db = sessions::Database::new(database);
|
||||
|
||||
Self {
|
||||
path: path.to_string(),
|
||||
local_data_dir: local_data_dir.to_string(),
|
||||
project,
|
||||
gb_repository,
|
||||
sessions_db,
|
||||
}
|
||||
}
|
||||
|
||||
fn project_repository(&self) -> project_repository::Repository {
|
||||
project_repository::Repository::open(&self.project).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// setup project repository and gb_repository
|
||||
let local_data_dir = find_local_data_dir().unwrap();
|
||||
let path = find_git_directory().unwrap();
|
||||
|
||||
let butler = ButlerCli::from(&path, &local_data_dir);
|
||||
|
||||
let args = Cli::parse();
|
||||
match args.command.as_str() {
|
||||
"info" => run_info(butler), // shows internal data states for the project
|
||||
"status" => run_status(butler), // shows virtual branch status
|
||||
"new" => run_new(butler), // create new empty virtual branch
|
||||
"move" => run_move(butler), // move file ownership from one branch to another
|
||||
"setup" => run_setup(butler), // sets target sha from remote branch
|
||||
"commit" => run_commit(butler), // creates trees from the virtual branch content and creates a commit
|
||||
"branches" => run_branches(butler),
|
||||
"remotes" => run_remotes(butler),
|
||||
"flush" => run_flush(butler), // artificially forces a session flush
|
||||
"reset" => run_reset(butler), // sets all vbranches to unapplied
|
||||
"clear" => run_clear(butler), // deletes all vbranch stuff
|
||||
_ => println!("Unknown command: {}", args.command),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_clear(butler: ButlerCli) {
|
||||
// make sure there is a session
|
||||
let session = butler
|
||||
.gb_repository
|
||||
.get_or_create_current_session()
|
||||
.expect("failed to get or create currnt session");
|
||||
let session_reader = sessions::Reader::open(&butler.gb_repository, &session)
|
||||
.expect("failed to open current session reader");
|
||||
let branch_writer = virtual_branches::branch::Writer::new(&butler.gb_repository);
|
||||
let iterator =
|
||||
virtual_branches::Iterator::new(&session_reader).expect("failed to read branches");
|
||||
for branch in iterator.flatten() {
|
||||
branch_writer
|
||||
.delete(&branch)
|
||||
.unwrap_or_else(|e| panic!("failed to delete branch: {:?}", e));
|
||||
}
|
||||
}
|
||||
|
||||
fn run_reset(butler: ButlerCli) {
|
||||
// get the branch to commit
|
||||
let current_session = butler
|
||||
.gb_repository
|
||||
.get_or_create_current_session()
|
||||
.expect("failed to get or create currnt session");
|
||||
let current_session_reader = sessions::Reader::open(&butler.gb_repository, ¤t_session)
|
||||
.expect("failed to open current session reader");
|
||||
|
||||
let virtual_branches = virtual_branches::Iterator::new(¤t_session_reader)
|
||||
.expect("failed to read virtual branches")
|
||||
.collect::<Result<Vec<virtual_branches::branch::Branch>, reader::Error>>()
|
||||
.expect("failed to read virtual branches")
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let writer = virtual_branches::branch::Writer::new(&butler.gb_repository);
|
||||
for mut branch in virtual_branches {
|
||||
println!("resetting {:?}", branch);
|
||||
branch.applied = false;
|
||||
writer.write(&branch).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_remotes(butler: ButlerCli) {
|
||||
let branches =
|
||||
virtual_branches::remote_branches(&butler.gb_repository, &butler.project_repository())
|
||||
.unwrap();
|
||||
for branch in branches {
|
||||
println!("{:?}", branch);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_flush(butler: ButlerCli) {
|
||||
println!("Flushing sessions");
|
||||
butler.gb_repository.flush().unwrap();
|
||||
}
|
||||
|
||||
fn run_branches(butler: ButlerCli) {
|
||||
let branches = list_virtual_branches(&butler.gb_repository, &butler.project_repository(), true)
|
||||
.expect("failed to list branches");
|
||||
for branch in branches {
|
||||
println!("{}", branch.id.red());
|
||||
println!("{}", branch.name.red());
|
||||
for file in branch.files {
|
||||
println!(" {}", file.path.display().to_string().blue());
|
||||
for hunk in file.hunks {
|
||||
println!("--");
|
||||
println!(" {}", hunk.diff.green());
|
||||
println!("--");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_commit(butler: ButlerCli) {
|
||||
// get the branch to commit
|
||||
let current_session = butler
|
||||
.gb_repository
|
||||
.get_or_create_current_session()
|
||||
.expect("failed to get or create currnt session");
|
||||
let current_session_reader = sessions::Reader::open(&butler.gb_repository, ¤t_session)
|
||||
.expect("failed to open current session reader");
|
||||
|
||||
let virtual_branches = virtual_branches::Iterator::new(¤t_session_reader)
|
||||
.expect("failed to read virtual branches")
|
||||
.collect::<Result<Vec<virtual_branches::branch::Branch>, reader::Error>>()
|
||||
.expect("failed to read virtual branches")
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut ids = Vec::new();
|
||||
let mut names = Vec::new();
|
||||
for branch in virtual_branches {
|
||||
ids.push(branch.id);
|
||||
names.push(branch.name);
|
||||
}
|
||||
let selection = Select::with_theme(&ColorfulTheme::default())
|
||||
.items(&names)
|
||||
.default(0)
|
||||
.interact_on_opt(&Term::stderr())
|
||||
.unwrap();
|
||||
let commit_branch = ids[selection.unwrap()].clone();
|
||||
println!("Committing virtual branch {}", commit_branch.red());
|
||||
|
||||
// get the commit message
|
||||
let message: String = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Commit message")
|
||||
.interact_text()
|
||||
.unwrap();
|
||||
virtual_branches::commit(
|
||||
&butler.gb_repository,
|
||||
&butler.project_repository(),
|
||||
&commit_branch,
|
||||
&message,
|
||||
)
|
||||
.expect("failed to commit");
|
||||
}
|
||||
|
||||
fn run_new(butler: ButlerCli) {
|
||||
let input: String = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("New branch name")
|
||||
.interact_text()
|
||||
.unwrap();
|
||||
|
||||
virtual_branches::create_virtual_branch(
|
||||
&butler.gb_repository,
|
||||
&virtual_branches::branch::BranchCreateRequest {
|
||||
name: Some(input),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.expect("failed to create virtual branch");
|
||||
}
|
||||
|
||||
fn run_move(butler: ButlerCli) {
|
||||
let all_hunks =
|
||||
virtual_branches::get_status_by_branch(&butler.gb_repository, &butler.project_repository())
|
||||
.expect("failed to get status files")
|
||||
.into_iter()
|
||||
.flat_map(|(_branch, files)| {
|
||||
files
|
||||
.into_iter()
|
||||
.flat_map(|file| {
|
||||
file.hunks
|
||||
.into_iter()
|
||||
.map(|hunk| hunk.id)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let selected_files: Vec<String> = MultiSelect::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Which hunks do you want to move?")
|
||||
.items(&all_hunks)
|
||||
.interact()
|
||||
.expect("failed to get selections")
|
||||
.iter()
|
||||
.map(|i| all_hunks[*i].clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let current_session = butler
|
||||
.gb_repository
|
||||
.get_or_create_current_session()
|
||||
.expect("failed to get or create currnt session");
|
||||
let current_session_reader = sessions::Reader::open(&butler.gb_repository, ¤t_session)
|
||||
.expect("failed to open current session reader");
|
||||
|
||||
let virtual_branches = virtual_branches::Iterator::new(¤t_session_reader)
|
||||
.expect("failed to read virtual branches")
|
||||
.collect::<Result<Vec<virtual_branches::branch::Branch>, reader::Error>>()
|
||||
.expect("failed to read virtual branches")
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let selection = Select::with_theme(&ColorfulTheme::default())
|
||||
.items(
|
||||
&virtual_branches
|
||||
.iter()
|
||||
.map(|b| b.name.clone())
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.default(0)
|
||||
.interact_on_opt(&Term::stderr())
|
||||
.unwrap();
|
||||
|
||||
let target_branch = virtual_branches[selection.unwrap()].clone();
|
||||
let mut ownership = target_branch.ownership.clone();
|
||||
ownership.put(
|
||||
&selected_files
|
||||
.join("\n")
|
||||
.try_into()
|
||||
.expect("failed to convert to ownership"),
|
||||
);
|
||||
|
||||
virtual_branches::update_branch(
|
||||
&butler.gb_repository,
|
||||
virtual_branches::branch::BranchUpdateRequest {
|
||||
id: target_branch.id,
|
||||
ownership: Some(ownership),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.expect("failed to update branch");
|
||||
}
|
||||
|
||||
// TODO: vbranches: split function that identifies part of a file and moves that hunk to another branch
|
||||
// - writes the ownership simply as: path/to/file:line_number-line_number
|
||||
|
||||
fn run_setup(butler: ButlerCli) {
|
||||
println!(
|
||||
" HEAD: {}",
|
||||
butler
|
||||
.project_repository()
|
||||
.get_head()
|
||||
.unwrap()
|
||||
.name()
|
||||
.unwrap()
|
||||
.blue()
|
||||
);
|
||||
let items = butler.project_repository().git_remote_branches().unwrap();
|
||||
|
||||
let selection = Select::with_theme(&ColorfulTheme::default())
|
||||
.items(&items)
|
||||
.default(0)
|
||||
.interact_on_opt(&Term::stderr())
|
||||
.unwrap();
|
||||
|
||||
match selection {
|
||||
Some(index) => {
|
||||
println!("Setting target to: {}", items[index].red());
|
||||
butler
|
||||
.gb_repository
|
||||
.set_target_branch(&butler.project_repository(), &items[index])
|
||||
.unwrap();
|
||||
}
|
||||
None => println!("User did not select anything"),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_status(butler: ButlerCli) {
|
||||
let statuses =
|
||||
virtual_branches::get_status_by_branch(&butler.gb_repository, &butler.project_repository())
|
||||
.expect("failed to get status by branch");
|
||||
for (branch, files) in statuses {
|
||||
if branch.applied {
|
||||
println!(" branch: {}", branch.name.blue());
|
||||
println!(" head: {}", branch.head.to_string().green());
|
||||
println!(" tree: {}", branch.tree.to_string().green());
|
||||
println!(" id: {}", branch.id.green());
|
||||
println!("applied: {}", branch.applied.to_string().green());
|
||||
println!(" files:");
|
||||
for file in files {
|
||||
println!(" {}", file.path.display().to_string().yellow());
|
||||
for hunk in file.hunks {
|
||||
println!(" {}", hunk.id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!(" branch: {}", branch.name.blue());
|
||||
println!("applied: {}", branch.applied.to_string().green());
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
// notes:
|
||||
//let head = self.git_repository.head()?;
|
||||
//let tree = head.peel_to_tree()?;
|
||||
|
||||
// just print information for the project
|
||||
fn run_info(butler: ButlerCli) {
|
||||
println!("path: {}", butler.path.yellow());
|
||||
println!("data_dir: {}", butler.local_data_dir.yellow());
|
||||
|
||||
// find the project in project storage that matches the cwd
|
||||
println!("{}", "project:".to_string().red());
|
||||
println!(" id: {}", butler.project.id.blue());
|
||||
println!(" title: {}", butler.project.title.blue());
|
||||
println!(
|
||||
" description: {}",
|
||||
butler
|
||||
.project
|
||||
.description
|
||||
.clone()
|
||||
.unwrap_or("none".to_string())
|
||||
.blue()
|
||||
);
|
||||
println!(
|
||||
" project_data_last_fetched: {:?}",
|
||||
butler.project.project_data_last_fetched
|
||||
);
|
||||
println!(
|
||||
" project_gitbutler_data_last_fetched: {:?}",
|
||||
butler.project.gitbutler_data_last_fetched
|
||||
);
|
||||
println!(" path: {}", butler.project.path.blue());
|
||||
|
||||
if let Some(api) = butler.project.api.as_ref() {
|
||||
println!(" {}:", "api".to_string().red());
|
||||
println!(" api name: {}", api.name.blue());
|
||||
println!(" repo id: {}", api.repository_id.blue());
|
||||
println!(" git url: {}", api.git_url.blue());
|
||||
println!(" created: {}", api.created_at.blue());
|
||||
println!(" updated: {}", api.updated_at.blue());
|
||||
}
|
||||
|
||||
println!("{}", "project repo:".to_string().red());
|
||||
println!(
|
||||
" HEAD: {}",
|
||||
butler
|
||||
.project_repository()
|
||||
.get_head()
|
||||
.unwrap()
|
||||
.name()
|
||||
.unwrap()
|
||||
.blue()
|
||||
);
|
||||
|
||||
println!("{}", "sessions:".to_string().red());
|
||||
// sessions storage
|
||||
let sessions = butler
|
||||
.sessions_db
|
||||
.list_by_project_id(&butler.project.id, None)
|
||||
.unwrap();
|
||||
//list the sessions
|
||||
for session in &sessions {
|
||||
println!(" id: {}", session.id.blue());
|
||||
}
|
||||
|
||||
// gitbutler repo stuff
|
||||
// read default target
|
||||
|
||||
let current_session = butler
|
||||
.gb_repository
|
||||
.get_or_create_current_session()
|
||||
.expect("failed to get or create currnt session");
|
||||
let current_session_reader = sessions::Reader::open(&butler.gb_repository, ¤t_session)
|
||||
.expect("failed to open current session reader");
|
||||
|
||||
let target_reader = virtual_branches::target::Reader::new(¤t_session_reader);
|
||||
match target_reader.read_default() {
|
||||
Ok(target) => {
|
||||
println!("{}", "target:".to_string().red());
|
||||
println!(" base sha: {}", target.sha.to_string().blue());
|
||||
}
|
||||
Err(reader::Error::NotFound) => {}
|
||||
Err(e) => panic!("failed to read default target: {}", e),
|
||||
};
|
||||
|
||||
println!("{}", "virtual branches:".to_string().red());
|
||||
virtual_branches::Iterator::new(¤t_session_reader)
|
||||
.expect("failed to read virtual branches")
|
||||
.collect::<Result<Vec<virtual_branches::branch::Branch>, reader::Error>>()
|
||||
.expect("failed to read virtual branches")
|
||||
.into_iter()
|
||||
.for_each(|branch| {
|
||||
println!(" {}", branch.name);
|
||||
for file in branch.ownership.to_string().lines() {
|
||||
println!(" {}", file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn find_git_directory() -> Option<String> {
|
||||
match Repository::discover("./") {
|
||||
Ok(repo) => {
|
||||
let mut path = repo
|
||||
.workdir()
|
||||
.map(|path| path.to_string_lossy().to_string())
|
||||
.unwrap();
|
||||
path = path.trim_end_matches('/').to_string();
|
||||
Some(path)
|
||||
}
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_local_data_dir() -> Option<String> {
|
||||
let mut path = dirs::config_dir().unwrap();
|
||||
path.push("com.gitbutler.app.dev");
|
||||
Some(path.to_string_lossy().to_string())
|
||||
fn main() -> ExitCode {
|
||||
cli::Butler::parse().run()
|
||||
}
|
||||
|
98
src-tauri/src/bin/cli/butler/app.rs
Normal file
98
src-tauri/src/bin/cli/butler/app.rs
Normal file
@ -0,0 +1,98 @@
|
||||
use anyhow::{Context, Result};
|
||||
use git2::Repository;
|
||||
|
||||
use git_butler_tauri::{
|
||||
database, gb_repository, project_repository, projects, sessions, storage, users,
|
||||
};
|
||||
|
||||
pub struct App {
|
||||
path: String,
|
||||
local_data_dir: String,
|
||||
project: projects::Project,
|
||||
gb_repository: gb_repository::Repository,
|
||||
sessions_db: sessions::Database,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new() -> Result<Self> {
|
||||
let path = find_git_directory().context("failed to find project directory")?;
|
||||
let local_data_dir = find_local_data_dir().context("could not find local data dir")?;
|
||||
|
||||
let storage = storage::Storage::from_path(local_data_dir.clone());
|
||||
let users_storage = users::Storage::new(storage.clone());
|
||||
|
||||
let projects_storage = projects::Storage::new(storage);
|
||||
let projects = projects_storage
|
||||
.list_projects()
|
||||
.context("failed to list projects")?;
|
||||
|
||||
let project = projects
|
||||
.into_iter()
|
||||
.find(|p| p.path == path)
|
||||
.context("failed to find project")?;
|
||||
|
||||
let gb_repository = gb_repository::Repository::open(
|
||||
local_data_dir.clone(),
|
||||
project.id.clone(),
|
||||
projects_storage,
|
||||
users_storage,
|
||||
)
|
||||
.context("failed to open repository")?;
|
||||
|
||||
let db_path = std::path::Path::new(&local_data_dir).join("database.sqlite3");
|
||||
let database = database::Database::open(db_path).context("failed to open database")?;
|
||||
let sessions_db = sessions::Database::new(database);
|
||||
|
||||
Ok(Self {
|
||||
path,
|
||||
local_data_dir,
|
||||
project,
|
||||
gb_repository,
|
||||
sessions_db,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &str {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn local_data_dir(&self) -> &str {
|
||||
&self.local_data_dir
|
||||
}
|
||||
|
||||
pub fn project(&self) -> &projects::Project {
|
||||
&self.project
|
||||
}
|
||||
|
||||
pub fn sessions_db(&self) -> &sessions::Database {
|
||||
&self.sessions_db
|
||||
}
|
||||
|
||||
pub fn project_repository(&self) -> project_repository::Repository {
|
||||
project_repository::Repository::open(&self.project).unwrap()
|
||||
}
|
||||
|
||||
pub fn gb_repository(&self) -> &gb_repository::Repository {
|
||||
&self.gb_repository
|
||||
}
|
||||
}
|
||||
|
||||
fn find_git_directory() -> Option<String> {
|
||||
match Repository::discover("./") {
|
||||
Ok(repo) => {
|
||||
let mut path = repo
|
||||
.workdir()
|
||||
.map(|path| path.to_string_lossy().to_string())
|
||||
.unwrap();
|
||||
path = path.trim_end_matches('/').to_string();
|
||||
Some(path)
|
||||
}
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_local_data_dir() -> Option<String> {
|
||||
let mut path = dirs::config_dir().unwrap();
|
||||
path.push("com.gitbutler.app.dev");
|
||||
Some(path.to_string_lossy().to_string())
|
||||
}
|
38
src-tauri/src/bin/cli/butler/commands/branches.rs
Normal file
38
src-tauri/src/bin/cli/butler/commands/branches.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Args;
|
||||
use colored::Colorize;
|
||||
|
||||
use git_butler_tauri::virtual_branches;
|
||||
|
||||
use crate::cli::butler::app::App;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct Branches {}
|
||||
|
||||
impl super::RunCommand for Branches {
|
||||
fn run(self) -> Result<()> {
|
||||
let app = App::new().context("Failed to create app")?;
|
||||
|
||||
let branches = virtual_branches::list_virtual_branches(
|
||||
app.gb_repository(),
|
||||
&app.project_repository(),
|
||||
true,
|
||||
)
|
||||
.context("failed to list branches")?;
|
||||
|
||||
for branch in branches {
|
||||
println!("{}", branch.id.red());
|
||||
println!("{}", branch.name.red());
|
||||
for file in branch.files {
|
||||
println!(" {}", file.path.display().to_string().blue());
|
||||
for hunk in file.hunks {
|
||||
println!("--");
|
||||
println!(" {}", hunk.diff.green());
|
||||
println!("--");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
32
src-tauri/src/bin/cli/butler/commands/clear.rs
Normal file
32
src-tauri/src/bin/cli/butler/commands/clear.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Args;
|
||||
|
||||
use git_butler_tauri::{sessions, virtual_branches};
|
||||
|
||||
use crate::cli::butler::app::App;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct Clear {}
|
||||
|
||||
impl super::RunCommand for Clear {
|
||||
fn run(self) -> Result<()> {
|
||||
let app = App::new().context("Failed to create app")?;
|
||||
let session = app
|
||||
.gb_repository()
|
||||
.get_or_create_current_session()
|
||||
.context("failed to get or create currnt session")?;
|
||||
let session_reader = sessions::Reader::open(app.gb_repository(), &session)
|
||||
.context("failed to open current session reader")?;
|
||||
let branch_writer = virtual_branches::branch::Writer::new(app.gb_repository());
|
||||
|
||||
let iterator =
|
||||
virtual_branches::Iterator::new(&session_reader).expect("failed to read branches");
|
||||
for branch in iterator.flatten() {
|
||||
branch_writer
|
||||
.delete(&branch)
|
||||
.context("failed to delete branch")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
68
src-tauri/src/bin/cli/butler/commands/commit.rs
Normal file
68
src-tauri/src/bin/cli/butler/commands/commit.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Args;
|
||||
use colored::Colorize;
|
||||
use dialoguer::{console::Term, theme::ColorfulTheme, Input, Select};
|
||||
|
||||
use git_butler_tauri::{reader, sessions, virtual_branches};
|
||||
|
||||
use crate::cli::butler::app::App;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct Commit {}
|
||||
|
||||
impl super::RunCommand for Commit {
|
||||
fn run(self) -> Result<()> {
|
||||
let app = App::new().context("Failed to create app")?;
|
||||
|
||||
// get the branch to commit
|
||||
let current_session = app
|
||||
.gb_repository()
|
||||
.get_or_create_current_session()
|
||||
.context("failed to get or create currnt session")?;
|
||||
|
||||
let current_session_reader = sessions::Reader::open(app.gb_repository(), ¤t_session)
|
||||
.context("failed to open current session reader")?;
|
||||
|
||||
let virtual_branches = virtual_branches::Iterator::new(¤t_session_reader)
|
||||
.context("failed to read virtual branches")?
|
||||
.collect::<Result<Vec<virtual_branches::branch::Branch>, reader::Error>>()
|
||||
.context("failed to read virtual branches")?
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut ids = Vec::new();
|
||||
let mut names = Vec::new();
|
||||
for branch in virtual_branches {
|
||||
ids.push(branch.id);
|
||||
names.push(branch.name);
|
||||
}
|
||||
let selection = match Select::with_theme(&ColorfulTheme::default())
|
||||
.items(&names)
|
||||
.default(0)
|
||||
.interact_on_opt(&Term::stderr())
|
||||
.context("failed to get selection")?
|
||||
{
|
||||
Some(selection) => selection,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let commit_branch = ids[selection].clone();
|
||||
println!("Committing virtual branch {}", commit_branch.red());
|
||||
|
||||
// get the commit message
|
||||
let message: String = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Commit message")
|
||||
.interact_text()
|
||||
.context("failed to get commit message")?;
|
||||
|
||||
virtual_branches::commit(
|
||||
app.gb_repository(),
|
||||
&app.project_repository(),
|
||||
&commit_branch,
|
||||
&message,
|
||||
)
|
||||
.context("failed to commit")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
18
src-tauri/src/bin/cli/butler/commands/flush.rs
Normal file
18
src-tauri/src/bin/cli/butler/commands/flush.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Args;
|
||||
|
||||
use crate::cli::butler::app::App;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct Flush {}
|
||||
|
||||
impl super::RunCommand for Flush {
|
||||
fn run(self) -> Result<()> {
|
||||
let app = App::new().context("Failed to create app")?;
|
||||
println!("Flushing sessions");
|
||||
app.gb_repository()
|
||||
.flush()
|
||||
.context("failed to flush sessions")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
108
src-tauri/src/bin/cli/butler/commands/info.rs
Normal file
108
src-tauri/src/bin/cli/butler/commands/info.rs
Normal file
@ -0,0 +1,108 @@
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Args;
|
||||
use colored::Colorize;
|
||||
|
||||
use git_butler_tauri::{reader, sessions, virtual_branches};
|
||||
|
||||
use crate::cli::butler::app::App;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct Info {}
|
||||
|
||||
impl super::RunCommand for Info {
|
||||
fn run(self) -> Result<()> {
|
||||
let app = App::new().context("Failed to create app")?;
|
||||
|
||||
// just print information for the project
|
||||
println!("path: {}", app.path().yellow());
|
||||
println!("data_dir: {}", app.local_data_dir().yellow());
|
||||
|
||||
// find the project in project storage that matches the cwd
|
||||
println!("{}", "project:".to_string().red());
|
||||
println!(" id: {}", app.project().id.blue());
|
||||
println!(" title: {}", app.project().title.blue());
|
||||
println!(
|
||||
" description: {}",
|
||||
app.project()
|
||||
.description
|
||||
.clone()
|
||||
.unwrap_or("none".to_string())
|
||||
.blue()
|
||||
);
|
||||
println!(
|
||||
" project_data_last_fetched: {:?}",
|
||||
app.project().project_data_last_fetched
|
||||
);
|
||||
println!(
|
||||
" project_gitbutler_data_last_fetched: {:?}",
|
||||
app.project().gitbutler_data_last_fetched
|
||||
);
|
||||
println!(" path: {}", app.project().path.blue());
|
||||
|
||||
if let Some(api) = app.project().api.as_ref() {
|
||||
println!(" {}:", "api".to_string().red());
|
||||
println!(" api name: {}", api.name.blue());
|
||||
println!(" repo id: {}", api.repository_id.blue());
|
||||
println!(" git url: {}", api.git_url.blue());
|
||||
println!(" created: {}", api.created_at.blue());
|
||||
println!(" updated: {}", api.updated_at.blue());
|
||||
}
|
||||
|
||||
println!("{}", "project repo:".to_string().red());
|
||||
println!(
|
||||
" HEAD: {}",
|
||||
app.project_repository()
|
||||
.get_head()
|
||||
.context("failed to get head")?
|
||||
.name()
|
||||
.context("failed to get head name")?
|
||||
.blue()
|
||||
);
|
||||
|
||||
println!("{}", "sessions:".to_string().red());
|
||||
// sessions storage
|
||||
let sessions = app
|
||||
.sessions_db()
|
||||
.list_by_project_id(&app.project().id, None)
|
||||
.unwrap();
|
||||
//list the sessions
|
||||
for session in &sessions {
|
||||
println!(" id: {}", session.id.blue());
|
||||
}
|
||||
|
||||
// gitbutler repo stuff
|
||||
// read default target
|
||||
|
||||
let current_session = app
|
||||
.gb_repository()
|
||||
.get_or_create_current_session()
|
||||
.context("failed to get or create currnt session")?;
|
||||
let current_session_reader = sessions::Reader::open(app.gb_repository(), ¤t_session)
|
||||
.context("failed to open current session reader")?;
|
||||
|
||||
let target_reader = virtual_branches::target::Reader::new(¤t_session_reader);
|
||||
match target_reader.read_default() {
|
||||
Ok(target) => {
|
||||
println!("{}", "target:".to_string().red());
|
||||
println!(" base sha: {}", target.sha.to_string().blue());
|
||||
}
|
||||
Err(reader::Error::NotFound) => {}
|
||||
Err(e) => panic!("failed to read default target: {}", e),
|
||||
};
|
||||
|
||||
println!("{}", "virtual branches:".to_string().red());
|
||||
virtual_branches::Iterator::new(¤t_session_reader)
|
||||
.context("failed to read virtual branches")?
|
||||
.collect::<Result<Vec<virtual_branches::branch::Branch>, reader::Error>>()
|
||||
.context("failed to read virtual branches")?
|
||||
.into_iter()
|
||||
.for_each(|branch| {
|
||||
println!(" {}", branch.name);
|
||||
for file in branch.ownership.to_string().lines() {
|
||||
println!(" {}", file);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
35
src-tauri/src/bin/cli/butler/commands/mod.rs
Normal file
35
src-tauri/src/bin/cli/butler/commands/mod.rs
Normal file
@ -0,0 +1,35 @@
|
||||
mod branches;
|
||||
pub use branches::Branches;
|
||||
|
||||
mod clear;
|
||||
pub use clear::Clear;
|
||||
|
||||
mod commit;
|
||||
pub use commit::Commit;
|
||||
|
||||
mod flush;
|
||||
pub use flush::Flush;
|
||||
|
||||
mod info;
|
||||
pub use info::Info;
|
||||
|
||||
mod mv;
|
||||
pub use mv::Move;
|
||||
|
||||
mod new;
|
||||
pub use new::New;
|
||||
|
||||
mod remotes;
|
||||
pub use remotes::Remotes;
|
||||
|
||||
mod reset;
|
||||
pub use reset::Reset;
|
||||
|
||||
mod run;
|
||||
pub use run::RunCommand;
|
||||
|
||||
mod setup;
|
||||
pub use setup::Setup;
|
||||
|
||||
mod status;
|
||||
pub use status::Status;
|
93
src-tauri/src/bin/cli/butler/commands/mv.rs
Normal file
93
src-tauri/src/bin/cli/butler/commands/mv.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Args;
|
||||
use dialoguer::{console::Term, theme::ColorfulTheme, MultiSelect, Select};
|
||||
|
||||
use git_butler_tauri::{reader, sessions, virtual_branches};
|
||||
|
||||
use crate::cli::butler::app::App;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct Move {}
|
||||
|
||||
impl super::RunCommand for Move {
|
||||
fn run(self) -> Result<()> {
|
||||
let app = App::new().context("Failed to create app")?;
|
||||
|
||||
let all_hunks =
|
||||
virtual_branches::get_status_by_branch(app.gb_repository(), &app.project_repository())
|
||||
.context("failed to get status files")?
|
||||
.into_iter()
|
||||
.flat_map(|(_branch, files)| {
|
||||
files
|
||||
.into_iter()
|
||||
.flat_map(|file| {
|
||||
file.hunks
|
||||
.into_iter()
|
||||
.map(|hunk| hunk.id)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let selected_files: Vec<String> = MultiSelect::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Which hunks do you want to move?")
|
||||
.items(&all_hunks)
|
||||
.interact()
|
||||
.context("failed to get selections")?
|
||||
.iter()
|
||||
.map(|i| all_hunks[*i].clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let current_session = app
|
||||
.gb_repository()
|
||||
.get_or_create_current_session()
|
||||
.context("failed to get or create currnt session")?;
|
||||
|
||||
let current_session_reader = sessions::Reader::open(app.gb_repository(), ¤t_session)
|
||||
.context("failed to open current session reader")?;
|
||||
|
||||
let virtual_branches = virtual_branches::Iterator::new(¤t_session_reader)
|
||||
.context("failed to read virtual branches")?
|
||||
.collect::<Result<Vec<virtual_branches::branch::Branch>, reader::Error>>()
|
||||
.context("failed to read virtual branches")?
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let selection = match Select::with_theme(&ColorfulTheme::default())
|
||||
.items(
|
||||
&virtual_branches
|
||||
.iter()
|
||||
.map(|b| b.name.clone())
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.default(0)
|
||||
.interact_on_opt(&Term::stderr())
|
||||
.context("failed to get selection")?
|
||||
{
|
||||
Some(selection) => selection,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let target_branch = virtual_branches[selection].clone();
|
||||
let mut ownership = target_branch.ownership.clone();
|
||||
ownership.put(
|
||||
&selected_files
|
||||
.join("\n")
|
||||
.try_into()
|
||||
.context("failed to convert to ownership")?,
|
||||
);
|
||||
|
||||
virtual_branches::update_branch(
|
||||
app.gb_repository(),
|
||||
virtual_branches::branch::BranchUpdateRequest {
|
||||
id: target_branch.id,
|
||||
ownership: Some(ownership),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.context("failed to update branch")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
32
src-tauri/src/bin/cli/butler/commands/new.rs
Normal file
32
src-tauri/src/bin/cli/butler/commands/new.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Args;
|
||||
use dialoguer::{theme::ColorfulTheme, Input};
|
||||
|
||||
use git_butler_tauri::virtual_branches;
|
||||
|
||||
use crate::cli::butler::app::App;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct New {}
|
||||
|
||||
impl super::RunCommand for New {
|
||||
fn run(self) -> Result<()> {
|
||||
let app = App::new().context("Failed to create app")?;
|
||||
|
||||
let input: String = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("New branch name")
|
||||
.interact_text()
|
||||
.context("failed to get branch name")?;
|
||||
|
||||
virtual_branches::create_virtual_branch(
|
||||
app.gb_repository(),
|
||||
&virtual_branches::branch::BranchCreateRequest {
|
||||
name: Some(input),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.context("failed to create virtual branch")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
22
src-tauri/src/bin/cli/butler/commands/remotes.rs
Normal file
22
src-tauri/src/bin/cli/butler/commands/remotes.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Args;
|
||||
|
||||
use git_butler_tauri::virtual_branches;
|
||||
|
||||
use crate::cli::butler::app::App;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct Remotes {}
|
||||
|
||||
impl super::RunCommand for Remotes {
|
||||
fn run(self) -> Result<()> {
|
||||
let app = App::new().context("Failed to create app")?;
|
||||
let branches =
|
||||
virtual_branches::remote_branches(app.gb_repository(), &app.project_repository())
|
||||
.context("failed to get remote branches")?;
|
||||
for branch in branches {
|
||||
println!("{}", branch.name);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
39
src-tauri/src/bin/cli/butler/commands/reset.rs
Normal file
39
src-tauri/src/bin/cli/butler/commands/reset.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Args;
|
||||
|
||||
use git_butler_tauri::{reader, sessions, virtual_branches};
|
||||
|
||||
use crate::cli::butler::app::App;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct Reset {}
|
||||
|
||||
impl super::RunCommand for Reset {
|
||||
fn run(self) -> Result<()> {
|
||||
let app = App::new().context("Failed to create app")?;
|
||||
// get the branch to commit
|
||||
let current_session = app
|
||||
.gb_repository()
|
||||
.get_or_create_current_session()
|
||||
.context("failed to get or create currnt session")?;
|
||||
|
||||
let current_session_reader = sessions::Reader::open(app.gb_repository(), ¤t_session)
|
||||
.context("failed to open current session reader")?;
|
||||
|
||||
let virtual_branches = virtual_branches::Iterator::new(¤t_session_reader)
|
||||
.context("failed to read virtual branches")?
|
||||
.collect::<Result<Vec<virtual_branches::branch::Branch>, reader::Error>>()
|
||||
.context("failed to read virtual branches")?
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let writer = virtual_branches::branch::Writer::new(app.gb_repository());
|
||||
for mut branch in virtual_branches {
|
||||
println!("resetting {}", branch.name);
|
||||
branch.applied = false;
|
||||
writer.write(&branch).context("failed to write branch")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
5
src-tauri/src/bin/cli/butler/commands/run.rs
Normal file
5
src-tauri/src/bin/cli/butler/commands/run.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use anyhow::Result;
|
||||
|
||||
pub trait RunCommand {
|
||||
fn run(self) -> Result<()>;
|
||||
}
|
47
src-tauri/src/bin/cli/butler/commands/setup.rs
Normal file
47
src-tauri/src/bin/cli/butler/commands/setup.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Args;
|
||||
use colored::Colorize;
|
||||
use dialoguer::{console::Term, theme::ColorfulTheme, Select};
|
||||
|
||||
use crate::cli::butler::app::App;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct Setup {}
|
||||
|
||||
impl super::RunCommand for Setup {
|
||||
fn run(self) -> Result<()> {
|
||||
let app = App::new().context("Failed to create app")?;
|
||||
|
||||
println!(
|
||||
" HEAD: {}",
|
||||
app.project_repository()
|
||||
.get_head()
|
||||
.context("failed to get head")?
|
||||
.name()
|
||||
.context("failed to get head name")?
|
||||
.blue()
|
||||
);
|
||||
let items = app
|
||||
.project_repository()
|
||||
.git_remote_branches()
|
||||
.context("failed to get remote branches")?;
|
||||
|
||||
let selection = Select::with_theme(&ColorfulTheme::default())
|
||||
.items(&items)
|
||||
.default(0)
|
||||
.interact_on_opt(&Term::stderr())
|
||||
.context("failed to get selection")?;
|
||||
|
||||
match selection {
|
||||
Some(index) => {
|
||||
println!("Setting target to: {}", items[index].red());
|
||||
app.gb_repository()
|
||||
.set_target_branch(&app.project_repository(), &items[index])
|
||||
.context("failed to set target branch")?;
|
||||
}
|
||||
None => println!("User did not select anything"),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
42
src-tauri/src/bin/cli/butler/commands/status.rs
Normal file
42
src-tauri/src/bin/cli/butler/commands/status.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Args;
|
||||
use colored::Colorize;
|
||||
|
||||
use git_butler_tauri::virtual_branches;
|
||||
|
||||
use crate::cli::butler::app::App;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct Status {}
|
||||
|
||||
impl super::RunCommand for Status {
|
||||
fn run(self) -> Result<()> {
|
||||
let app = App::new().context("Failed to create app")?;
|
||||
|
||||
let statuses =
|
||||
virtual_branches::get_status_by_branch(app.gb_repository(), &app.project_repository())
|
||||
.context("failed to get status by branch")?;
|
||||
|
||||
for (branch, files) in statuses {
|
||||
if branch.applied {
|
||||
println!(" branch: {}", branch.name.blue());
|
||||
println!(" head: {}", branch.head.to_string().green());
|
||||
println!(" tree: {}", branch.tree.to_string().green());
|
||||
println!(" id: {}", branch.id.green());
|
||||
println!("applied: {}", branch.applied.to_string().green());
|
||||
println!(" files:");
|
||||
for file in files {
|
||||
println!(" {}", file.path.display().to_string().yellow());
|
||||
for hunk in file.hunks {
|
||||
println!(" {}", hunk.id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!(" branch: {}", branch.name.blue());
|
||||
println!("applied: {}", branch.applied.to_string().green());
|
||||
}
|
||||
println!();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
56
src-tauri/src/bin/cli/butler/mod.rs
Normal file
56
src-tauri/src/bin/cli/butler/mod.rs
Normal file
@ -0,0 +1,56 @@
|
||||
mod app;
|
||||
mod commands;
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
use commands::RunCommand;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use colored::Colorize;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Cli {
|
||||
#[clap(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum Command {
|
||||
Branches(commands::Branches),
|
||||
Clear(commands::Clear),
|
||||
Commit(commands::Commit),
|
||||
Flush(commands::Flush),
|
||||
Info(commands::Info),
|
||||
Move(commands::Move),
|
||||
New(commands::New),
|
||||
Remotes(commands::Remotes),
|
||||
Reset(commands::Reset),
|
||||
Setup(commands::Setup),
|
||||
Status(commands::Status),
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
pub fn run(self) -> ExitCode {
|
||||
let output = match self.command {
|
||||
Command::Branches(branches) => branches.run(),
|
||||
Command::Clear(clear) => clear.run(),
|
||||
Command::Commit(commit) => commit.run(),
|
||||
Command::Flush(flush) => flush.run(),
|
||||
Command::Info(info) => info.run(),
|
||||
Command::Move(mv) => mv.run(),
|
||||
Command::New(new) => new.run(),
|
||||
Command::Remotes(remotes) => remotes.run(),
|
||||
Command::Reset(reset) => reset.run(),
|
||||
Command::Setup(setup) => setup.run(),
|
||||
Command::Status(status) => status.run(),
|
||||
};
|
||||
|
||||
match output {
|
||||
Ok(_) => ExitCode::SUCCESS,
|
||||
Err(e) => {
|
||||
eprintln!("{}: {:#}", "error".red(), e);
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
src-tauri/src/bin/cli/mod.rs
Normal file
3
src-tauri/src/bin/cli/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod butler;
|
||||
|
||||
pub use butler::Cli as Butler;
|
@ -116,16 +116,16 @@ pub struct VirtualBranchHunk {
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RemoteBranch {
|
||||
sha: String,
|
||||
name: String,
|
||||
last_commit_ts: u128,
|
||||
first_commit_ts: u128,
|
||||
ahead: u32,
|
||||
behind: u32,
|
||||
upstream: Option<String>,
|
||||
authors: Vec<String>,
|
||||
mergeable: bool,
|
||||
merge_conflicts: Vec<String>,
|
||||
pub sha: String,
|
||||
pub name: String,
|
||||
pub last_commit_ts: u128,
|
||||
pub first_commit_ts: u128,
|
||||
pub ahead: u32,
|
||||
pub behind: u32,
|
||||
pub upstream: Option<String>,
|
||||
pub authors: Vec<String>,
|
||||
pub mergeable: bool,
|
||||
pub merge_conflicts: Vec<String>,
|
||||
}
|
||||
|
||||
fn get_default_target(current_session_reader: &sessions::Reader) -> Result<Option<target::Target>> {
|
||||
|
Loading…
Reference in New Issue
Block a user