cmd: when a single remote is defined, default to it for git fetch/push

A simple quick implementation of what I've suggested on discord
This commit is contained in:
Anton Bulakh 2023-04-04 16:34:12 +03:00 committed by Anton Bulakh
parent df7079033e
commit 791b821115
4 changed files with 90 additions and 15 deletions

View file

@ -47,7 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
based on terminal width. [#1043](https://github.com/martinvonz/jj/issues/1043)
* Nodes in the (text-based) graphical log output now use a `◉` symbol instead
of the letter `o`. The ASCII-based graph styles still use `o`.
of the letter `o`. The ASCII-based graph styles still use `o`.
* Commands that accept a diff format (`jj diff`, `jj interdiff`, `jj show`,
`jj log`, and `jj obslog`) now accept `--types` to show only the type of file
@ -77,6 +77,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `jj obslog` and `jj log` now show abandoned commits as hidden.
* `jj git fetch` and `jj git push` will now use the single defined remote even if it is not named "origin".
### Fixed bugs
* Modify/delete conflicts now include context lines
@ -211,7 +213,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[the documentation](docs/config.md).
* `jj print` was renamed to `jj cat`. `jj print` remains as an alias.
* In content that goes to the terminal, the ANSI escape byte (0x1b) is replaced
by a "␛" character. That prevents them from interfering with the ANSI escapes
jj itself writes.

View file

@ -7,6 +7,7 @@ use std::sync::Mutex;
use std::time::Instant;
use clap::{ArgGroup, Subcommand};
use config::ConfigError;
use itertools::Itertools;
use jujutsu_lib::backend::ObjectId;
use jujutsu_lib::git::{self, GitFetchError, GitPushError, GitRefUpdate};
@ -292,17 +293,12 @@ fn cmd_git_fetch(
args: &GitFetchArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let git_repo = get_git_repo(workspace_command.repo().store())?;
let remotes = if args.remotes.is_empty() {
const KEY: &str = "git.fetch";
let config = command.settings().config();
config
.get(KEY)
.or_else(|_| config.get_string(KEY).map(|r| vec![r]))?
get_default_fetch_remotes(ui, command, &git_repo)?
} else {
args.remotes.clone()
};
let repo = workspace_command.repo();
let git_repo = get_git_repo(repo.store())?;
let mut tx = workspace_command.start_transaction(&format!(
"fetch from git remote(s) {}",
remotes.iter().join(",")
@ -328,6 +324,47 @@ fn cmd_git_fetch(
Ok(())
}
fn get_single_remote(git_repo: &git2::Repository) -> Result<Option<String>, CommandError> {
let git_remotes = git_repo.remotes()?;
Ok(match git_remotes.len() {
1 => git_remotes.get(0).map(ToOwned::to_owned),
_ => None,
})
}
const DEFAULT_REMOTE: &str = "origin";
fn get_default_fetch_remotes(
ui: &mut Ui,
command: &CommandHelper,
git_repo: &git2::Repository,
) -> Result<Vec<String>, CommandError> {
const KEY: &str = "git.fetch";
let config = command.settings().config();
match config
.get(KEY)
.or_else(|_| config.get_string(KEY).map(|r| vec![r]))
{
// if nothing was explicitly configured, try to guess
Err(ConfigError::NotFound(_)) => {
if let Some(remote) = get_single_remote(git_repo)? {
if remote != DEFAULT_REMOTE {
writeln!(
ui.hint(),
"Fetching from the only existing remote: {}",
remote
)?;
}
Ok(vec![remote])
} else {
Ok(vec![DEFAULT_REMOTE.to_owned()])
}
}
r => Ok(r?),
}
}
fn absolute_git_source(cwd: &Path, source: &str) -> String {
// Git appears to turn URL-like source to absolute path if local git directory
// exits, and fails because '$PWD/https' is unsupported protocol. Since it would
@ -577,11 +614,14 @@ fn cmd_git_push(
args: &GitPushArgs,
) -> Result<(), CommandError> {
let mut workspace_command = command.workspace_helper(ui)?;
let git_repo = get_git_repo(workspace_command.repo().store())?;
let remote = if let Some(name) = &args.remote {
name.clone()
} else {
command.settings().config().get("git.push")?
get_default_push_remote(ui, command, &git_repo)?
};
let mut tx;
let mut branch_updates = vec![];
let mut seen_branches = hashset! {};
@ -871,7 +911,6 @@ fn cmd_git_push(
return Ok(());
}
let git_repo = get_git_repo(repo.store())?;
with_remote_callbacks(ui, |cb| {
git::push_updates(&git_repo, &remote, &ref_updates, cb)
})
@ -884,6 +923,27 @@ fn cmd_git_push(
Ok(())
}
fn get_default_push_remote(
ui: &mut Ui,
command: &CommandHelper,
git_repo: &git2::Repository,
) -> Result<String, CommandError> {
match command.settings().config().get_string("git.push") {
// similar to get_default_fetch_remotes
Err(ConfigError::NotFound(_)) => {
if let Some(remote) = get_single_remote(git_repo)? {
if remote != DEFAULT_REMOTE {
writeln!(ui.hint(), "Pushing to the only existing remote: {}", remote)?;
}
Ok(remote)
} else {
Ok(DEFAULT_REMOTE.to_owned())
}
}
r => Ok(r?),
}
}
fn branch_updates_for_push(
repo: &dyn Repo,
remote_name: &str,

View file

@ -1,10 +1,6 @@
[aliases]
# Placeholder: added by user
[git]
push = "origin"
fetch = "origin"
[revset-aliases]
# Placeholder: added by user

View file

@ -88,6 +88,23 @@ fn test_git_fetch_single_remote() {
let repo_path = test_env.env_root().join("repo");
add_git_remote(&test_env, &repo_path, "rem1");
test_env
.jj_cmd(&repo_path, &["git", "fetch"])
.assert()
.success()
.stderr("Fetching from the only existing remote: rem1\n");
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
rem1: 6a21102783e8 message
"###);
}
#[test]
fn test_git_fetch_single_remote_from_arg() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
add_git_remote(&test_env, &repo_path, "rem1");
test_env.jj_cmd_success(&repo_path, &["git", "fetch", "--remote", "rem1"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
rem1: 6a21102783e8 message