ok/jj
1
0
Fork 0
forked from mirrors/jj

cmd: have jj branch list report git-tracking (@git) branches

This doesn't change the way @git branches are stored in `git_refs` as opposed
to inside `BranchTarget` like normal remote-tracking branches. There are
subtle differences in behavior with e.g. `jj branch forget` and I'm not sure
how easy it is to rewrite `jj git import/export` to support a different
way of storage.

I've decided to call these "local-git tracking branches" since they track
branches in the local git repository. "local git-tracking" branches sounds a
bit more natural, but these could be confused with there are no remote
git-tracking branches. If one had the idea these might exist, they would be
confused with remote-tracking branches in the local git repo.

This addresses a portion of #1666
This commit is contained in:
Ilya Grigoriev 2023-06-10 21:44:48 -07:00
parent 9963879d15
commit 8df945b71d
6 changed files with 45 additions and 7 deletions

View file

@ -101,6 +101,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
now shorter within the default log revset. You can override the default by
setting the `revsets.short-prefixes` config to a different revset.
* The last seen state of branches in the underlying git repo is now presented by
`jj branch list` as a remote called `git` (e.g. `main@git`). Such branches
exist in colocated repos or if you use `jj git export`.
### Fixed bugs
* Modify/delete conflicts now include context lines

View file

@ -27,7 +27,7 @@ use crate::op_store::RefTarget;
use crate::repo::{MutableRepo, Repo};
use crate::revset;
use crate::settings::GitSettings;
use crate::view::RefName;
use crate::view::{RefName, View};
#[derive(Error, Debug, PartialEq)]
pub enum GitImportError {
@ -60,6 +60,16 @@ fn local_branch_name_to_ref_name(branch: &str) -> String {
format!("refs/heads/{branch}")
}
// TODO: Eventually, git-tracking branches should no longer be stored in
// git_refs but with the other remote-tracking branches in BranchTarget. Note
// that there are important but subtle differences in behavior for, e.g. `jj
// branch forget`.
pub fn git_tracking_branches(view: &View) -> impl Iterator<Item = (&str, &RefTarget)> {
view.git_refs().iter().filter_map(|(ref_name, target)| {
ref_name_to_local_branch_name(ref_name).map(|branch_name| (branch_name, target))
})
}
fn prevent_gc(git_repo: &git2::Repository, id: &CommitId) -> Result<(), git2::Error> {
// If multiple processes do git::import_refs() in parallel, this can fail to
// acquire a lock file even with force=true.

View file

@ -3,6 +3,7 @@ use std::collections::BTreeSet;
use clap::builder::NonEmptyStringValueParser;
use itertools::Itertools;
use jujutsu_lib::backend::{CommitId, ObjectId};
use jujutsu_lib::git::git_tracking_branches;
use jujutsu_lib::op_store::RefTarget;
use jujutsu_lib::repo::Repo;
use jujutsu_lib::revset;
@ -258,6 +259,28 @@ fn cmd_branch_list(
let workspace_command = command.workspace_helper(ui)?;
let repo = workspace_command.repo();
let mut all_branches = repo.view().branches().clone();
for (branch_name, git_tracking_target) in git_tracking_branches(repo.view()) {
let branch_target = all_branches.entry(branch_name.to_owned()).or_default();
if branch_target.remote_targets.contains_key("git") {
// TODO(#1690): There should be a mechanism to prevent importing a
// remote named "git" in `jj git import`.
// TODO: This is not currently tested
writeln!(
ui.warning(),
"WARNING: Branch {branch_name} has a remote-tracking branch for a remote named \
`git`. Local-git tracking branches for it will not be shown.\nIt is recommended \
to rename that remote, as jj normally reserves the `@git` suffix to denote \
local-git tracking branches."
)?;
} else {
// TODO: `BTreeMap::try_insert` could be used here once that's stabilized.
branch_target
.remote_targets
.insert("git".to_string(), git_tracking_target.clone());
}
}
let print_branch_target =
|formatter: &mut dyn Formatter, target: Option<&RefTarget>| -> Result<(), CommandError> {
match target {
@ -293,15 +316,12 @@ fn cmd_branch_list(
let mut formatter = ui.stdout_formatter();
let formatter = formatter.as_mut();
for (name, branch_target) in repo.view().branches() {
for (name, branch_target) in all_branches {
write!(formatter.labeled("branch"), "{name}")?;
print_branch_target(formatter, branch_target.local_target.as_ref())?;
for (remote, remote_target) in branch_target
.remote_targets
.iter()
.sorted_by_key(|(name, _target)| name.to_owned())
{
for (remote, remote_target) in branch_target.remote_targets.iter() {
if Some(remote_target) == branch_target.local_target.as_ref() {
continue;
}

View file

@ -113,6 +113,8 @@ fn test_branch_forget_glob() {
"###);
}
// TODO: Test `jj branch list` with a remote named `git`
fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String {
let template = r#"branches ++ " " ++ commit_id.short()"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])

View file

@ -272,6 +272,7 @@ fn test_git_fetch_conflicting_branches_colocated() {
rem1 (conflicted):
+ f652c32197cf (no description set)
+ 6a21102783e8 message
@git (behind by 1 commits): f652c32197cf (no description set)
@rem1 (behind by 1 commits): 6a21102783e8 message
"###);
}

View file

@ -197,6 +197,7 @@ fn test_git_import_move_export_undo() {
test_env.jj_cmd_success(&repo_path, &["branch", "set", "a"]);
insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###"
a: 096dc80da670 (no description set)
@git (behind by 1 commits): 230dd059e1b0 (no description set)
"###);
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["git", "export"]), @"");