Skip to content

Commit 9a0387d

Browse files
committed
feat: Increase support for main-only workflows
* Correctly Look for remote branches if an explicit branch is named * Improve error message if the current branch is one of the default upstream branches.
1 parent 972cafe commit 9a0387d

File tree

3 files changed

+75
-23
lines changed

3 files changed

+75
-23
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Unreleased
22

3+
* Fix and improve the experience of working with a main-only workflow.
4+
- Provide a tailored error message if your current branch is the selected upstream branch
5+
- Work correctly with explicitly-defined remote upstream branches.
6+
37
# Version 0.2.5
48

59
* Correctly find git repos in parent dirs of CWD

src/config.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ use clap::Parser;
44

55
// Env vars that provide defaults for args
66
const MAX_COMMITS_VAR: &str = "GIT_INSTAFIX_MAX_COMMITS";
7+
const MAX_COMMITS_SETTING: &str = "instafix.max-commits";
78
const UPSTREAM_VAR: &str = "GIT_INSTAFIX_UPSTREAM";
9+
pub const UPSTREAM_SETTING: &str = "instafix.default-upstream-branch";
810
const REQUIRE_NEWLINE_VAR: &str = "GIT_INSTAFIX_REQUIRE_NEWLINE";
11+
const REQUIRE_NEWLINE_SETTING: &str = "instafix.require-newline";
912
const THEME_VAR: &str = "GIT_INSTAFIX_THEME";
13+
const THEME_SETTING: &str = "instafix.theme";
1014

1115
// Other defaults
1216
pub(crate) const DEFAULT_UPSTREAM_BRANCHES: &[&str] = &["main", "master", "develop", "trunk"];
@@ -101,17 +105,17 @@ fn args_to_config_using_git_config(args: Args) -> Result<Config, anyhow::Error>
101105
.unwrap_or_else(|| cfg.get_bool("instafix.squash").unwrap_or(false)),
102106
max_commits: args
103107
.max_commits
104-
.unwrap_or_else(|| cfg.get_i32("instafix.max-commits").unwrap_or(15) as usize),
108+
.unwrap_or_else(|| cfg.get_i32(MAX_COMMITS_SETTING).unwrap_or(15) as usize),
105109
commit_message_pattern: args.commit_message_pattern,
106110
default_upstream_branch: args
107111
.default_upstream_branch
108-
.or_else(|| cfg.get_string("instafix.default-upstream-branch").ok()),
112+
.or_else(|| cfg.get_string(UPSTREAM_SETTING).ok()),
109113
require_newline: args
110114
.require_newline
111-
.unwrap_or_else(|| cfg.get_bool("instafix.require-newline").unwrap_or(false)),
115+
.unwrap_or_else(|| cfg.get_bool(REQUIRE_NEWLINE_SETTING).unwrap_or(false)),
112116
help_themes: args.help_themes,
113117
theme: args.theme.unwrap_or_else(|| {
114-
cfg.get_string("instafix.theme")
118+
cfg.get_string(THEME_SETTING)
115119
.unwrap_or_else(|_| DEFAULT_THEME.to_string())
116120
}),
117121
})

src/selecter.rs

+63-19
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,55 @@
22
33
use std::collections::HashMap;
44

5-
use anyhow::bail;
5+
use anyhow::{anyhow, bail};
66
use console::style;
77
use dialoguer::Select;
88
use git2::BranchType;
99
use git2::Commit;
10-
use git2::Object;
1110
use git2::Oid;
1211
use git2::{Branch, Repository};
1312

13+
use crate::config;
1414
use crate::format_ref;
1515

