From 791b82111526c60729c3773ca5fe25a70057a045 Mon Sep 17 00:00:00 2001 From: Anton Bulakh Date: Tue, 4 Apr 2023 16:34:12 +0300 Subject: [PATCH] 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 --- CHANGELOG.md | 6 ++-- src/commands/git.rs | 78 ++++++++++++++++++++++++++++++++++++----- src/config/misc.toml | 4 --- tests/test_git_fetch.rs | 17 +++++++++ 4 files changed, 90 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61e008e293..0884171967 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/src/commands/git.rs b/src/commands/git.rs index 0bbafcec87..01ff9a4811 100644 --- a/src/commands/git.rs +++ b/src/commands/git.rs @@ -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, 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, 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 { + 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, diff --git a/src/config/misc.toml b/src/config/misc.toml index 437dd6e922..fd395c2dfa 100644 --- a/src/config/misc.toml +++ b/src/config/misc.toml @@ -1,10 +1,6 @@ [aliases] # Placeholder: added by user -[git] -push = "origin" -fetch = "origin" - [revset-aliases] # Placeholder: added by user diff --git a/tests/test_git_fetch.rs b/tests/test_git_fetch.rs index e33cf7ebb5..d4ae2b4784 100644 --- a/tests/test_git_fetch.rs +++ b/tests/test_git_fetch.rs @@ -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