2
2
3
3
use std:: collections:: HashMap ;
4
4
5
- use anyhow:: bail;
5
+ use anyhow:: { anyhow , bail} ;
6
6
use console:: style;
7
7
use dialoguer:: Select ;
8
8
use git2:: BranchType ;
9
9
use git2:: Commit ;
10
- use git2:: Object ;
11
10
use git2:: Oid ;
12
11
use git2:: { Branch , Repository } ;
13
12
13
+ use crate :: config;
14
14
use crate :: format_ref;
15
15
16
+ pub ( crate ) struct CommitSelection < ' a > {
17
+ pub commit : Commit < ' a > ,
18
+ pub branch : Branch < ' a > ,
19
+ }
20
+
16
21
pub ( crate ) fn select_commit_to_amend < ' a > (
17
22
repo : & ' a Repository ,
18
- upstream : Option < Object < ' a > > ,
23
+ upstream : Option < CommitSelection > ,
19
24
max_commits : usize ,
20
25
message_pattern : Option < & str > ,
21
26
) -> Result < Commit < ' a > , anyhow:: Error > {
22
27
let mut walker = repo. revwalk ( ) ?;
23
28
walker. push_head ( ) ?;
24
29
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
27
32
. flatten ( )
28
33
. take_while ( |rev| * rev != upstream_oid)
29
34
. take ( max_commits)
30
35
. 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
32
54
} else {
33
55
walker
34
56
. flatten ( )
@@ -40,7 +62,9 @@ pub(crate) fn select_commit_to_amend<'a>(
40
62
bail ! (
41
63
"No commits between {} and {:?}" ,
42
64
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( ) )
44
68
) ;
45
69
}
46
70
let branches: HashMap < Oid , String > = repo
@@ -106,17 +130,27 @@ pub(crate) fn get_merge_base<'a>(
106
130
repo : & ' a Repository ,
107
131
head_branch : & ' a Branch ,
108
132
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
+ )
118
149
} 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
+ )
120
154
} else {
121
155
return Ok ( None ) ;
122
156
} ;
@@ -125,12 +159,15 @@ pub(crate) fn get_merge_base<'a>(
125
159
head_branch
126
160
. get ( )
127
161
. target ( )
128
- . expect ( "all branches should ahve a target" ) ,
162
+ . expect ( "all branches should have a target" ) ,
129
163
upstream. id ( ) ,
130
164
) ?;
131
165
let commit = repo. find_object ( mb, None ) . unwrap ( ) ;
132
166
133
- Ok ( Some ( commit) )
167
+ Ok ( Some ( CommitSelection {
168
+ commit : commit. peel_to_commit ( ) ?,
169
+ branch,
170
+ } ) )
134
171
}
135
172
136
173
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
146
183
. unwrap_or_else ( || "<unknown>" . into ( ) ) ;
147
184
first
148
185
}
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