forked from mirrors/jj
git fetch: accept several remotes
The "--remote" option can be repeated, and the "git.fetch" key is now a list.
This commit is contained in:
parent
4550b9c481
commit
af9471e65c
4 changed files with 89 additions and 22 deletions
|
@ -74,9 +74,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
and `remote_needle` as optional arguments and matches just the branches whose
|
||||
name contains `branch_needle` and remote contains `remote_needle`.
|
||||
|
||||
* `jj git fetch` accepts repeated `--remote` arguments.
|
||||
|
||||
* Default remotes can be configured for the `jj git fetch` and `jj git push`
|
||||
operations ("origin" by default) using the `git.fetch` and `git.push`
|
||||
configuration entries.
|
||||
configuration entries. `git.fetch` can be a list if multiple remotes must
|
||||
be fetched from.
|
||||
|
||||
* `jj duplicate` can now duplicate multiple changes in one go. This preserves
|
||||
any parent-child relationships between them. For example, the entire tree of
|
||||
|
|
|
@ -180,6 +180,21 @@
|
|||
"type": "boolean",
|
||||
"description": "Whether jj creates a local branch with the same name when it imports a remote-tracking branch from git. See https://github.com/martinvonz/jj/blob/main/docs/config.md#automatic-local-branch-creation",
|
||||
"default": true
|
||||
},
|
||||
"fetch": {
|
||||
"description": "The remote(s) from which commits are fetched",
|
||||
"default": "origin",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -85,9 +85,10 @@ pub struct GitRemoteListArgs {}
|
|||
/// Fetch from a Git remote
|
||||
#[derive(clap::Args, Clone, Debug)]
|
||||
pub struct GitFetchArgs {
|
||||
/// The remote to fetch from (only named remotes are supported)
|
||||
#[arg(long)]
|
||||
remote: Option<String>,
|
||||
/// The remote to fetch from (only named remotes are supported, can be
|
||||
/// repeated)
|
||||
#[arg(long = "remote", value_name = "remote")]
|
||||
remotes: Vec<String>,
|
||||
}
|
||||
|
||||
/// Create a new repo backed by a clone of a Git repo
|
||||
|
@ -238,24 +239,33 @@ fn cmd_git_fetch(
|
|||
args: &GitFetchArgs,
|
||||
) -> Result<(), CommandError> {
|
||||
let mut workspace_command = command.workspace_helper(ui)?;
|
||||
let remote = if let Some(name) = &args.remote {
|
||||
name.clone()
|
||||
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]))?
|
||||
} else {
|
||||
command.settings().config().get("git.fetch")?
|
||||
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 {}", &remote));
|
||||
with_remote_callbacks(ui, |cb| {
|
||||
git::fetch(
|
||||
tx.mut_repo(),
|
||||
&git_repo,
|
||||
&remote,
|
||||
cb,
|
||||
&command.settings().git_settings(),
|
||||
)
|
||||
})
|
||||
.map_err(|err| user_error(err.to_string()))?;
|
||||
let mut tx = workspace_command.start_transaction(&format!(
|
||||
"fetch from git remote(s) {}",
|
||||
remotes.iter().join(",")
|
||||
));
|
||||
for remote in remotes {
|
||||
with_remote_callbacks(ui, |cb| {
|
||||
git::fetch(
|
||||
tx.mut_repo(),
|
||||
&git_repo,
|
||||
&remote,
|
||||
cb,
|
||||
&command.settings().git_settings(),
|
||||
)
|
||||
})
|
||||
.map_err(|err| user_error(err.to_string()))?;
|
||||
}
|
||||
tx.finish(ui)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -57,15 +57,53 @@ fn test_git_fetch_single_remote_from_config() {
|
|||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_git_fetch_multiple_remotes() {
|
||||
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");
|
||||
add_git_remote(&test_env, &repo_path, "rem2");
|
||||
|
||||
test_env.jj_cmd_success(
|
||||
&repo_path,
|
||||
&["git", "fetch", "--remote", "rem1", "--remote", "rem2"],
|
||||
);
|
||||
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
|
||||
rem1: 9f01a0e04879 message
|
||||
rem2: 9f01a0e04879 message
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_git_fetch_multiple_remotes_from_config() {
|
||||
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");
|
||||
add_git_remote(&test_env, &repo_path, "rem2");
|
||||
test_env.add_config(r#"git.fetch = ["rem1", "rem2"]"#);
|
||||
|
||||
test_env.jj_cmd_success(&repo_path, &["git", "fetch"]);
|
||||
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
|
||||
rem1: 9f01a0e04879 message
|
||||
rem2: 9f01a0e04879 message
|
||||
"###);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_git_fetch_nonexistent_remote() {
|
||||
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");
|
||||
|
||||
let stderr = &test_env.jj_cmd_failure(&repo_path, &["git", "fetch", "--remote", "rem1"]);
|
||||
let stderr = &test_env.jj_cmd_failure(
|
||||
&repo_path,
|
||||
&["git", "fetch", "--remote", "rem1", "--remote", "rem2"],
|
||||
);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Error: No git remote named 'rem1'
|
||||
Error: No git remote named 'rem2'
|
||||
"###);
|
||||
// No remote should have been fetched as part of the failing transaction
|
||||
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
|
||||
|
@ -76,11 +114,12 @@ fn test_git_fetch_nonexistent_remote_from_config() {
|
|||
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");
|
||||
test_env.add_config(r#"git.fetch = "rem1""#);
|
||||
add_git_remote(&test_env, &repo_path, "rem1");
|
||||
test_env.add_config(r#"git.fetch = ["rem1", "rem2"]"#);
|
||||
|
||||
let stderr = &test_env.jj_cmd_failure(&repo_path, &["git", "fetch"]);
|
||||
insta::assert_snapshot!(stderr, @r###"
|
||||
Error: No git remote named 'rem1'
|
||||
Error: No git remote named 'rem2'
|
||||
"###);
|
||||
// No remote should have been fetched as part of the failing transaction
|
||||
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
|
||||
|
|
Loading…
Reference in a new issue