forked from mirrors/jj
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.
This commit is contained in:
parent
e63818998d
commit
2832d7c739
5 changed files with 61 additions and 32 deletions
|
@ -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
|
and `remote_needle` as optional arguments and matches just the branches whose
|
||||||
name contains `branch_needle` and remote contains `remote_needle`.
|
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
|
### Fixed bugs
|
||||||
|
|
||||||
* When sharing the working copy with a Git repo, we used to forget to export
|
* When sharing the working copy with a Git repo, we used to forget to export
|
||||||
|
|
|
@ -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
|
[http-auth]: https://github.com/martinvonz/jj/issues/469
|
||||||
[tut]: tutorial.md#Conflicts
|
[tut]: tutorial.md#Conflicts
|
||||||
[stacked]: https://jg.gg/2018/09/29/stacked-diffs-versus-pull-requests/
|
[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".
|
||||||
|
|
|
@ -969,8 +969,8 @@ struct GitRemoteListArgs {}
|
||||||
#[derive(clap::Args, Clone, Debug)]
|
#[derive(clap::Args, Clone, Debug)]
|
||||||
struct GitFetchArgs {
|
struct GitFetchArgs {
|
||||||
/// The remote to fetch from (only named remotes are supported)
|
/// The remote to fetch from (only named remotes are supported)
|
||||||
#[arg(long, default_value = "origin")]
|
#[arg(long)]
|
||||||
remote: String,
|
remote: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new repo backed by a clone of a Git repo
|
/// 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"])))]
|
#[command(group(ArgGroup::new("what").args(&["branch", "all", "change"])))]
|
||||||
struct GitPushArgs {
|
struct GitPushArgs {
|
||||||
/// The remote to push to (only named remotes are supported)
|
/// The remote to push to (only named remotes are supported)
|
||||||
#[arg(long, default_value = "origin")]
|
#[arg(long)]
|
||||||
remote: String,
|
remote: Option<String>,
|
||||||
/// Push only this branch (can be repeated)
|
/// Push only this branch (can be repeated)
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
branch: Vec<String>,
|
branch: Vec<String>,
|
||||||
|
@ -3901,14 +3901,15 @@ fn cmd_git_fetch(
|
||||||
args: &GitFetchArgs,
|
args: &GitFetchArgs,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
let mut workspace_command = command.workspace_helper(ui)?;
|
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 repo = workspace_command.repo();
|
||||||
let git_repo = get_git_repo(repo.store())?;
|
let git_repo = get_git_repo(repo.store())?;
|
||||||
let mut tx =
|
let mut tx = workspace_command.start_transaction(&format!("fetch from git remote {}", &remote));
|
||||||
workspace_command.start_transaction(&format!("fetch from git remote {}", &args.remote));
|
with_remote_callbacks(ui, |cb| git::fetch(tx.mut_repo(), &git_repo, &remote, cb))
|
||||||
with_remote_callbacks(ui, |cb| {
|
.map_err(|err| user_error(err.to_string()))?;
|
||||||
git::fetch(tx.mut_repo(), &git_repo, &args.remote, cb)
|
|
||||||
})
|
|
||||||
.map_err(|err| user_error(err.to_string()))?;
|
|
||||||
workspace_command.finish_transaction(ui, tx)?;
|
workspace_command.finish_transaction(ui, tx)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -4157,7 +4158,10 @@ fn cmd_git_push(
|
||||||
args: &GitPushArgs,
|
args: &GitPushArgs,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
let mut workspace_command = command.workspace_helper(ui)?;
|
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 tx;
|
||||||
let mut branch_updates = vec![];
|
let mut branch_updates = vec![];
|
||||||
let mut seen_branches = hashset! {};
|
let mut seen_branches = hashset! {};
|
||||||
|
@ -4167,7 +4171,7 @@ fn cmd_git_push(
|
||||||
if !seen_branches.insert(branch_name.clone()) {
|
if !seen_branches.insert(branch_name.clone()) {
|
||||||
continue;
|
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 {
|
match push_action {
|
||||||
BranchPushAction::AlreadyMatches => {}
|
BranchPushAction::AlreadyMatches => {}
|
||||||
BranchPushAction::LocalConflicted => {}
|
BranchPushAction::LocalConflicted => {}
|
||||||
|
@ -4178,7 +4182,7 @@ fn cmd_git_push(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx = workspace_command
|
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() {
|
} else if !args.branch.is_empty() {
|
||||||
for branch_name in &args.branch {
|
for branch_name in &args.branch {
|
||||||
if !seen_branches.insert(branch_name.clone()) {
|
if !seen_branches.insert(branch_name.clone()) {
|
||||||
|
@ -4186,7 +4190,7 @@ fn cmd_git_push(
|
||||||
}
|
}
|
||||||
if let Some(update) = branch_updates_for_push(
|
if let Some(update) = branch_updates_for_push(
|
||||||
workspace_command.repo().as_repo_ref(),
|
workspace_command.repo().as_repo_ref(),
|
||||||
&args.remote,
|
&remote,
|
||||||
branch_name,
|
branch_name,
|
||||||
)? {
|
)? {
|
||||||
branch_updates.push((branch_name.clone(), update));
|
branch_updates.push((branch_name.clone(), update));
|
||||||
|
@ -4194,14 +4198,14 @@ fn cmd_git_push(
|
||||||
writeln!(
|
writeln!(
|
||||||
ui,
|
ui,
|
||||||
"Branch {}@{} already matches {}",
|
"Branch {}@{} already matches {}",
|
||||||
branch_name, &args.remote, branch_name
|
branch_name, &remote, branch_name
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tx = workspace_command.start_transaction(&format!(
|
tx = workspace_command.start_transaction(&format!(
|
||||||
"push {} to git remote {}",
|
"push {} to git remote {}",
|
||||||
make_branch_term(&args.branch),
|
make_branch_term(&args.branch),
|
||||||
&args.remote
|
&remote
|
||||||
));
|
));
|
||||||
} else if !args.change.is_empty() {
|
} else if !args.change.is_empty() {
|
||||||
// TODO: Allow specifying --branch and --change at the same time
|
// TODO: Allow specifying --branch and --change at the same time
|
||||||
|
@ -4218,7 +4222,7 @@ fn cmd_git_push(
|
||||||
"change"
|
"change"
|
||||||
},
|
},
|
||||||
commits.iter().map(|c| c.change_id().hex()).join(", "),
|
commits.iter().map(|c| c.change_id().hex()).join(", "),
|
||||||
&args.remote
|
&remote
|
||||||
));
|
));
|
||||||
for (change_str, commit) in std::iter::zip(args.change.iter(), commits) {
|
for (change_str, commit) in std::iter::zip(args.change.iter(), commits) {
|
||||||
let mut branch_name = format!(
|
let mut branch_name = format!(
|
||||||
|
@ -4257,14 +4261,14 @@ fn cmd_git_push(
|
||||||
tx.mut_repo()
|
tx.mut_repo()
|
||||||
.set_local_branch(branch_name.clone(), RefTarget::Normal(commit.id().clone()));
|
.set_local_branch(branch_name.clone(), RefTarget::Normal(commit.id().clone()));
|
||||||
if let Some(update) =
|
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));
|
branch_updates.push((branch_name.clone(), update));
|
||||||
} else {
|
} else {
|
||||||
writeln!(
|
writeln!(
|
||||||
ui,
|
ui,
|
||||||
"Branch {}@{} already matches {}",
|
"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()) {
|
if !seen_branches.insert(branch_name.clone()) {
|
||||||
continue;
|
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 {
|
match push_action {
|
||||||
BranchPushAction::AlreadyMatches => {}
|
BranchPushAction::AlreadyMatches => {}
|
||||||
BranchPushAction::LocalConflicted => {}
|
BranchPushAction::LocalConflicted => {}
|
||||||
|
@ -4326,7 +4330,7 @@ fn cmd_git_push(
|
||||||
}
|
}
|
||||||
tx = workspace_command.start_transaction(&format!(
|
tx = workspace_command.start_transaction(&format!(
|
||||||
"push current branch(es) to git remote {}",
|
"push current branch(es) to git remote {}",
|
||||||
&args.remote
|
&remote
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
drop(seen_branches);
|
drop(seen_branches);
|
||||||
|
@ -4370,7 +4374,7 @@ fn cmd_git_push(
|
||||||
// already been pushed.
|
// already been pushed.
|
||||||
let mut old_heads = vec![];
|
let mut old_heads = vec![];
|
||||||
for branch_target in repo.view().branches().values() {
|
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());
|
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 {
|
for (branch_name, update) in &branch_updates {
|
||||||
match (&update.old_target, &update.new_target) {
|
match (&update.old_target, &update.new_target) {
|
||||||
(Some(old_target), Some(new_target)) => {
|
(Some(old_target), Some(new_target)) => {
|
||||||
|
@ -4449,7 +4453,7 @@ fn cmd_git_push(
|
||||||
|
|
||||||
let git_repo = get_git_repo(repo.store())?;
|
let git_repo = get_git_repo(repo.store())?;
|
||||||
with_remote_callbacks(ui, |cb| {
|
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()))?;
|
.map_err(|err| user_error(err.to_string()))?;
|
||||||
git::import_refs(tx.mut_repo(), &git_repo)?;
|
git::import_refs(tx.mut_repo(), &git_repo)?;
|
||||||
|
|
|
@ -151,15 +151,15 @@ fn env_base() -> config::Config {
|
||||||
pub fn default_config() -> config::Config {
|
pub fn default_config() -> config::Config {
|
||||||
// Syntax error in default config isn't a user error. That's why defaults are
|
// Syntax error in default config isn't a user error. That's why defaults are
|
||||||
// loaded by separate builder.
|
// loaded by separate builder.
|
||||||
|
macro_rules! from_toml {
|
||||||
|
($file:literal) => {
|
||||||
|
config::File::from_str(include_str!($file), config::FileFormat::Toml)
|
||||||
|
};
|
||||||
|
}
|
||||||
config::Config::builder()
|
config::Config::builder()
|
||||||
.add_source(config::File::from_str(
|
.add_source(from_toml!("config/colors.toml"))
|
||||||
include_str!("config/colors.toml"),
|
.add_source(from_toml!("config/merge_tools.toml"))
|
||||||
config::FileFormat::Toml,
|
.add_source(from_toml!("config/git.toml"))
|
||||||
))
|
|
||||||
.add_source(config::File::from_str(
|
|
||||||
include_str!("config/merge_tools.toml"),
|
|
||||||
config::FileFormat::Toml,
|
|
||||||
))
|
|
||||||
.build()
|
.build()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
3
src/config/git.toml
Normal file
3
src/config/git.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[git]
|
||||||
|
push = "origin"
|
||||||
|
fetch = "origin"
|
Loading…
Reference in a new issue