From 2832d7c73951cce5dbba6f3db165de0365c03735 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 17 Jan 2023 14:27:49 +0100 Subject: [PATCH] config: allow configuration of git remotes for fetch and push operations The `git.fetch` and `git.push` keys can be used in the configuration file for the default to use in `jj git fetch` and `jj git push` operations. By defaut, "origin" is used in both cases. --- CHANGELOG.md | 4 ++++ docs/github.md | 18 ++++++++++++++++ src/commands.rs | 52 ++++++++++++++++++++++++--------------------- src/config.rs | 16 +++++++------- src/config/git.toml | 3 +++ 5 files changed, 61 insertions(+), 32 deletions(-) create mode 100644 src/config/git.toml diff --git a/CHANGELOG.md b/CHANGELOG.md index b6683dbf1..34e618ba5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,10 @@ 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`. +* 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. + ### Fixed bugs * When sharing the working copy with a Git repo, we used to forget to export diff --git a/docs/github.md b/docs/github.md index 7a8786eb0..bf3a4e035 100644 --- a/docs/github.md +++ b/docs/github.md @@ -126,3 +126,21 @@ For a detailed overview, how Jujutsu handles conflicts, revisit the [tutorial][t [http-auth]: https://github.com/martinvonz/jj/issues/469 [tut]: tutorial.md#Conflicts [stacked]: https://jg.gg/2018/09/29/stacked-diffs-versus-pull-requests/ + +## Using several remotes + +It is common to use several remotes when contributing to a shared repository. For example, +"upstream" can designate the remote where the changes will be merged through a pull-request +while "origin" is your private fork of the project. In this case, you might want to +`jj git fetch` from "upstream" and to `jj git push` to "origin". + +You can configure the default remotes to fetch from and push to in your configuration file +(for example `.jj/repo/config.toml`): + +```toml +[git] +fetch = "upstream" +push = "origin" +``` + +The default for both `git.fetch` and `git.push` is "origin". diff --git a/src/commands.rs b/src/commands.rs index 3d17dd3c1..025248eee 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -969,8 +969,8 @@ struct GitRemoteListArgs {} #[derive(clap::Args, Clone, Debug)] struct GitFetchArgs { /// The remote to fetch from (only named remotes are supported) - #[arg(long, default_value = "origin")] - remote: String, + #[arg(long)] + remote: Option, } /// Create a new repo backed by a clone of a Git repo @@ -996,8 +996,8 @@ struct GitCloneArgs { #[command(group(ArgGroup::new("what").args(&["branch", "all", "change"])))] struct GitPushArgs { /// The remote to push to (only named remotes are supported) - #[arg(long, default_value = "origin")] - remote: String, + #[arg(long)] + remote: Option, /// Push only this branch (can be repeated) #[arg(long, short)] branch: Vec, @@ -3901,14 +3901,15 @@ fn cmd_git_fetch( args: &GitFetchArgs, ) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; + let remote = args + .remote + .clone() + .unwrap_or_else(|| command.settings().config().get("git.fetch").unwrap()); 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 {}", &args.remote)); - with_remote_callbacks(ui, |cb| { - git::fetch(tx.mut_repo(), &git_repo, &args.remote, cb) - }) - .map_err(|err| user_error(err.to_string()))?; + 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)) + .map_err(|err| user_error(err.to_string()))?; workspace_command.finish_transaction(ui, tx)?; Ok(()) } @@ -4157,7 +4158,10 @@ fn cmd_git_push( args: &GitPushArgs, ) -> Result<(), CommandError> { let mut workspace_command = command.workspace_helper(ui)?; - + let remote = args + .remote + .clone() + .unwrap_or_else(|| command.settings().config().get("git.push").unwrap()); let mut tx; let mut branch_updates = vec![]; let mut seen_branches = hashset! {}; @@ -4167,7 +4171,7 @@ fn cmd_git_push( if !seen_branches.insert(branch_name.clone()) { continue; } - let push_action = classify_branch_push_action(branch_target, &args.remote); + let push_action = classify_branch_push_action(branch_target, &remote); match push_action { BranchPushAction::AlreadyMatches => {} BranchPushAction::LocalConflicted => {} @@ -4178,7 +4182,7 @@ fn cmd_git_push( } } tx = workspace_command - .start_transaction(&format!("push all branches to git remote {}", &args.remote)); + .start_transaction(&format!("push all branches to git remote {}", &remote)); } else if !args.branch.is_empty() { for branch_name in &args.branch { if !seen_branches.insert(branch_name.clone()) { @@ -4186,7 +4190,7 @@ fn cmd_git_push( } if let Some(update) = branch_updates_for_push( workspace_command.repo().as_repo_ref(), - &args.remote, + &remote, branch_name, )? { branch_updates.push((branch_name.clone(), update)); @@ -4194,14 +4198,14 @@ fn cmd_git_push( writeln!( ui, "Branch {}@{} already matches {}", - branch_name, &args.remote, branch_name + branch_name, &remote, branch_name )?; } } tx = workspace_command.start_transaction(&format!( "push {} to git remote {}", make_branch_term(&args.branch), - &args.remote + &remote )); } else if !args.change.is_empty() { // TODO: Allow specifying --branch and --change at the same time @@ -4218,7 +4222,7 @@ fn cmd_git_push( "change" }, commits.iter().map(|c| c.change_id().hex()).join(", "), - &args.remote + &remote )); for (change_str, commit) in std::iter::zip(args.change.iter(), commits) { let mut branch_name = format!( @@ -4257,14 +4261,14 @@ fn cmd_git_push( tx.mut_repo() .set_local_branch(branch_name.clone(), RefTarget::Normal(commit.id().clone())); if let Some(update) = - branch_updates_for_push(tx.mut_repo().as_repo_ref(), &args.remote, &branch_name)? + branch_updates_for_push(tx.mut_repo().as_repo_ref(), &remote, &branch_name)? { branch_updates.push((branch_name.clone(), update)); } else { writeln!( ui, "Branch {}@{} already matches {}", - branch_name, &args.remote, branch_name + branch_name, &remote, branch_name )?; } } @@ -4309,7 +4313,7 @@ fn cmd_git_push( if !seen_branches.insert(branch_name.clone()) { continue; } - let push_action = classify_branch_push_action(branch_target, &args.remote); + let push_action = classify_branch_push_action(branch_target, &remote); match push_action { BranchPushAction::AlreadyMatches => {} BranchPushAction::LocalConflicted => {} @@ -4326,7 +4330,7 @@ fn cmd_git_push( } tx = workspace_command.start_transaction(&format!( "push current branch(es) to git remote {}", - &args.remote + &remote )); } drop(seen_branches); @@ -4370,7 +4374,7 @@ fn cmd_git_push( // already been pushed. let mut old_heads = vec![]; for branch_target in repo.view().branches().values() { - if let Some(old_head) = branch_target.remote_targets.get(&args.remote) { + if let Some(old_head) = branch_target.remote_targets.get(&remote) { old_heads.extend(old_head.adds()); } } @@ -4402,7 +4406,7 @@ fn cmd_git_push( } } - writeln!(ui, "Branch changes to push to {}:", &args.remote)?; + writeln!(ui, "Branch changes to push to {}:", &remote)?; for (branch_name, update) in &branch_updates { match (&update.old_target, &update.new_target) { (Some(old_target), Some(new_target)) => { @@ -4449,7 +4453,7 @@ fn cmd_git_push( let git_repo = get_git_repo(repo.store())?; with_remote_callbacks(ui, |cb| { - git::push_updates(&git_repo, &args.remote, &ref_updates, cb) + git::push_updates(&git_repo, &remote, &ref_updates, cb) }) .map_err(|err| user_error(err.to_string()))?; git::import_refs(tx.mut_repo(), &git_repo)?; diff --git a/src/config.rs b/src/config.rs index 5bb7418ec..a041a24e4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -151,15 +151,15 @@ fn env_base() -> config::Config { pub fn default_config() -> config::Config { // Syntax error in default config isn't a user error. That's why defaults are // loaded by separate builder. + macro_rules! from_toml { + ($file:literal) => { + config::File::from_str(include_str!($file), config::FileFormat::Toml) + }; + } config::Config::builder() - .add_source(config::File::from_str( - include_str!("config/colors.toml"), - config::FileFormat::Toml, - )) - .add_source(config::File::from_str( - include_str!("config/merge_tools.toml"), - config::FileFormat::Toml, - )) + .add_source(from_toml!("config/colors.toml")) + .add_source(from_toml!("config/merge_tools.toml")) + .add_source(from_toml!("config/git.toml")) .build() .unwrap() } diff --git a/src/config/git.toml b/src/config/git.toml new file mode 100644 index 000000000..11e983c0f --- /dev/null +++ b/src/config/git.toml @@ -0,0 +1,3 @@ +[git] +push = "origin" +fetch = "origin"