This commit is contained in:
parent
193036fab7
commit
d6d2909de0
6 changed files with 371 additions and 29 deletions
94
Cargo.lock
generated
94
Cargo.lock
generated
|
@ -146,6 +146,21 @@ version = "1.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
|
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fuzzy-matcher"
|
name = "fuzzy-matcher"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
|
@ -169,12 +184,31 @@ name = "g"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"either",
|
||||||
"inquire",
|
"inquire",
|
||||||
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"subprocess",
|
"subprocess",
|
||||||
|
"toml",
|
||||||
"yansi",
|
"yansi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inquire"
|
name = "inquire"
|
||||||
version = "0.7.5"
|
version = "0.7.5"
|
||||||
|
@ -323,18 +357,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.217"
|
version = "1.0.219"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.217"
|
version = "1.0.219"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -353,6 +387,15 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook"
|
name = "signal-hook"
|
||||||
version = "0.3.17"
|
version = "0.3.17"
|
||||||
|
@ -426,6 +469,40 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.8.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.22.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.14"
|
version = "1.0.14"
|
||||||
|
@ -617,6 +694,15 @@ version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yansi"
|
name = "yansi"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
|
@ -5,7 +5,10 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.26", features = ["cargo"] }
|
clap = { version = "4.5.26", features = ["cargo"] }
|
||||||
|
either = { version = "1.15.0", features = ["serde"] }
|
||||||
inquire = "0.7.5"
|
inquire = "0.7.5"
|
||||||
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.137"
|
serde_json = "1.0.137"
|
||||||
subprocess = "0.2.9"
|
subprocess = "0.2.9"
|
||||||
|
toml = "0.8.20"
|
||||||
yansi = "1.0.1"
|
yansi = "1.0.1"
|
||||||
|
|
2
PKGBUILD
2
PKGBUILD
|
@ -6,7 +6,7 @@ pkgdesc="git workflow wrapper"
|
||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
url="https://git.hydrar.de/jmarya/g"
|
url="https://git.hydrar.de/jmarya/g"
|
||||||
license=("MIT")
|
license=("MIT")
|
||||||
depends=("git" "serie" "ripgrep" "onefetch")
|
depends=("git" "serie" "ripgrep" "onefetch", "fd", "sd")
|
||||||
makedepends=("rustup" "git")
|
makedepends=("rustup" "git")
|
||||||
source=("${pkgname}::git+https://git.hydrar.de/jmarya/g.git")
|
source=("${pkgname}::git+https://git.hydrar.de/jmarya/g.git")
|
||||||
sha256sums=("SKIP")
|
sha256sums=("SKIP")
|
||||||
|
|
265
src/bootstrap.rs
Normal file
265
src/bootstrap.rs
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use either::Either;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use subprocess::Exec;
|
||||||
|
|
||||||
|
use crate::git::switch_branch;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct BootstrapConfig {
|
||||||
|
pub ask: HashMap<String, AskConfig>,
|
||||||
|
pub actions: ActionsConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub enum AskKind {
|
||||||
|
#[serde(rename = "text")]
|
||||||
|
Text,
|
||||||
|
#[serde(rename = "number")]
|
||||||
|
Number,
|
||||||
|
#[serde(rename = "selection")]
|
||||||
|
Selection,
|
||||||
|
#[serde(rename = "bool")]
|
||||||
|
Bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AskValue {
|
||||||
|
Text(String, String),
|
||||||
|
Number(String, i64),
|
||||||
|
Selection(String, String),
|
||||||
|
Bool(String, bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AskValue {
|
||||||
|
pub fn has_name(&self, name: &str) -> bool {
|
||||||
|
match self {
|
||||||
|
AskValue::Text(n, _) => n.as_str() == name,
|
||||||
|
AskValue::Number(n, _) => n.as_str() == name,
|
||||||
|
AskValue::Selection(n, _) => n.as_str() == name,
|
||||||
|
AskValue::Bool(n, _) => n.as_str() == name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text(&self) -> String {
|
||||||
|
match self {
|
||||||
|
AskValue::Text(_, t) => t.clone(),
|
||||||
|
AskValue::Number(_, n) => n.to_string(),
|
||||||
|
AskValue::Selection(_, s) => s.clone(),
|
||||||
|
AskValue::Bool(_, b) => b.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct AskConfig {
|
||||||
|
pub kind: AskKind,
|
||||||
|
pub prompt: String,
|
||||||
|
pub options: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ActionsConfig {
|
||||||
|
pub replace: Vec<ReplaceAction>,
|
||||||
|
pub branch: Vec<BranchAction>,
|
||||||
|
pub script: Vec<ScriptAction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct VarCompare {
|
||||||
|
pub var: String,
|
||||||
|
pub eq: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ReplaceAction {
|
||||||
|
pub when: Option<Either<String, VarCompare>>,
|
||||||
|
pub from: String,
|
||||||
|
pub to: String,
|
||||||
|
pub to_var: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct BranchAction {
|
||||||
|
pub when: Option<Either<String, VarCompare>>,
|
||||||
|
pub branch: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ScriptAction {
|
||||||
|
pub when: Option<Either<String, VarCompare>>,
|
||||||
|
pub script: String,
|
||||||
|
pub expose: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval(when: &Either<String, VarCompare>, vars: &[AskValue]) -> bool {
|
||||||
|
if when.is_left() {
|
||||||
|
let when = when.as_ref().left().unwrap();
|
||||||
|
let var = vars.into_iter().find(|x| x.has_name(&when)).unwrap();
|
||||||
|
if let AskValue::Bool(_, ret) = var {
|
||||||
|
return *ret;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let when = when.as_ref().right().unwrap();
|
||||||
|
let var = vars.into_iter().find(|x| x.has_name(&when.var)).unwrap();
|
||||||
|
match var {
|
||||||
|
AskValue::Text(_, t) => {
|
||||||
|
return *t == when.eq;
|
||||||
|
}
|
||||||
|
AskValue::Number(_, n) => {
|
||||||
|
return n.to_string() == when.eq;
|
||||||
|
}
|
||||||
|
AskValue::Selection(_, s) => {
|
||||||
|
return *s == when.eq;
|
||||||
|
}
|
||||||
|
AskValue::Bool(_, b) => {
|
||||||
|
return b.to_string() == when.eq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("Could not evaluate condition");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_script_vars(expose: Option<Vec<String>>, vars: &[AskValue]) -> String {
|
||||||
|
if let Some(expose) = expose {
|
||||||
|
let mut exp = Vec::new();
|
||||||
|
|
||||||
|
for ex in expose {
|
||||||
|
let v = vars.iter().find(|x| x.has_name(&ex)).unwrap();
|
||||||
|
exp.push(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return exp
|
||||||
|
.iter()
|
||||||
|
.map(|x| match x {
|
||||||
|
AskValue::Text(name, text) => format!("{name}='{text}'"),
|
||||||
|
AskValue::Number(name, num) => format!("{name}={num}"),
|
||||||
|
AskValue::Selection(name, select) => format!("{name}='{select}'"),
|
||||||
|
AskValue::Bool(name, b) => format!("{name}='{}'", b.to_string()),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn do_script(action: &ScriptAction, vars: &[AskValue], name: &str) {
|
||||||
|
let pre = build_script_vars(action.expose.clone(), &vars);
|
||||||
|
println!("Running script '{}'", action.script);
|
||||||
|
Exec::cmd("sh")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(format!("{pre}\n./{}", action.script))
|
||||||
|
.cwd(std::path::Path::new(name))
|
||||||
|
.popen()
|
||||||
|
.unwrap()
|
||||||
|
.wait()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn do_replace(action: &ReplaceAction, name: &str, vars: &[AskValue]) {
|
||||||
|
let to = if let Some(var) = &action.to_var {
|
||||||
|
vars.iter().find(|x| x.has_name(var)).unwrap().text()
|
||||||
|
} else {
|
||||||
|
action.to.clone()
|
||||||
|
};
|
||||||
|
println!("Replacing '{}' -> '{}'", action.from, to);
|
||||||
|
Exec::cmd("fd")
|
||||||
|
.arg(".")
|
||||||
|
.arg("-tf")
|
||||||
|
.arg("-x")
|
||||||
|
.arg("sd")
|
||||||
|
.arg(&action.from)
|
||||||
|
.arg(&to)
|
||||||
|
.cwd(std::path::Path::new(name))
|
||||||
|
.popen()
|
||||||
|
.unwrap()
|
||||||
|
.wait()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bootstrap a git repository using a base template
|
||||||
|
pub fn bootstrap(base: &str, name: &str) {
|
||||||
|
Exec::cmd("git")
|
||||||
|
.arg("clone")
|
||||||
|
.arg(base)
|
||||||
|
.arg(name)
|
||||||
|
.popen()
|
||||||
|
.unwrap()
|
||||||
|
.wait()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if std::fs::exists(format!("{name}/bootstrap.toml")).unwrap_or(false) {
|
||||||
|
let config: BootstrapConfig =
|
||||||
|
toml::from_str(&std::fs::read_to_string(&format!("{name}/bootstrap.toml")).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
let mut vars = Vec::new();
|
||||||
|
|
||||||
|
for (var_name, def) in &config.ask {
|
||||||
|
match def.kind {
|
||||||
|
AskKind::Text => {
|
||||||
|
let a = inquire::prompt_text(&def.prompt).unwrap();
|
||||||
|
vars.push(AskValue::Text(var_name.clone(), a));
|
||||||
|
}
|
||||||
|
AskKind::Number => {
|
||||||
|
let a = inquire::prompt_text(&def.prompt).unwrap();
|
||||||
|
let a: i64 = a.parse().unwrap();
|
||||||
|
vars.push(AskValue::Number(var_name.clone(), a));
|
||||||
|
}
|
||||||
|
AskKind::Selection => {
|
||||||
|
let a = inquire::Select::new(&def.prompt, def.options.clone().unwrap())
|
||||||
|
.prompt()
|
||||||
|
.unwrap();
|
||||||
|
vars.push(AskValue::Selection(var_name.clone(), a));
|
||||||
|
}
|
||||||
|
AskKind::Bool => {
|
||||||
|
let a = inquire::prompt_confirmation(&def.prompt).unwrap();
|
||||||
|
vars.push(AskValue::Bool(var_name.clone(), a));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for action in config.actions.branch {
|
||||||
|
if let Some(when) = action.when {
|
||||||
|
if eval(&when, &vars) {
|
||||||
|
println!("Switching to '{}' branch", action.branch);
|
||||||
|
switch_branch(&action.branch);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("Switching to '{}' branch", action.branch);
|
||||||
|
switch_branch(&action.branch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for action in config.actions.replace {
|
||||||
|
if let Some(when) = &action.when {
|
||||||
|
if eval(when, &vars) {
|
||||||
|
do_replace(&action, name, &vars);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
do_replace(&action, name, &vars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for action in config.actions.script {
|
||||||
|
if let Some(when) = &action.when {
|
||||||
|
if eval(when, &vars) {
|
||||||
|
do_script(&action, &vars, name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
do_script(&action, &vars, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fs::remove_dir_all(std::path::Path::new(name).join(".git")).unwrap();
|
||||||
|
Exec::cmd("git")
|
||||||
|
.arg("init")
|
||||||
|
.arg("--quiet")
|
||||||
|
.cwd(std::path::Path::new(name))
|
||||||
|
.popen()
|
||||||
|
.unwrap()
|
||||||
|
.wait()
|
||||||
|
.unwrap();
|
||||||
|
}
|
30
src/git.rs
30
src/git.rs
|
@ -13,6 +13,15 @@ pub fn has_remote() -> bool {
|
||||||
!str.is_empty()
|
!str.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn switch_branch(branch: &str) {
|
||||||
|
let mut git = Exec::cmd("git")
|
||||||
|
.arg("checkout")
|
||||||
|
.arg(branch)
|
||||||
|
.popen()
|
||||||
|
.unwrap();
|
||||||
|
git.wait().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new branch and switch to it in the current repository
|
/// Create a new branch and switch to it in the current repository
|
||||||
pub fn create_new_branch(branch: &str) {
|
pub fn create_new_branch(branch: &str) {
|
||||||
let mut git = Exec::cmd("git")
|
let mut git = Exec::cmd("git")
|
||||||
|
@ -269,24 +278,3 @@ pub fn pull() {
|
||||||
.wait()
|
.wait()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bootstrap a git repository using a base template
|
|
||||||
pub fn bootstrap(base: &str, name: &str) {
|
|
||||||
Exec::cmd("git")
|
|
||||||
.arg("clone")
|
|
||||||
.arg(base)
|
|
||||||
.arg(name)
|
|
||||||
.popen()
|
|
||||||
.unwrap()
|
|
||||||
.wait()
|
|
||||||
.unwrap();
|
|
||||||
std::fs::remove_dir_all(std::path::Path::new(name).join(".git")).unwrap();
|
|
||||||
Exec::cmd("git")
|
|
||||||
.arg("init")
|
|
||||||
.arg("--quiet")
|
|
||||||
.cwd(std::path::Path::new(name))
|
|
||||||
.popen()
|
|
||||||
.unwrap()
|
|
||||||
.wait()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use git::{
|
use bootstrap::bootstrap;
|
||||||
bootstrap, commit, create_new_branch, delete_branch, fetch, git_add, pull, push, revert_commits,
|
use git::{commit, create_new_branch, delete_branch, fetch, git_add, pull, push, revert_commits};
|
||||||
};
|
|
||||||
use gitmoji::{CommitMessage, select_gitmoji};
|
use gitmoji::{CommitMessage, select_gitmoji};
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use subprocess::{Exec, Redirection};
|
use subprocess::{Exec, Redirection};
|
||||||
use yansi::{Color, Paint};
|
use yansi::{Color, Paint};
|
||||||
|
|
||||||
mod args;
|
mod args;
|
||||||
|
mod bootstrap;
|
||||||
mod git;
|
mod git;
|
||||||
mod gitmoji;
|
mod gitmoji;
|
||||||
mod precommit;
|
mod precommit;
|
||||||
|
|
Loading…
Add table
Reference in a new issue