Skip to content

Commit 0562d8c

Browse files
committed
feat: fuzzy branch switching
1 parent 30357b2 commit 0562d8c

File tree

8 files changed

+113
-40
lines changed

8 files changed

+113
-40
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "dukit"
3-
version = "0.1.0"
3+
version = "0.2.0"
44
edition = "2021"
55
authors =[ "Tharun <tharun1@hotmail.co.uk>" ]
66
description = "git cli tool for the feathered"

README.md

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,17 @@ For the orange\yellow bill inclined amongst us
1010
### Status info
1111
`git status` command (default method) but copy pasteable
1212
```sh
13-
> dukit
13+
> dukit -b
1414

15-
S
16-
src/lib.rs
15+
Staged
16+
src/lib.rs
1717

18-
M
19-
src/lib.rs
18+
Modified
19+
README.md
20+
src/base_commands.rs
2021

21-
U
22-
.gitignore
23-
README.md
24-
duck.gif
22+
Untracked
23+
hi.txt
2524
```
2625
### Branch info
2726
Notice how easy copy pasting should be
@@ -35,6 +34,21 @@ Notice how easy copy pasting should be
3534
main
3635

3736
```
37+
38+
### Fuzzy branch switching
39+
```sh
40+
> dukit -f
41+
42+
#opens fzf
43+
test2
44+
test1
45+
▌ feature/git-switch-interactive
46+
3/3 ─────────────────────────────────────────────────────────────
47+
> {search bar here}
48+
49+
Switched to branch 'feature/git-switch-interactive'
50+
```
51+
3852
### Interactive `git add`
3953
This opens your default editor with files to add
4054

@@ -62,6 +76,7 @@ This opens your default editor with files to add
6276
## Installation
6377
```
6478
cargo install dukit
79+
apt install fzf
6580
```
6681

6782

@@ -72,14 +87,11 @@ cargo install dukit
7287
- If you want to contribute code, make a pull request. anything short of a war crime will probably be accepted.
7388

7489
## TODO
75-
- [ ] testing NO CLUE
76-
- [ ] what is up docs
7790
- [x] status info
7891
- [x] branch and remote info
7992
- [x] git add using editor
8093
- [x] unwrap unwrap unwrap unwrap unwrap unwrap
81-
- [ ] git switch branch using editor
82-
- [ ] fuzzy branch switching?
94+
- [x] fuzzy branch switching?
8395
- [ ] ez stash and poppin info
8496
- [ ] nice git log info
8597
- [ ] ez copy commit hashes

src/base_commands.rs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::errors::DuckErrors;
2-
use crate::{DisplayableCliCommand, TEMP_FILE_PATH};
2+
use crate::{DisplayableCliCommand, GIT_SWITCH_UNCOMMITED_CHANGES_ERROR, TEMP_FILE_PATH};
33
use std::{
44
env::var,
55
fs::{self, File},
@@ -13,7 +13,8 @@ pub enum BaseCliCommands {
1313
RemoteBranch,
1414
OpenEditor,
1515
AddFile,
16-
Todo,
16+
FzfGitBranch,
17+
GitSwitch,
1718
}
1819

1920
impl BaseCliCommands {
@@ -22,28 +23,49 @@ impl BaseCliCommands {
2223
match self {
2324
BaseCliCommands::OpenEditor => self.open_editor(stdin.unwrap_or_default()),
2425
BaseCliCommands::AddFile => self.add_file(stdin.unwrap_or_default()),
26+
BaseCliCommands::GitSwitch => self.switch_branch(stdin.unwrap_or_default()),
2527
_ => self.run_generic_command(),
2628
}
2729
}
2830

2931
/// gets the cli command as a string
3032
fn get_cli_command(&self) -> String {
3133
match self {
32-
BaseCliCommands::Status => "git status -s".to_string(),
33-
BaseCliCommands::BranchList => "git branch -l".to_string(),
34-
BaseCliCommands::RemoteBranch => {
34+
Self::Status => "git status -s".to_string(),
35+
Self::BranchList => "git branch -l".to_string(),
36+
Self::RemoteBranch => {
3537
"git status -uno | grep -E 'Your branch is (ahead|behind|up to date)'".to_string()
3638
}
37-
BaseCliCommands::OpenEditor => self.get_editor(),
38-
BaseCliCommands::AddFile => "git add".to_string(),
39-
BaseCliCommands::Todo => "echo 'hi :3'".to_string(),
39+
Self::OpenEditor => self.get_editor(),
40+
Self::AddFile => "git add".to_string(),
41+
Self::FzfGitBranch => "git branch -l| grep '^[^*]' | fzf".to_string(),
42+
Self::GitSwitch => "git switch".to_string(),
4043
}
4144
}
4245

46+
/// switches to given branch
47+
fn switch_branch(self, branch_name: String) -> Result<String, DuckErrors> {
48+
let cmd_to_switch_branch = self.get_cli_command() + " " + &branch_name;
49+
let output = Command::new("sh")
50+
.arg("-c")
51+
.arg(cmd_to_switch_branch)
52+
.output()
53+
.map_err(|_| DuckErrors::SpawnChildProccesForGeneric)?;
54+
55+
let stderr = String::from_utf8_lossy(&output.stderr);
56+
if stderr.contains(GIT_SWITCH_UNCOMMITED_CHANGES_ERROR) {
57+
return Err(DuckErrors::LocalChangesOverwrittenByCheckout);
58+
}
59+
60+
let displayable_output = DisplayableCliCommand(output);
61+
Ok(displayable_output.to_string())
62+
}
63+
4364
/// opens editor with given stdin to display to user
4465
/// returns a string of the saved file on exit
4566
fn open_editor(self, stdin: String) -> Result<String, DuckErrors> {
46-
let mut file = File::create(TEMP_FILE_PATH).map_err(|_| DuckErrors::CouldNotWriteToTempFile)?;
67+
let mut file =
68+
File::create(TEMP_FILE_PATH).map_err(|_| DuckErrors::CouldNotWriteToTempFile)?;
4769
file.write_all(stdin.as_bytes())
4870
.map_err(|_| DuckErrors::CouldNotWriteToTempFile)?;
4971
drop(file);

src/duck_commands.rs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,52 @@
1+
use termion::color;
2+
13
use crate::{
2-
base_commands::BaseCliCommands, errors::DuckErrors, COMMENT_CHAR, CURRENT_BRANCH_CHAR, DELETED_CHAR, DELETED_LABEL, EMPTY_CHAR, INTERACTIVE_ADD_HELP, LINE_SEPERATOR, MODIFIED_CHAR, MODIFIED_LABEL, NOTHING_TO_COMMIT_MESSAGE, NO_FILES_SELECTED_TO_ADD, NO_REMOTE_INFO, RUNNING_GIT_ADD, STAGED_LABEL, TICKED_BOX, UNSTAGED_LABEL, UNTRACKED_CHAR, UNTRACKED_LABEL
4+
base_commands::BaseCliCommands, errors::DuckErrors, COMMENT_CHAR, CURRENT_BRANCH_CHAR,
5+
DELETED_CHAR, DELETED_LABEL, EMPTY_CHAR, INTERACTIVE_ADD_HELP, LINE_SEPERATOR, MODIFIED_CHAR,
6+
MODIFIED_LABEL, NOTHING_TO_COMMIT_MESSAGE, NO_FILES_SELECTED_TO_ADD, NO_REMOTE_INFO,
7+
RUNNING_GIT_ADD, STAGED_LABEL, TICKED_BOX, UNSTAGED_LABEL, UNTRACKED_CHAR, UNTRACKED_LABEL,
38
};
4-
use termion::color;
59

610
pub enum DuckCommands {
711
Status,
812
Branch,
913
Add,
14+
FuzzyBranchSwitch,
1015
}
1116

1217
impl DuckCommands {
1318
/// DUCKS!
1419
pub fn run(&self) {
1520
match self {
16-
DuckCommands::Status => self.duck_file_status(),
17-
DuckCommands::Branch => self.duck_branch(),
18-
DuckCommands::Add => self.duck_interactive_add(),
21+
Self::Status => self.duck_file_status(),
22+
Self::Branch => self.duck_branch(),
23+
Self::Add => self.duck_interactive_add(),
24+
Self::FuzzyBranchSwitch => self.duck_fuzzy_branch_switch(),
25+
}
26+
}
27+
28+
fn duck_fuzzy_branch_switch(&self) {
29+
let out = match BaseCliCommands::FzfGitBranch.run(None) {
30+
Ok(output) => output,
31+
Err(e) => {
32+
e.printout();
33+
return;
34+
}
35+
};
36+
37+
if out.trim().is_empty() {
38+
DuckErrors::NoBranchGiven.printout();
39+
return;
1940
}
41+
42+
let out = match BaseCliCommands::GitSwitch.run(Some(out.trim().to_string())) {
43+
Ok(output) => output,
44+
Err(e) => {
45+
e.printout();
46+
return;
47+
}
48+
};
49+
println!("{}\n {}", color::Fg(color::Green), out.trim());
2050
}
2151

2252
/// pretty git status for files output
@@ -222,7 +252,7 @@ impl DuckCommands {
222252

223253
if to_be_added.is_empty() {
224254
println!("{}\n{}", color::Fg(color::Green), NO_FILES_SELECTED_TO_ADD);
225-
return
255+
return;
226256
}
227257

228258
for file in to_be_added {

src/errors.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use termion::color;
22

3-
//TODO return the correct thangs
43
#[derive(Debug)]
54
pub enum DuckErrors {
65
Fuck,
@@ -11,7 +10,10 @@ pub enum DuckErrors {
1110
CouldNotWriteToTempFile,
1211
SpawnChildProccesForGeneric,
1312
NoMatchingLineSeperatorFound,
14-
CouldNotReadTempFile
13+
CouldNotReadTempFile,
14+
LocalChangesOverwrittenByCheckout,
15+
GitSwitchGeneric,
16+
NoBranchGiven,
1517
}
1618

1719
impl std::fmt::Display for DuckErrors {
@@ -25,17 +27,16 @@ impl std::fmt::Display for DuckErrors {
2527
Self::CouldNotWriteToTempFile => write!(f, "could not write to temp file"),
2628
Self::SpawnChildProccesForGeneric => write!(f, "could not spawn child process for command"),
2729
Self::NoMatchingLineSeperatorFound=> write!(f, "could not find matching line seperator. (dont mess with the stuff thats commented)"),
28-
Self::CouldNotReadTempFile=> write!(f, "could not read temp file")
30+
Self::CouldNotReadTempFile=> write!(f, "could not read temp file"),
31+
Self::LocalChangesOverwrittenByCheckout=> write!(f, "Your local changes would be overwritten. commit or stash your changes"),
32+
Self::GitSwitchGeneric=> write!(f, "could not run git switch"),
33+
Self::NoBranchGiven=> write!(f, "no branch name given")
2934
}
3035
}
3136
}
3237

3338
impl DuckErrors {
3439
pub fn printout(&self) {
35-
eprintln!(
36-
"{} Error while running 'duck command': {}",
37-
color::Fg(color::Red),
38-
self
39-
);
40+
eprintln!("{} \nError: {}", color::Fg(color::Red), self);
4041
}
4142
}

src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ const NO_FILES_SELECTED_TO_ADD: &str = "no files selected to add";
1212

1313
const NOTHING_TO_COMMIT_MESSAGE: &str = "Nothing to commit, working tree clean.";
1414
const NO_REMOTE_INFO: &str = "No remote info.";
15-
15+
const GIT_SWITCH_UNCOMMITED_CHANGES_ERROR: &str =
16+
"error: Your local changes to the following files would be overwritten by checkout:";
17+
const SWITCHED_BRANCH: &str = "Switched to branch";
1618
const CURRENT_BRANCH_CHAR: char = '*';
1719
const COMMENT_CHAR: char = '#';
1820
const MODIFIED_CHAR: char = 'M';
@@ -34,7 +36,7 @@ impl std::fmt::Display for DisplayableCliCommand {
3436
let stdout = String::from_utf8_lossy(&self.0.stdout);
3537
let stderr = String::from_utf8_lossy(&self.0.stderr);
3638

37-
if stderr.is_empty() {
39+
if stderr.is_empty() && !stderr.contains(SWITCHED_BRANCH){
3840
return writeln!(f, "{}", stdout);
3941
}
4042
writeln!(f, "{}", stderr)

src/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ struct Args {
1717
/// interactive add
1818
#[arg(short, long, default_value_t = false)]
1919
iadd: bool,
20+
21+
/// fuzzy switch branch
22+
#[arg(short, long, default_value_t = false)]
23+
fuzzybranch: bool,
2024
}
2125

2226
fn main() {
@@ -25,6 +29,8 @@ fn main() {
2529
DuckCommands::Branch.run();
2630
} else if args.iadd {
2731
DuckCommands::Add.run();
32+
} else if args.fuzzybranch {
33+
DuckCommands::FuzzyBranchSwitch.run();
2834
} else if args.status {
2935
DuckCommands::Status.run();
3036
}

0 commit comments

Comments
 (0)