From a1156b028f2c415e2c87f7c1a8442f8a575e7885 Mon Sep 17 00:00:00 2001 From: Paulo Coelho <9609090+prscoelho@users.noreply.github.com> Date: Wed, 21 Feb 2024 18:27:50 +0000 Subject: [PATCH] cli `branch list`: list tracked branches by remote Add a way for users to list tracked branches by specifying remote names. This change keeps the current `--all` printing logic, but: - Skip over the branch altogether if it doesn't contain a specified tracked remote - Skip over the remote if it's not a specified tracked remote - Don't print the untracked_remote_refs at the end Usage: `jj branch list --tracked origin --tracked git` `jj branch list -t origin` --- CHANGELOG.md | 3 + cli/src/commands/branch.rs | 28 ++++++- cli/tests/cli-reference@.md.snap | 3 +- cli/tests/test_branch_command.rs | 130 +++++++++++++++++++++++++++++++ docs/branches.md | 15 ++++ 5 files changed, 175 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac78133347..eb252dd655 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added completions for [Nushell](https://nushell.sh) to `jj util completion` +* `jj branch list` now supports a `--tracked/-t ` option which can be used + multiple times to specify multiple remotes. + ### Fixed bugs * On Windows, symlinks in the repo are now materialized as regular files in the diff --git a/cli/src/commands/branch.rs b/cli/src/commands/branch.rs index 570da5d78a..4afe29289f 100644 --- a/cli/src/commands/branch.rs +++ b/cli/src/commands/branch.rs @@ -100,9 +100,13 @@ pub struct BranchDeleteArgs { pub struct BranchListArgs { /// Show all tracking and non-tracking remote branches including the ones /// whose targets are synchronized with the local branches. - #[arg(long, short, conflicts_with_all = ["names", "revisions"])] + #[arg(long, short, conflicts_with_all = ["names", "revisions", "tracked"])] all: bool, + /// Show remote tracked branches for specified remotes + #[arg(long, short, conflicts_with_all = ["names", "revisions", "all"], value_name = "REMOTE")] + tracked: Vec, + /// Show branches whose local name matches /// /// By default, the specified name matches exactly. Use `glob:` prefix to @@ -685,6 +689,22 @@ fn cmd_branch_list( .into_iter() .partition::, _>(|&(_, remote_ref)| remote_ref.is_tracking()); + let tracked_found: HashSet<&str> = args + .tracked + .iter() + .filter(|queried_remote| { + tracking_remote_refs + .iter() + .any(|(tracking_remote, _)| tracking_remote == queried_remote) + }) + .map(String::as_str) + .collect(); + + let args_tracked = !args.tracked.is_empty(); + if args_tracked && tracked_found.is_empty() { + continue; + } + if branch_target.local_target.is_present() || !tracking_remote_refs.is_empty() { write!(formatter.labeled("branch"), "{name}")?; if branch_target.local_target.is_present() { @@ -696,7 +716,11 @@ fn cmd_branch_list( for &(remote, remote_ref) in &tracking_remote_refs { let synced = remote_ref.target == *branch_target.local_target; - if !args.all && synced { + + let skip_synced = synced && !args_tracked; + let skip_tracked = args_tracked && !tracked_found.contains(remote); + + if !args.all && (skip_synced || skip_tracked) { continue; } write!(formatter, " ")?; diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index 3201a151db..0451657566 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -2,7 +2,6 @@ source: cli/tests/test_generate_md_cli_help.rs description: "AUTO-GENERATED FILE, DO NOT EDIT. This cli reference is generated as an `insta` snapshot. MkDocs follows they symlink from docs/cli-reference.md to the snap. Unfortunately, `insta` unavoidably creates this header. Luckily, MkDocs ignores the header since it has the same format as Markdown headers. TODO: MkDocs may fail on Windows if symlinks are not enabled in the OS settings" --- - # Command-Line Help for `jj` @@ -302,6 +301,7 @@ For information about branches, see https://github.com/martinvonz/jj/blob/main/d Possible values: `true`, `false` +* `-t`, `--tracked ` — Show remote tracked branches for specified remotes * `-r`, `--revisions ` — Show branches whose local targets are in the given revisions @@ -1972,4 +1972,3 @@ For information about stale working copies, see https://github.com/martinvonz/jj This document was generated automatically by clap-markdown. - diff --git a/cli/tests/test_branch_command.rs b/cli/tests/test_branch_command.rs index 3795ae1893..fe8a27d6c2 100644 --- a/cli/tests/test_branch_command.rs +++ b/cli/tests/test_branch_command.rs @@ -1206,6 +1206,136 @@ fn test_branch_list_much_remote_divergence() { "###); } +#[test] +fn test_branch_list_tracked() { + let test_env = TestEnvironment::default(); + test_env.add_config("git.auto-local-branch = true"); + + // Initialize remote refs + test_env.jj_cmd_ok(test_env.env_root(), &["init", "remote", "--git"]); + let remote_path = test_env.env_root().join("remote"); + for branch in [ + "remote-sync", + "remote-unsync", + "remote-untrack", + "remote-delete", + ] { + test_env.jj_cmd_ok(&remote_path, &["new", "root()", "-m", branch]); + test_env.jj_cmd_ok(&remote_path, &["branch", "create", branch]); + } + test_env.jj_cmd_ok(&remote_path, &["new"]); + test_env.jj_cmd_ok(&remote_path, &["git", "export"]); + + // Initialize local refs + let mut remote_git_path = test_env.env_root().join("remote"); + remote_git_path.extend([".jj", "repo", "store", "git"]); + test_env.jj_cmd_ok( + test_env.env_root(), + &[ + "git", + "clone", + "--colocate", + remote_git_path.to_str().unwrap(), + "local", + ], + ); + + test_env.jj_cmd_ok(test_env.env_root(), &["init", "upstream", "--git"]); + + // Setup upstream + let mut upstream_git_path = test_env.env_root().join("upstream"); + test_env.jj_cmd_ok( + &upstream_git_path, + &["new", "root()", "-m", "upstream-sync"], + ); + test_env.jj_cmd_ok(&upstream_git_path, &["branch", "create", "upstream-sync"]); + test_env.jj_cmd_ok(&upstream_git_path, &["new"]); + test_env.jj_cmd_ok(&upstream_git_path, &["git", "export"]); + + upstream_git_path.extend([".jj", "repo", "store", "git"]); + + let local_path = test_env.env_root().join("local"); + + test_env.jj_cmd_ok( + &local_path, + &[ + "git", + "remote", + "add", + "upstream", + upstream_git_path.to_str().unwrap(), + ], + ); + test_env.jj_cmd_ok(&local_path, &["git", "fetch", "--all-remotes"]); + + test_env.jj_cmd_ok(&local_path, &["new", "root()", "-m", "local-only"]); + test_env.jj_cmd_ok(&local_path, &["branch", "create", "local-only"]); + + // Mutate refs in local repository + test_env.jj_cmd_ok(&local_path, &["branch", "delete", "remote-delete"]); + test_env.jj_cmd_ok(&local_path, &["branch", "delete", "remote-untrack"]); + test_env.jj_cmd_ok(&local_path, &["branch", "untrack", "remote-untrack@origin"]); + test_env.jj_cmd_ok( + &local_path, + &["branch", "set", "--allow-backwards", "remote-unsync"], + ); + + insta::assert_snapshot!( + test_env.jj_cmd_success(&local_path, &["branch", "list", "--all"]), @r###" + local-only: nmzmmopx e1da745b (empty) local-only + @git: nmzmmopx e1da745b (empty) local-only + remote-delete (deleted) + @origin: mnmymoky 203e60eb (empty) remote-delete + (this branch will be *deleted permanently* on the remote on the next `jj git push`. Use `jj branch forget` to prevent this) + remote-sync: zwtyzrop c761c7ea (empty) remote-sync + @git: zwtyzrop c761c7ea (empty) remote-sync + @origin: zwtyzrop c761c7ea (empty) remote-sync + remote-unsync: nmzmmopx e1da745b (empty) local-only + @git: nmzmmopx e1da745b (empty) local-only + @origin (ahead by 1 commits, behind by 1 commits): qpsqxpyq 38ef8af7 (empty) remote-unsync + remote-untrack@origin: vmortlor 71a16b05 (empty) remote-untrack + upstream-sync: lolpmnqw 32fa6da0 (empty) upstream-sync + @git: lolpmnqw 32fa6da0 (empty) upstream-sync + @upstream: lolpmnqw 32fa6da0 (empty) upstream-sync + "###); + + insta::assert_snapshot!( + test_env.jj_cmd_success(&local_path, &["branch", "list", "--tracked", "origin"]), @r###" + remote-delete (deleted) + @origin: mnmymoky 203e60eb (empty) remote-delete + (this branch will be *deleted permanently* on the remote on the next `jj git push`. Use `jj branch forget` to prevent this) + remote-sync: zwtyzrop c761c7ea (empty) remote-sync + @origin: zwtyzrop c761c7ea (empty) remote-sync + remote-unsync: nmzmmopx e1da745b (empty) local-only + @origin (ahead by 1 commits, behind by 1 commits): qpsqxpyq 38ef8af7 (empty) remote-unsync + "### + ); + + // Querying for a non-existant remote results in no branches shown + insta::assert_snapshot!( + test_env.jj_cmd_success(&local_path, &["branch", "list", "--tracked", "non-existant"]), @"" + ); + + insta::assert_snapshot!( + test_env.jj_cmd_success(&local_path, &["branch", "list", "-t", "git", "-t", "origin", "-t", "upstream"]), @r###" + local-only: nmzmmopx e1da745b (empty) local-only + @git: nmzmmopx e1da745b (empty) local-only + remote-delete (deleted) + @origin: mnmymoky 203e60eb (empty) remote-delete + (this branch will be *deleted permanently* on the remote on the next `jj git push`. Use `jj branch forget` to prevent this) + remote-sync: zwtyzrop c761c7ea (empty) remote-sync + @git: zwtyzrop c761c7ea (empty) remote-sync + @origin: zwtyzrop c761c7ea (empty) remote-sync + remote-unsync: nmzmmopx e1da745b (empty) local-only + @git: nmzmmopx e1da745b (empty) local-only + @origin (ahead by 1 commits, behind by 1 commits): qpsqxpyq 38ef8af7 (empty) remote-unsync + upstream-sync: lolpmnqw 32fa6da0 (empty) upstream-sync + @git: lolpmnqw 32fa6da0 (empty) upstream-sync + @upstream: lolpmnqw 32fa6da0 (empty) upstream-sync + "### + ); +} + 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]) diff --git a/docs/branches.md b/docs/branches.md index aec2b04142..046e73ab81 100644 --- a/docs/branches.md +++ b/docs/branches.md @@ -113,6 +113,21 @@ $ # The local branch (e.g. stuff) is unaffected. It may or may not still $ # be tracking branches on other remotes (e.g. stuff@upstream). ``` +### Listing tracked branches + +To list tracked branches, you can `jj branch list --tracked ` or `jj branch list -t `. + +You can also list multiple remotes `jj branch list -t -t `. + +Example: + +```sh +# List all tracked @origin branches. +$ jj branch list --tracked origin +# List all tracked @origin or @git branches. +$ jj branch list -t origin -t git +``` + ### Automatic tracking of branches & `git.auto-local-branch` option There are two situations where `jj` tracks branches automatically. `jj git