Skip to content

Commit 95ef90e

Browse files
committed
feat: --checkout flag support in branch_fetch
1 parent ea21458 commit 95ef90e

File tree

4 files changed

+127
-93
lines changed

4 files changed

+127
-93
lines changed

src/cli/branch_fetch.rs

+89-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use documented::{Documented, DocumentedFields};
22

33
use super::flags::CliFlag;
4-
use super::{CliParseError, HelpOrVersion, LocalFlag, SubCommand};
4+
use super::{CliParseError, Flag, HelpOrVersion, LocalFlag, SubCommand};
55

66
/// A branch
77
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Documented, DocumentedFields)]
@@ -17,10 +17,11 @@ pub struct Branch {
1717
}
1818

1919
/// Fetch branches for a GitHub repository as a local branch
20-
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Documented, DocumentedFields)]
20+
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Documented)]
2121
pub struct BranchFetch {
2222
/// A list of branches to fetch
2323
pub branches: Vec<Branch>,
24+
pub checkout: bool,
2425
}
2526

2627
impl BranchFetch {
@@ -45,18 +46,24 @@ impl SubCommand for BranchFetch {
4546
global_flag: &mut HelpOrVersion,
4647
) -> Result<Self, CliParseError> {
4748
let mut branches: Vec<Branch> = vec![];
49+
let mut checkout = false;
4850

4951
for arg in args.by_ref() {
5052
if let Ok(flag) = arg.parse::<HelpOrVersion>() {
5153
global_flag.validate(flag)?;
5254
continue;
5355
}
5456

55-
// Non-flag arguments for branch-fetch are always branch names with optional
56-
// commits
57-
if let Some(local_flag) = LocalFlag::parse(&arg)? {
58-
// Only global flags should be parsed for branch-fetch
59-
return Err(CliParseError::UnexpectedFlag(local_flag));
57+
match dbg!(LocalFlag::parse(&arg)?) {
58+
Some(flag @ LocalFlag::Checkout) => {
59+
if checkout {
60+
return Err(CliParseError::DuplicateFlag(Flag::LocalFlag(flag)));
61+
}
62+
checkout = true;
63+
continue;
64+
},
65+
Some(flag) => return Err(CliParseError::UnexpectedFlag(flag)),
66+
None => (),
6067
}
6168

6269
let (branch_name, commit) = match arg.split_once('@') {
@@ -87,7 +94,11 @@ impl SubCommand for BranchFetch {
8794
});
8895
}
8996

90-
Ok(BranchFetch { branches })
97+
if checkout && branches.is_empty() {
98+
return Err(CliParseError::CheckoutNoSource);
99+
}
100+
101+
Ok(BranchFetch { branches, checkout })
91102
}
92103
}
93104

@@ -111,6 +122,7 @@ mod tests {
111122
name: "master".to_owned(),
112123
commit: None,
113124
}],
125+
checkout: false,
114126
})),
115127
help_or_version: HelpOrVersion::None,
116128
})
@@ -141,6 +153,36 @@ mod tests {
141153
commit: None,
142154
}
143155
],
156+
checkout: false,
157+
})),
158+
help_or_version: HelpOrVersion::None,
159+
})
160+
);
161+
// with checkout flag
162+
assert_eq!(
163+
patchy(&[
164+
"branch-fetch",
165+
"-c",
166+
"helix-editor/helix/master",
167+
"helix-editor/helix/develop"
168+
]),
169+
Ok(Cli {
170+
subcommand: Some(Subcommand::BranchFetch(BranchFetch {
171+
branches: vec![
172+
Branch {
173+
repo_owner: "helix-editor".to_owned(),
174+
repo_name: "helix".to_owned(),
175+
name: "master".to_owned(),
176+
commit: None,
177+
},
178+
Branch {
179+
repo_owner: "helix-editor".to_owned(),
180+
repo_name: "helix".to_owned(),
181+
name: "develop".to_owned(),
182+
commit: None,
183+
}
184+
],
185+
checkout: true,
144186
})),
145187
help_or_version: HelpOrVersion::None,
146188
})
@@ -159,6 +201,7 @@ mod tests {
159201
name: "master".to_owned(),
160202
commit: Some("6049f20".to_owned()),
161203
}],
204+
checkout: false,
162205
})),
163206
help_or_version: HelpOrVersion::None,
164207
})
@@ -196,6 +239,7 @@ mod tests {
196239
commit: Some("abc123".to_owned()),
197240
}
198241
],
242+
checkout: false,
199243
})),
200244
help_or_version: HelpOrVersion::None,
201245
})
@@ -205,7 +249,11 @@ mod tests {
205249
#[test]
206250
fn multiple_at_in_branch_name() {
207251
assert_eq!(
208-
patchy(&["branch-fetch", "owner/repo/branch@commit@extra"]),
252+
patchy(&[
253+
"branch-fetch",
254+
"owner/repo/branch@commit@extra",
255+
"--checkout"
256+
]),
209257
Ok(Cli {
210258
subcommand: Some(Subcommand::BranchFetch(BranchFetch {
211259
branches: vec![Branch {
@@ -214,6 +262,7 @@ mod tests {
214262
name: "branch".to_owned(),
215263
commit: Some("commit@extra".to_owned()),
216264
},],
265+
checkout: true,
217266
})),
218267
help_or_version: HelpOrVersion::None,
219268
})
@@ -225,20 +274,48 @@ mod tests {
225274
assert_eq!(
226275
patchy(&["branch-fetch", "--help"]),
227276
Ok(Cli {
228-
subcommand: Some(Subcommand::BranchFetch(BranchFetch { branches: vec![] })),
277+
subcommand: Some(Subcommand::BranchFetch(BranchFetch {
278+
branches: vec![],
279+
checkout: false
280+
})),
229281
help_or_version: HelpOrVersion::Help,
230282
})
231283
);
232284

233285
assert_eq!(
234286
patchy(&["branch-fetch", "--version"]),
235287
Ok(Cli {
236-
subcommand: Some(Subcommand::BranchFetch(BranchFetch { branches: vec![] })),
288+
subcommand: Some(Subcommand::BranchFetch(BranchFetch {
289+
branches: vec![],
290+
checkout: false
291+
})),
237292
help_or_version: HelpOrVersion::Version,
238293
})
239294
);
240295
}
241296

297+
#[test]
298+
fn duplicate_checkout() {
299+
assert_eq!(
300+
patchy(&["branch-fetch", "some/branch/somewhere", "-c", "--checkout"]),
301+
Err(CliParseError::DuplicateFlag(Flag::LocalFlag(
302+
LocalFlag::Checkout
303+
)))
304+
);
305+
}
306+
307+
#[test]
308+
fn checkout_with_no_source() {
309+
assert_eq!(
310+
patchy(&["branch-fetch", "-c"]),
311+
Err(CliParseError::CheckoutNoSource)
312+
);
313+
assert_eq!(
314+
patchy(&["branch-fetch", "--checkout"]),
315+
Err(CliParseError::CheckoutNoSource)
316+
);
317+
}
318+
242319
#[test]
243320
fn invalid_flags() {
244321
assert_eq!(
@@ -247,7 +324,7 @@ mod tests {
247324
);
248325
assert_eq!(
249326
patchy(&["branch-fetch", "--checkout"]),
250-
Err(CliParseError::UnexpectedFlag(LocalFlag::Checkout))
327+
Err(CliParseError::CheckoutNoSource)
251328
);
252329
assert_eq!(
253330
patchy(&["branch-fetch", "--branch-name=test"]),

src/cli/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ pub mod run;
1313
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
1414
pub enum CliParseError {
1515
UnexpectedFlag(LocalFlag),
16+
// --checkout, but where exactly...? No source supplied.
17+
CheckoutNoSource,
1618
UnknownFlag(String),
1719
InvalidArgument(String),
1820
InvalidRepo(String),
@@ -62,6 +64,11 @@ impl fmt::Display for CliParseError {
6264
write!(f, "{pr} must be followed by a commit hash")
6365
},
6466
CliParseError::InvalidRepo(repo) => write!(f, "Invalid repo: {repo}"),
67+
CliParseError::CheckoutNoSource => write!(
68+
f,
69+
"Expected at least 1 argument when using the {} flag",
70+
LocalFlag::Checkout
71+
),
6572
}
6673
}
6774
}

src/cli/pr_fetch.rs

+16
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ impl SubCommand for PrFetch {
117117
}
118118
}
119119

120+
if checkout && prs.is_empty() {
121+
return Err(CliParseError::CheckoutNoSource);
122+
}
123+
120124
Ok(PrFetch {
121125
checkout,
122126
remote_name: repo_name,
@@ -577,6 +581,18 @@ mod tests {
577581
);
578582
}
579583

584+
#[test]
585+
fn checkout_with_no_source() {
586+
assert_eq!(
587+
patchy(&["pr-fetch", "-c"]),
588+
Err(CliParseError::CheckoutNoSource)
589+
);
590+
assert_eq!(
591+
patchy(&["pr-fetch", "--checkout"]),
592+
Err(CliParseError::CheckoutNoSource)
593+
);
594+
}
595+
580596
#[test]
581597
fn forgot_flag_dash() {
582598
assert_eq!(

src/commands/branch_fetch.rs

+15-81
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,11 @@
11
use colored::Colorize as _;
22

3-
use super::run::parse_if_maybe_hash;
43
use crate::cli::branch_fetch::BranchFetch;
54
use crate::git_commands::{GIT, fetch_branch};
65
use crate::{fail, success};
76

8-
pub struct Item {
9-
/// # Examples
10-
///
11-
/// helix-editor/helix
12-
pub repo: String,
13-
/// # Examples
14-
///
15-
/// master
16-
pub branch: String,
17-
/// If specified, use a custom branch name instead of a generated one
18-
///
19-
/// # Examples
20-
///
21-
/// my-custom-branch123
22-
pub local_branch_name: Option<String>,
23-
/// If specified, do a **hard reset** to this commit when fetching the
24-
/// branch
25-
///
26-
/// # Examples
27-
///
28-
/// 6049f2035
29-
pub commit_hash: Option<String>,
30-
}
31-
32-
impl Item {
33-
pub fn new(
34-
repo: String,
35-
branch: String,
36-
local_branch_name: Option<String>,
37-
commit_hash: Option<String>,
38-
) -> Self {
39-
Self {
40-
repo,
41-
branch,
42-
local_branch_name,
43-
commit_hash,
44-
}
45-
}
46-
47-
pub fn create(arg: &str) -> anyhow::Result<Self> {
48-
let (remote, hash) = parse_if_maybe_hash(arg, "@");
49-
50-
let (repo, branch) = remote.rsplit_once('/').ok_or_else(|| {
51-
anyhow::anyhow!(
52-
"Invalid format: {}, skipping. Valid format is: username/repo/branch. Example: \
53-
helix-editor/helix/master",
54-
remote
55-
)
56-
})?;
57-
58-
Ok(Self::new(repo.to_owned(), branch.to_owned(), None, hash))
59-
}
60-
61-
#[must_use]
62-
pub fn with_branch_name(mut self, branch_name: Option<String>) -> Self {
63-
self.local_branch_name = branch_name;
64-
self
65-
}
66-
}
67-
687
pub async fn branch_fetch(args: BranchFetch) -> anyhow::Result<()> {
69-
#[expect(
70-
clippy::unused_enumerate_index,
71-
reason = "The commented code will use this. TODO"
72-
)]
73-
for (_i, branch) in args.branches.into_iter().enumerate() {
8+
for (i, branch) in args.branches.into_iter().enumerate() {
749
match fetch_branch(&branch).await {
7510
Ok((_, info)) => {
7611
success!(
@@ -90,21 +25,20 @@ pub async fn branch_fetch(args: BranchFetch) -> anyhow::Result<()> {
9025

9126
// If user uses --checkout flag, we're going to checkout the
9227
// first fetched branch
93-
// if i == 0 && args.checkout {
94-
// if let Err(cant_checkout) = GIT(&["checkout",
95-
// &info.branch.local_branch_name]) {
96-
// fail!(
97-
// "Could not check out branch
98-
// {}:\n{cant_checkout}",
99-
// info.branch.local_branch_name
100-
// );
101-
// } else {
102-
// success!(
103-
// "Automatically checked out the first branch: {}",
104-
// info.branch.local_branch_name
105-
// );
106-
// }
107-
// }
28+
if i == 0 && args.checkout {
29+
if let Err(cant_checkout) = GIT(&["checkout", &info.branch.local_branch_name]) {
30+
fail!(
31+
"Could not check out branch
32+
{}:\n{cant_checkout}",
33+
info.branch.local_branch_name
34+
);
35+
} else {
36+
success!(
37+
"Automatically checked out the first branch: {}",
38+
info.branch.local_branch_name
39+
);
40+
}
41+
}
10842
},
10943
Err(err) => {
11044
fail!("{err}");

0 commit comments

Comments
 (0)