git export: export deletion of forgotten remote-tracking branches

This commit is contained in:
Ilya Grigoriev 2023-06-27 22:18:15 +00:00
parent b6a9423f38
commit 59b354992a
3 changed files with 41 additions and 13 deletions

View file

@ -332,23 +332,37 @@ pub fn export_refs(
let mut branches_to_delete = BTreeMap::new(); let mut branches_to_delete = BTreeMap::new();
let mut failed_branches = vec![]; let mut failed_branches = vec![];
let view = mut_repo.view(); let view = mut_repo.view();
// This list includes refs known to jj, namely all git-tracking refs and refs let jj_repo_iter_all_branches = view.branches().iter().flat_map(|(branch, target)| {
// for local jj branches. itertools::chain(
// Short-term TODO: use the fact that git refs other than local branches are target
// included. .local_target
.as_ref()
.map(|_| RefName::LocalBranch(branch.to_owned())),
target
.remote_targets
.keys()
.map(|remote| RefName::RemoteBranch {
branch: branch.to_string(),
remote: remote.to_string(),
}),
)
});
let jj_known_refs: HashSet<_> = view let jj_known_refs: HashSet<_> = view
.git_refs() .git_refs()
.keys() .keys()
.filter_map(|name| parse_git_ref(name)) .filter_map(|name| parse_git_ref(name))
.chain( .chain(jj_repo_iter_all_branches)
view.branches()
.keys()
.map(|branch| RefName::LocalBranch(branch.to_owned())),
)
.collect(); .collect();
for jj_known_ref in jj_known_refs { for jj_known_ref in jj_known_refs {
let new_branch = match &jj_known_ref { let new_branch = match &jj_known_ref {
RefName::LocalBranch(branch) => view.get_local_branch(branch), RefName::LocalBranch(branch) => view.get_local_branch(branch),
RefName::RemoteBranch { remote, branch } => {
// Currently, the only situation where this case occurs *and* new_branch !=
// old_branch is after a `jj branch forget`. So, in practice, for
// remote-tracking branches either `new_branch == old_branch` or
// `new_branch == None`.
view.get_remote_branch(branch, remote)
}
_ => continue, _ => continue,
}; };
let old_branch = view.get_git_ref(&to_git_ref_name(&jj_known_ref)); let old_branch = view.get_git_ref(&to_git_ref_name(&jj_known_ref));

View file

@ -1522,7 +1522,8 @@ pub fn print_failed_git_export(
"{}", "{}",
match branch_ref { match branch_ref {
RefName::LocalBranch(name) => name.to_string(), RefName::LocalBranch(name) => name.to_string(),
// Should never happen, only local branches are exported RefName::RemoteBranch { branch, remote } => format!("{branch}@{remote}"),
// Should never happen, tags and git_refs should never be exported
branch_ref => to_git_ref_name(branch_ref), branch_ref => to_git_ref_name(branch_ref),
} }
)?; )?;

View file

@ -318,6 +318,7 @@ fn test_branch_forget_fetched_branch() {
feature1: 9f01a0e04879 message feature1: 9f01a0e04879 message
"###); "###);
// TEST 1: with export-import
// Forget the branch // Forget the branch
test_env.jj_cmd_success(&repo_path, &["branch", "forget", "feature1"]); test_env.jj_cmd_success(&repo_path, &["branch", "forget", "feature1"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @""); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
@ -330,14 +331,24 @@ fn test_branch_forget_fetched_branch() {
// the ref in jj view's `git_refs` tracking the local git repo's remote-tracking // the ref in jj view's `git_refs` tracking the local git repo's remote-tracking
// branch. // branch.
// TODO: Show that jj git push is also a no-op // TODO: Show that jj git push is also a no-op
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["git", "export"]), @r###" insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["git", "export"]), @"");
Nothing changed.
"###);
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["git", "import"]), @r###" insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["git", "import"]), @r###"
Nothing changed. Nothing changed.
"###); "###);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @""); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
// We can fetch feature1 again.
let stdout = test_env.jj_cmd_success(&repo_path, &["git", "fetch", "--remote=origin"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
feature1: 9f01a0e04879 message
"###);
// TEST 2: No export/import (otherwise the same as test 1)
// Short-term TODO: While it looks like the bug above is fixed, correct behavior
// now actually depends on export/import.
test_env.jj_cmd_success(&repo_path, &["branch", "forget", "feature1"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
// Short-term TODO: Fix this BUG. It should be possible to fetch `feature1` // Short-term TODO: Fix this BUG. It should be possible to fetch `feature1`
// again. // again.
let stdout = test_env.jj_cmd_success(&repo_path, &["git", "fetch", "--remote=origin"]); let stdout = test_env.jj_cmd_success(&repo_path, &["git", "fetch", "--remote=origin"]);
@ -346,6 +357,8 @@ fn test_branch_forget_fetched_branch() {
"###); "###);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @""); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @"");
// TEST 3: fetch branch that was moved & forgotten
// Move the branch in the git repo. // Move the branch in the git repo.
git_repo git_repo
.commit( .commit(