16+
pub(crate) struct CommitSelection<'a> {
17+
pub commit: Commit<'a>,
18+
pub branch: Branch<'a>,
19+
}
20+
1621
pub(crate) fn select_commit_to_amend<'a>(
1722
repo: &'a Repository,
18-
upstream: Option<Object<'a>>,
23+
upstream: Option<CommitSelection>,
1924
max_commits: usize,
2025
message_pattern: Option<&str>,
2126
) -> Result<Commit<'a>, anyhow::Error> {
2227
let mut walker = repo.revwalk()?;
2328
walker.push_head()?;
2429
let commits = if let Some(upstream) = upstream.as_ref() {
25-
let upstream_oid = upstream.id();
26-
walker
30+
let upstream_oid = upstream.commit.id();
31+
let commits = walker
2732
.flatten()
2833
.take_while(|rev| *rev != upstream_oid)
2934
.take(max_commits)
3035
.map(|rev| repo.find_commit(rev))
31-
.collect::<Result<Vec<_>, _>>()?
36+
.collect::<Result<Vec<_>, _>>()?;
37+
38+
let head = repo.head()?;
39+
let current_branch_name = head
40+
.shorthand()
41+
.ok_or_else(|| anyhow!("HEAD is not a branch"))?;
42+
if repo.head()?.peel_to_commit()?.id() == upstream.commit.id()
43+
&& current_branch_name == upstream.branch.name().unwrap().unwrap()
44+
{
45+
let upstream_setting = config::UPSTREAM_SETTING;
46+
bail!(
47+
"HEAD is already pointing at a common upstream branch\n\
48+
If you don't create branches for your work consider setting upstream to a remote ref:\n\
49+
\n \
50+
git config {upstream_setting} origin/{current_branch_name}"
51+
)
52+
}
53+
commits
3254
} else {
3355
walker
3456
.flatten()
@@ -40,7 +62,9 @@ pub(crate) fn select_commit_to_amend<'a>(
4062
bail!(
4163
"No commits between {} and {:?}",
4264
format_ref(&repo.head()?)?,
43-
upstream.map(|u| u.id()).unwrap()
65+
upstream
66+
.map(|u| u.commit.id().to_string())
67+
.unwrap_or_else(|| "<no upstream>".to_string())
4468
);
4569
}
4670
let branches: HashMap<Oid, String> = repo
@@ -106,17 +130,27 @@ pub(crate) fn get_merge_base<'a>(
106130
repo: &'a Repository,
107131
head_branch: &'a Branch,
108132
upstream_name: Option<&str>,
109-
) -> Result<Option<Object<'a>>, anyhow::Error> {
110-
let upstream = if let Some(explicit_upstream_name) = upstream_name {
111-
let branch = repo.find_branch(explicit_upstream_name, BranchType::Local)?;
112-
branch.into_reference().peel_to_commit()?
113-
} else if let Some(branch) = crate::config::DEFAULT_UPSTREAM_BRANCHES
114-
.iter()
115-
.find_map(|b| repo.find_branch(b, BranchType::Local).ok())
116-
{
117-
branch.into_reference().peel_to_commit()?
133+
) -> Result<Option<CommitSelection<'a>>, anyhow::Error> {
134+
let (upstream, branch) = if let Some(explicit_upstream_name) = upstream_name {
135+
let mut bt = BranchType::Local;
136+
let branch = repo
137+
.find_branch(explicit_upstream_name, BranchType::Local)
138+
.or_else(|_| {
139+
bt = BranchType::Remote;
140+
repo.find_branch(explicit_upstream_name, BranchType::Remote)
141+
})?;
142+
let b2 = repo.find_branch(explicit_upstream_name, bt)?;
143+
(branch.into_reference().peel_to_commit()?, b2)
144+
} else if let Some(branch) = find_default_upstream_branch(repo) {
145+
(
146+
branch.into_reference().peel_to_commit()?,
147+
find_default_upstream_branch(repo).unwrap(),
148+
)
118149
} else if let Ok(upstream) = head_branch.upstream() {
119-
upstream.into_reference().peel_to_commit()?
150+
(
151+
upstream.into_reference().peel_to_commit()?,
152+
head_branch.upstream().unwrap(),
153+
)
120154
} else {
121155
return Ok(None);
122156
};
@@ -125,12 +159,15 @@ pub(crate) fn get_merge_base<'a>(
125159
head_branch
126160
.get()
127161
.target()
128-
.expect("all branches should ahve a target"),
162+
.expect("all branches should have a target"),
129163
upstream.id(),
130164
)?;
131165
let commit = repo.find_object(mb, None).unwrap();
132166

133-
Ok(Some(commit))
167+
Ok(Some(CommitSelection {
168+
commit: commit.peel_to_commit()?,
169+
branch,
170+
}))
134171
}
135172

136173
pub(crate) fn commit_id_and_summary(commits: &[Commit<'_>], idx: usize) -> String {
@@ -146,3 +183,10 @@ pub(crate) fn commit_id_and_summary(commits: &[Commit<'_>], idx: usize) -> Strin
146183
.unwrap_or_else(|| "<unknown>".into());
147184
first
148185
}
186+
187+
/// Check if any of the `config::DEFAULT_UPSTREAM_BRANCHES` exist in the repository
188+
fn find_default_upstream_branch(repo: &Repository) -> Option<Branch> {
189+
crate::config::DEFAULT_UPSTREAM_BRANCHES
190+
.iter()
191+
.find_map(|b| repo.find_branch(b, BranchType::Local).ok())
192+
}

0 commit comments

Comments
 (0)