mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 01:18:21 +00:00
Push with refspec (#2542)
* push: respect `branch.*.merge` when push default is upstream
This commit is contained in:
parent
a91132d187
commit
979fa68837
6 changed files with 141 additions and 6 deletions
|
|
@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
* After commit: jump back to unstaged area [[@tommady](https://github.com/tommady)] ([#2476](https://github.com/extrawurst/gitui/issues/2476))
|
||||
* The default key to close the commit error message popup is now the Escape key [[@wessamfathi](https://github.com/wessamfathi)] ([#2552](https://github.com/extrawurst/gitui/issues/2552))
|
||||
* use OSC52 copying in case other methods fail [[@naseschwarz](https://github.com/naseschwarz)] ([#2366](https://github.com/gitui-org/gitui/issues/2366))
|
||||
* push: respect `branch.*.merge` when push default is upstream [[@vlad-anger](https://github.com/vlad-anger)] ([#2542](https://github.com/gitui-org/gitui/pull/2542))
|
||||
|
||||
## [0.27.0] - 2024-01-14
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@ pub enum Error {
|
|||
#[error("git error:{0}")]
|
||||
Git(#[from] git2::Error),
|
||||
|
||||
///
|
||||
#[error("git config error: {0}")]
|
||||
GitConfig(String),
|
||||
|
||||
///
|
||||
#[error("strip prefix error: {0}")]
|
||||
StripPrefix(#[from] StripPrefixError),
|
||||
|
|
|
|||
|
|
@ -243,6 +243,25 @@ pub fn get_branch_remote(
|
|||
}
|
||||
}
|
||||
|
||||
/// Retrieve the upstream merge of a local `branch`,
|
||||
/// configured in "branch.*.merge"
|
||||
///
|
||||
/// For details check git2 `branch_upstream_merge`
|
||||
pub fn get_branch_upstream_merge(
|
||||
repo_path: &RepoPath,
|
||||
branch: &str,
|
||||
) -> Result<Option<String>> {
|
||||
let repo = repo(repo_path)?;
|
||||
let branch = repo.find_branch(branch, BranchType::Local)?;
|
||||
let reference = bytes2string(branch.get().name_bytes())?;
|
||||
let remote_name = repo.branch_upstream_merge(&reference).ok();
|
||||
if let Some(remote_name) = remote_name {
|
||||
Ok(Some(bytes2string(remote_name.as_ref())?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// returns whether the pull merge strategy is set to rebase
|
||||
pub fn config_is_pull_rebase(repo_path: &RepoPath) -> Result<bool> {
|
||||
let repo = repo(repo_path)?;
|
||||
|
|
@ -673,6 +692,49 @@ mod tests_branches {
|
|||
|
||||
assert!(get_branch_remote(repo_path, "foo").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_branch_no_upstream_merge_config() {
|
||||
let (_r, repo) = repo_init().unwrap();
|
||||
let root = repo.path().parent().unwrap();
|
||||
let repo_path: &RepoPath =
|
||||
&root.as_os_str().to_str().unwrap().into();
|
||||
|
||||
let upstream_merge_res =
|
||||
get_branch_upstream_merge(&repo_path, "master");
|
||||
assert!(
|
||||
upstream_merge_res.is_ok_and(|v| v.as_ref().is_none())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_branch_with_upstream_merge_config() {
|
||||
let (_r, repo) = repo_init().unwrap();
|
||||
let root = repo.path().parent().unwrap();
|
||||
let repo_path: &RepoPath =
|
||||
&root.as_os_str().to_str().unwrap().into();
|
||||
|
||||
let branch_name = "master";
|
||||
let upstrem_merge = "refs/heads/master";
|
||||
|
||||
let mut config = repo.config().unwrap();
|
||||
config
|
||||
.set_str(
|
||||
&format!("branch.{branch_name}.merge"),
|
||||
&upstrem_merge,
|
||||
)
|
||||
.expect("fail set branch merge config");
|
||||
|
||||
let upstream_merge_res =
|
||||
get_branch_upstream_merge(&repo_path, &branch_name);
|
||||
assert!(upstream_merge_res
|
||||
.as_ref()
|
||||
.is_ok_and(|v| v.as_ref().is_some()));
|
||||
assert_eq!(
|
||||
&upstream_merge_res.unwrap().unwrap(),
|
||||
upstrem_merge
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
|
|
@ -62,6 +62,52 @@ pub fn untracked_files_config_repo(
|
|||
Ok(ShowUntrackedFilesConfig::All)
|
||||
}
|
||||
|
||||
// see https://git-scm.com/docs/git-config#Documentation/git-config.txt-pushdefault
|
||||
/// represents `push.default` git config
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum PushDefaultStrategyConfig {
|
||||
Nothing,
|
||||
Current,
|
||||
Upstream,
|
||||
Simple,
|
||||
Matching,
|
||||
}
|
||||
|
||||
impl Default for PushDefaultStrategyConfig {
|
||||
fn default() -> Self {
|
||||
Self::Simple
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for PushDefaultStrategyConfig {
|
||||
type Error = crate::Error;
|
||||
fn try_from(
|
||||
value: &'a str,
|
||||
) -> std::result::Result<Self, Self::Error> {
|
||||
match value {
|
||||
"nothing" => Ok(Self::Nothing),
|
||||
"current" => Ok(Self::Current),
|
||||
"upstream" | "tracking" => Ok(Self::Upstream),
|
||||
"simple" => Ok(Self::Simple),
|
||||
"matching" => Ok(Self::Matching),
|
||||
_ => Err(crate::Error::GitConfig(format!(
|
||||
"malformed value for push.default: {value}, must be one of nothing, matching, simple, upstream or current"
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_default_strategy_config_repo(
|
||||
repo: &Repository,
|
||||
) -> Result<PushDefaultStrategyConfig> {
|
||||
(get_config_string_repo(repo, "push.default")?).map_or_else(
|
||||
|| Ok(PushDefaultStrategyConfig::default()),
|
||||
|entry_str| {
|
||||
PushDefaultStrategyConfig::try_from(entry_str.as_str())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
pub fn untracked_files_config(
|
||||
repo_path: &RepoPath,
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ pub use blame::{blame_file, BlameHunk, FileBlame};
|
|||
pub use branch::{
|
||||
branch_compare_upstream, checkout_branch, checkout_commit,
|
||||
config_is_pull_rebase, create_branch, delete_branch,
|
||||
get_branch_remote, get_branches_info,
|
||||
get_branch_remote, get_branch_upstream_merge, get_branches_info,
|
||||
merge_commit::merge_upstream_commit,
|
||||
merge_ff::branch_merge_upstream_fastforward,
|
||||
merge_rebase::merge_upstream_rebase, rename::rename_branch,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,12 @@ use crate::{
|
|||
progress::ProgressPercent,
|
||||
sync::{
|
||||
branch::branch_set_upstream_after_push,
|
||||
config::{
|
||||
push_default_strategy_config_repo,
|
||||
PushDefaultStrategyConfig,
|
||||
},
|
||||
cred::BasicAuthCredential,
|
||||
get_branch_upstream_merge,
|
||||
remotes::{proxy_auto, Callbacks},
|
||||
repository::repo,
|
||||
CommitId, RepoPath,
|
||||
|
|
@ -92,7 +97,7 @@ impl AsyncProgress for ProgressNotification {
|
|||
}
|
||||
|
||||
///
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum PushType {
|
||||
///
|
||||
Branch,
|
||||
|
|
@ -145,6 +150,9 @@ pub fn push_raw(
|
|||
let repo = repo(repo_path)?;
|
||||
let mut remote = repo.find_remote(remote)?;
|
||||
|
||||
let push_default_strategy =
|
||||
push_default_strategy_config_repo(&repo)?;
|
||||
|
||||
let mut options = PushOptions::new();
|
||||
options.proxy_options(proxy_auto());
|
||||
|
||||
|
|
@ -158,14 +166,28 @@ pub fn push_raw(
|
|||
(true, false) => "+",
|
||||
(false, false) => "",
|
||||
};
|
||||
let ref_type = match ref_type {
|
||||
let git_ref_type = match ref_type {
|
||||
PushType::Branch => "heads",
|
||||
PushType::Tag => "tags",
|
||||
};
|
||||
|
||||
let branch_name =
|
||||
format!("{branch_modifier}refs/{ref_type}/{branch}");
|
||||
remote.push(&[branch_name.as_str()], Some(&mut options))?;
|
||||
let mut push_ref =
|
||||
format!("{branch_modifier}refs/{git_ref_type}/{branch}");
|
||||
|
||||
if !delete
|
||||
&& ref_type == PushType::Branch
|
||||
&& push_default_strategy
|
||||
== PushDefaultStrategyConfig::Upstream
|
||||
{
|
||||
if let Ok(Some(branch_upstream_merge)) =
|
||||
get_branch_upstream_merge(repo_path, branch)
|
||||
{
|
||||
push_ref.push_str(&format!(":{branch_upstream_merge}"));
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("push to: {push_ref}");
|
||||
remote.push(&[push_ref], Some(&mut options))?;
|
||||
|
||||
if let Some((reference, msg)) =
|
||||
callbacks.get_stats()?.push_rejected_msg
|
||||
|
|
|
|||
Loading…
Reference in a new issue