1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use anyhow::Result;
use git2::{Config, Direction, Repository};
use crate::config;
use crate::config::ConfigValue;
use crate::simple_glob::{expand_refspec, ExpansionSide};
pub fn get_fetch_remote_ref(
repo: &Repository,
config: &Config,
branch: &str,
) -> Result<Option<String>> {
let remote_name = config::get_remote(config, branch)?;
get_remote_ref(repo, config, &remote_name, branch)
}
fn get_remote_ref(
repo: &Repository,
config: &Config,
remote_name: &str,
branch: &str,
) -> Result<Option<String>> {
let remote = repo.find_remote(remote_name)?;
let key = format!("branch.{}.merge", branch);
let ref_on_remote: ConfigValue<String> =
if let Some(ref_on_remote) = config::get(config, &key).read()? {
ref_on_remote
} else {
return Ok(None);
};
assert!(
ref_on_remote.starts_with("refs/"),
"'git config branch.{}.merge' should start with 'refs/'",
branch
);
if let Some(expanded) = expand_refspec(
&remote,
&ref_on_remote,
Direction::Fetch,
ExpansionSide::Right,
)? {
let exists = repo.find_reference(&expanded).is_ok();
if exists {
Ok(Some(expanded))
} else {
Ok(None)
}
} else {
Ok(None)
}
}
pub fn get_push_remote_ref(
repo: &Repository,
config: &Config,
branch: &str,
) -> Result<Option<String>> {
if let Some(RefOnRemote {
remote_name,
refname,
}) = get_push_ref_on_remote(config, branch)?
{
if let Some(remote_ref) = get_remote_ref(repo, config, &remote_name, &refname)? {
return Ok(Some(remote_ref));
}
}
Ok(None)
}
#[derive(Eq, PartialEq, Clone)]
struct RefOnRemote {
remote_name: String,
refname: String,
}
fn get_push_ref_on_remote(config: &Config, branch: &str) -> Result<Option<RefOnRemote>> {
let remote_name = config::get_push_remote(config, branch)?;
if let Some(merge) =
config::get(config, &format!("branch.{}.merge", branch)).parse_with(|ref_on_remote| {
Ok(RefOnRemote {
remote_name: remote_name.clone(),
refname: ref_on_remote.to_string(),
})
})?
{
return Ok(Some(merge.clone()));
}
let push_default = config::get(config, "push.default")
.with_default(&String::from("simple"))
.read()?
.expect("has default");
match push_default.as_str() {
"current" => Ok(Some(RefOnRemote {
remote_name: remote_name.to_string(),
refname: branch.to_string(),
})),
"upstream" | "tracking" | "simple" => {
panic!("The current branch foo has no upstream branch.");
}
"nothing" | "matching" => {
unimplemented!("push.default=nothing|matching is not implemented.")
}
_ => panic!("unexpected config push.default"),
}
}