From f8eaf0e8a07440b6f693f1fd0802b7f765e91149 Mon Sep 17 00:00:00 2001 From: Vladimir Petrzhikovskii Date: Tue, 13 Feb 2024 23:17:05 +0100 Subject: [PATCH] cli: list new remote branches during git fetch --- CHANGELOG.md | 1 + cli/src/cli_util.rs | 2 +- cli/src/commands/git.rs | 10 +-- cli/src/git_util.rs | 133 +++++++++++++++++++++++++++- cli/tests/test_branch_command.rs | 36 ++++++-- cli/tests/test_git_clone.rs | 6 ++ cli/tests/test_git_colocated.rs | 2 + cli/tests/test_git_fetch.rs | 61 ++++++++++--- cli/tests/test_git_import_export.rs | 16 +++- lib/src/git.rs | 19 ++-- 10 files changed, 248 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 593fb01494d..9ed1e9bdc2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#1252](https://github.com/martinvonz/jj/issues/1252), [#2971](https://github.com/martinvonz/jj/issues/2971)). This may become the default depending on feedback. +* `jj git fetch` now automatically prints new remote branches and tags by default. ### Fixed bugs diff --git a/cli/src/cli_util.rs b/cli/src/cli_util.rs index bcf4beeeed6..49b5b921a75 100644 --- a/cli/src/cli_util.rs +++ b/cli/src/cli_util.rs @@ -948,7 +948,7 @@ impl WorkspaceCommandHelper { return Ok(()); } - print_git_import_stats(ui, &stats)?; + print_git_import_stats(ui, tx.repo(), &stats, false)?; let mut tx = tx.into_inner(); // Rebase here to show slightly different status message. let num_rebased = tx.mut_repo().rebase_descendants(&self.settings)?; diff --git a/cli/src/commands/git.rs b/cli/src/commands/git.rs index 88317215128..3a7868c3d02 100644 --- a/cli/src/commands/git.rs +++ b/cli/src/commands/git.rs @@ -447,7 +447,7 @@ fn init_git_refs( if !tx.mut_repo().has_changes() { return Ok(repo); } - print_git_import_stats(ui, &stats)?; + print_git_import_stats(ui, repo.as_ref(), &stats, false)?; if colocated { // If git.auto-local-branch = true, local branches could be created for // the imported remote branches. @@ -537,7 +537,7 @@ fn cmd_git_fetch( GitFetchError::InternalGitError(err) => map_git_error(err), _ => user_error(err), })?; - print_git_import_stats(ui, &stats.import_stats)?; + print_git_import_stats(ui, tx.repo(), &stats.import_stats, true)?; } tx.finish( ui, @@ -726,7 +726,7 @@ fn do_git_clone( r#"Fetching into new repo in "{}""#, wc_path.display() )?; - let mut workspace_command = command.for_loaded_repo(ui, workspace, repo)?; + let mut workspace_command = command.for_loaded_repo(ui, workspace, repo.clone())?; maybe_add_gitignore(&workspace_command)?; git_repo.remote(remote_name, source).unwrap(); let mut fetch_tx = workspace_command.start_transaction(); @@ -751,7 +751,7 @@ fn do_git_clone( unreachable!("we didn't provide any globs") } })?; - print_git_import_stats(ui, &stats.import_stats)?; + print_git_import_stats(ui, repo.as_ref(), &stats.import_stats, true)?; fetch_tx.finish(ui, "fetch from git remote into empty repo")?; Ok((workspace_command, stats)) } @@ -1190,7 +1190,7 @@ fn cmd_git_import( // That's why cmd_git_export() doesn't export the HEAD ref. git::import_head(tx.mut_repo())?; let stats = git::import_refs(tx.mut_repo(), &command.settings().git_settings())?; - print_git_import_stats(ui, &stats)?; + print_git_import_stats(ui, tx.repo(), &stats, true)?; tx.finish(ui, "import git refs")?; Ok(()) } diff --git a/cli/src/git_util.rs b/cli/src/git_util.rs index df0b467e037..1762ed20d10 100644 --- a/cli/src/git_util.rs +++ b/cli/src/git_util.rs @@ -21,13 +21,17 @@ use std::sync::Mutex; use std::time::Instant; use std::{error, iter}; -use jj_lib::git::{self, FailedRefExport, FailedRefExportReason, GitImportStats}; +use itertools::Itertools; +use jj_lib::git::{self, FailedRefExport, FailedRefExportReason, GitImportStats, RefName}; use jj_lib::git_backend::GitBackend; -use jj_lib::repo::{ReadonlyRepo, Repo as _}; +use jj_lib::op_store::{RefTarget, RemoteRef}; +use jj_lib::repo::{ReadonlyRepo, Repo}; use jj_lib::store::Store; use jj_lib::workspace::Workspace; +use unicode_width::UnicodeWidthStr; use crate::cli_util::{user_error, CommandError}; +use crate::formatter::Formatter; use crate::progress::Progress; use crate::ui::Ui; @@ -172,7 +176,37 @@ pub fn with_remote_git_callbacks( f(callbacks) } -pub fn print_git_import_stats(ui: &mut Ui, stats: &GitImportStats) -> Result<(), CommandError> { +pub fn print_git_import_stats( + ui: &mut Ui, + repo: &dyn Repo, + stats: &GitImportStats, + show_ref_stats: bool, +) -> Result<(), CommandError> { + if show_ref_stats { + let refs_stats = stats + .changed_remote_refs + .iter() + .map(|(ref_name, (remote_ref, ref_target))| { + RefStatus::new(ref_name, remote_ref, ref_target, repo) + }) + .collect_vec(); + + let has_both_ref_kinds = refs_stats + .iter() + .any(|x| matches!(x.ref_kind, RefKind::Branch)) + && refs_stats + .iter() + .any(|x| matches!(x.ref_kind, RefKind::Tag)); + + let max_width = refs_stats.iter().map(|x| x.ref_name.width()).max(); + if let Some(max_width) = max_width { + let mut stderr = ui.stderr_formatter(); + for status in refs_stats { + status.output(max_width, has_both_ref_kinds, &mut *stderr)?; + } + } + } + if !stats.abandoned_commits.is_empty() { writeln!( ui.stderr(), @@ -180,9 +214,102 @@ pub fn print_git_import_stats(ui: &mut Ui, stats: &GitImportStats) -> Result<(), stats.abandoned_commits.len() )?; } + Ok(()) } +struct RefStatus { + ref_kind: RefKind, + ref_name: String, + tracking_status: TrackingStatus, + import_status: ImportStatus, +} + +impl RefStatus { + fn new( + ref_name: &RefName, + remote_ref: &RemoteRef, + ref_target: &RefTarget, + repo: &dyn Repo, + ) -> Self { + let mut tracking_status = TrackingStatus::Untracked; + let (ref_name, ref_kind) = match ref_name { + RefName::RemoteBranch { branch, remote } => { + if repo.view().get_remote_branch(branch, remote).is_tracking() { + tracking_status = TrackingStatus::Tracked; + } + (format!("{branch}@{remote}"), RefKind::Branch) + } + RefName::Tag(tag) => { + tracking_status = TrackingStatus::NotApplicable; + (tag.clone(), RefKind::Tag) + } + RefName::LocalBranch(branch) => (branch.clone(), RefKind::Branch), + }; + + let import_status = match (remote_ref.target.is_absent(), ref_target.is_absent()) { + (true, false) => ImportStatus::New, + (false, true) => ImportStatus::Deleted, + _ => ImportStatus::Updated, + }; + + Self { + ref_name, + tracking_status, + import_status, + ref_kind, + } + } + + fn output( + &self, + max_ref_name_width: usize, + has_both_ref_kinds: bool, + out: &mut dyn Formatter, + ) -> std::io::Result<()> { + let tracking_status = match self.tracking_status { + TrackingStatus::Tracked => "tracked", + TrackingStatus::Untracked => "untracked", + TrackingStatus::NotApplicable => "", + }; + + let import_status = match self.import_status { + ImportStatus::New => "new", + ImportStatus::Deleted => "deleted", + ImportStatus::Updated => "updated", + }; + + // This ensures alignment of the statuses that follow + let padded_ref_name = format!("{:width$}", self.ref_name, width = max_ref_name_width); + let ref_kind = match self.ref_kind { + RefKind::Branch => "branch: ", + RefKind::Tag if !has_both_ref_kinds => "tag: ", + RefKind::Tag => "tag: ", + }; + + write!(out, "{ref_kind}")?; + write!(out.labeled("branch"), "{padded_ref_name}")?; + writeln!(out, " [{import_status}] {tracking_status}") + } +} + +enum RefKind { + Branch, + Tag, +} + +enum TrackingStatus { + Tracked, + Untracked, + NotApplicable, // e.g. for tags +} + +enum ImportStatus { + New, + Deleted, + Updated, +} + pub fn print_failed_git_export( ui: &Ui, failed_branches: &[FailedRefExport], diff --git a/cli/tests/test_branch_command.rs b/cli/tests/test_branch_command.rs index e957e6d6676..3795ae18930 100644 --- a/cli/tests/test_branch_command.rs +++ b/cli/tests/test_branch_command.rs @@ -536,7 +536,9 @@ fn test_branch_forget_fetched_branch() { // We can fetch feature1 again. let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote=origin"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: feature1@origin [new] tracked + "###); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" feature1: mzyxwzks 9f01a0e0 message @origin: mzyxwzks 9f01a0e0 message @@ -548,7 +550,9 @@ fn test_branch_forget_fetched_branch() { // Fetch works even without the export-import let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote=origin"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: feature1@origin [new] tracked + "###); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" feature1: mzyxwzks 9f01a0e0 message @origin: mzyxwzks 9f01a0e0 message @@ -574,7 +578,9 @@ fn test_branch_forget_fetched_branch() { // Fetching a moved branch does not create a conflict let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote=origin"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: feature1@origin [new] tracked + "###); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" feature1: ooosovrs 38aefb17 (empty) another message @origin: ooosovrs 38aefb17 (empty) another message @@ -687,7 +693,12 @@ fn test_branch_track_untrack() { ); test_env.add_config("git.auto-local-branch = false"); let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: feature1@origin [new] untracked + branch: feature2@origin [new] untracked + branch: main@origin [new] untracked + + "###); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" feature1@origin: sptzoqmo 7b33f629 commit 1 feature2@origin: sptzoqmo 7b33f629 commit 1 @@ -759,7 +770,12 @@ fn test_branch_track_untrack() { ], ); let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: feature1@origin [updated] untracked + branch: feature2@origin [updated] untracked + branch: main@origin [updated] tracked + + "###); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" feature1: sptzoqmo 7b33f629 commit 1 feature1@origin: mmqqkyyt 40dabdaf commit 2 @@ -791,6 +807,10 @@ fn test_branch_track_untrack() { test_env.add_config("git.auto-local-branch = true"); let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); insta::assert_snapshot!(stderr, @r###" + branch: feature1@origin [updated] untracked + branch: feature2@origin [updated] untracked + branch: feature3@origin [new] tracked + branch: main@origin [updated] tracked Abandoned 1 commits that are no longer reachable. "###); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" @@ -847,7 +867,11 @@ fn test_branch_track_untrack_patterns() { // Fetch new commit without auto tracking test_env.add_config("git.auto-local-branch = false"); let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: feature1@origin [new] untracked + branch: feature2@origin [new] untracked + + "###); // Track local branch test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]); diff --git a/cli/tests/test_git_clone.rs b/cli/tests/test_git_clone.rs index dbf756459cc..a13e860530f 100644 --- a/cli/tests/test_git_clone.rs +++ b/cli/tests/test_git_clone.rs @@ -63,6 +63,7 @@ fn test_git_clone() { insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stderr, @r###" Fetching into new repo in "$TEST_ENV/clone" + branch: main@origin [new] untracked Working copy now at: uuqppmxq 1f0b881a (empty) (no description set) Parent commit : mzyxwzks 9f01a0e0 main | message Added 1 files, modified 0 files, removed 0 files @@ -173,6 +174,7 @@ fn test_git_clone_colocate() { insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stderr, @r###" Fetching into new repo in "$TEST_ENV/clone" + branch: main@origin [new] untracked Working copy now at: uuqppmxq 1f0b881a (empty) (no description set) Parent commit : mzyxwzks 9f01a0e0 main | message Added 1 files, modified 0 files, removed 0 files @@ -328,6 +330,8 @@ fn test_git_clone_remote_default_branch() { test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "clone1"]); insta::assert_snapshot!(stderr, @r###" Fetching into new repo in "$TEST_ENV/clone1" + branch: feature1@origin [new] untracked + branch: main@origin [new] untracked Working copy now at: sqpuoqvx cad212e1 (empty) (no description set) Parent commit : mzyxwzks 9f01a0e0 feature1 main | message Added 1 files, modified 0 files, removed 0 files @@ -346,6 +350,8 @@ fn test_git_clone_remote_default_branch() { test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "clone2"]); insta::assert_snapshot!(stderr, @r###" Fetching into new repo in "$TEST_ENV/clone2" + branch: feature1@origin [new] untracked + branch: main@origin [new] untracked Working copy now at: pmmvwywv fa729b1e (empty) (no description set) Parent commit : mzyxwzks 9f01a0e0 feature1@origin main | message Added 1 files, modified 0 files, removed 0 files diff --git a/cli/tests/test_git_colocated.rs b/cli/tests/test_git_colocated.rs index d59bbf459cb..3cc281c6a2d 100644 --- a/cli/tests/test_git_colocated.rs +++ b/cli/tests/test_git_colocated.rs @@ -507,6 +507,8 @@ fn test_git_colocated_fetch_deleted_or_moved_branch() { let (stdout, stderr) = test_env.jj_cmd_ok(&clone_path, &["git", "fetch"]); insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stderr, @r###" + branch: B_to_delete@origin [deleted] untracked + branch: C_to_move@origin [updated] tracked Abandoned 2 commits that are no longer reachable. "###); // "original C" and "B_to_delete" are abandoned, as the corresponding branches diff --git a/cli/tests/test_git_fetch.rs b/cli/tests/test_git_fetch.rs index 7b45f8faf13..034d998b15a 100644 --- a/cli/tests/test_git_fetch.rs +++ b/cli/tests/test_git_fetch.rs @@ -111,7 +111,7 @@ fn test_git_fetch_single_remote() { .jj_cmd(&repo_path, &["git", "fetch"]) .assert() .success() - .stderr("Fetching from the only existing remote: rem1\n"); + .stderr("Fetching from the only existing remote: rem1\nbranch: rem1@rem1 [new] tracked\n"); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" rem1: qxosxrvv 6a211027 message @rem1: qxosxrvv 6a211027 message @@ -237,6 +237,7 @@ fn test_git_fetch_nonexistent_remote() { &["git", "fetch", "--remote", "rem1", "--remote", "rem2"], ); insta::assert_snapshot!(stderr, @r###" + branch: rem1@rem1 [new] untracked Error: No git remote named 'rem2' "###); // No remote should have been fetched as part of the failing transaction @@ -253,6 +254,7 @@ fn test_git_fetch_nonexistent_remote_from_config() { let stderr = &test_env.jj_cmd_failure(&repo_path, &["git", "fetch"]); insta::assert_snapshot!(stderr, @r###" + branch: rem1@rem1 [new] untracked Error: No git remote named 'rem2' "###); // No remote should have been fetched as part of the failing transaction @@ -461,7 +463,12 @@ fn test_git_fetch_all() { insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @""); let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: a1@origin [new] tracked + branch: a2@origin [new] tracked + branch: b@origin [new] tracked + branch: trunk1@origin [new] tracked + "###); insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###" a1: nknoxmzm 359a9a02 descr_for_a1 @origin: nknoxmzm 359a9a02 descr_for_a1 @@ -529,6 +536,10 @@ fn test_git_fetch_all() { let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch"]); insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stderr, @r###" + branch: a1@origin [updated] tracked + branch: a2@origin [updated] tracked + branch: b@origin [updated] tracked + branch: trunk2@origin [new] tracked Abandoned 2 commits that are no longer reachable. "###); insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###" @@ -616,7 +627,9 @@ fn test_git_fetch_some_of_many_branches() { let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "b"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: b@origin [new] tracked + "###); insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" ◉ c7d4bdcbc215 descr_for_b b ◉ ff36dc55760e descr_for_trunk1 @@ -635,7 +648,10 @@ fn test_git_fetch_some_of_many_branches() { &["git", "fetch", "--branch", "glob:a*"], ); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: a1@origin [new] tracked + branch: a2@origin [new] tracked + "###); insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" ◉ decaa3966c83 descr_for_a2 a2 │ ◉ 359a9a02457d descr_for_a1 a1 @@ -704,6 +720,8 @@ fn test_git_fetch_some_of_many_branches() { ); insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stderr, @r###" + branch: a1@origin [updated] tracked + branch: b@origin [updated] tracked Abandoned 1 commits that are no longer reachable. "###); insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" @@ -741,6 +759,7 @@ fn test_git_fetch_some_of_many_branches() { ); insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stderr, @r###" + branch: a2@origin [updated] tracked Abandoned 1 commits that are no longer reachable. "###); insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" @@ -808,7 +827,10 @@ fn test_git_fetch_undo() { &["git", "fetch", "--branch", "b", "--branch", "a1"], ); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: a1@origin [new] tracked + branch: b@origin [new] tracked + "###); insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" ◉ c7d4bdcbc215 descr_for_b b │ ◉ 359a9a02457d descr_for_a1 a1 @@ -830,7 +852,9 @@ fn test_git_fetch_undo() { let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "b"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: b@origin [new] tracked + "###); insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" ◉ c7d4bdcbc215 descr_for_b b ◉ ff36dc55760e descr_for_trunk1 @@ -880,7 +904,9 @@ fn test_fetch_undo_what() { // Fetch a branch let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--branch", "b"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: b@origin [new] tracked + "###); insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" ◉ c7d4bdcbc215 descr_for_b b ◉ ff36dc55760e descr_for_trunk1 @@ -967,7 +993,9 @@ fn test_git_fetch_remove_fetch() { // Check that origin@origin is properly recreated let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: origin@origin [new] tracked + "###); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" origin (conflicted): + qpvuntsm 230dd059 (empty) (no description set) @@ -1050,7 +1078,12 @@ fn test_git_fetch_removed_branch() { // Fetch all branches let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: a1@origin [new] tracked + branch: a2@origin [new] tracked + branch: b@origin [new] tracked + branch: trunk1@origin [new] tracked + "###); insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" ◉ c7d4bdcbc215 descr_for_b b │ ◉ decaa3966c83 descr_for_a2 a2 @@ -1090,6 +1123,7 @@ fn test_git_fetch_removed_branch() { test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "a2"]); insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stderr, @r###" + branch: a2@origin [deleted] untracked Abandoned 1 commits that are no longer reachable. "###); insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" @@ -1136,7 +1170,12 @@ fn test_git_fetch_removed_parent_branch() { // Fetch all branches let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: a1@origin [new] tracked + branch: a2@origin [new] tracked + branch: b@origin [new] tracked + branch: trunk1@origin [new] tracked + "###); insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" ◉ c7d4bdcbc215 descr_for_b b │ ◉ decaa3966c83 descr_for_a2 a2 @@ -1163,6 +1202,8 @@ fn test_git_fetch_removed_parent_branch() { ); insta::assert_snapshot!(stdout, @""); insta::assert_snapshot!(stderr, @r###" + branch: a1@origin [deleted] untracked + branch: trunk1@origin [deleted] untracked Abandoned 1 commits that are no longer reachable. "###); insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" diff --git a/cli/tests/test_git_import_export.rs b/cli/tests/test_git_import_export.rs index 74953d0f2e3..2598e5fb5a6 100644 --- a/cli/tests/test_git_import_export.rs +++ b/cli/tests/test_git_import_export.rs @@ -148,7 +148,9 @@ fn test_git_import_undo() { let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "import"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: a [new] untracked + "###); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" a: qpvuntsm 230dd059 (empty) (no description set) @git: qpvuntsm 230dd059 (empty) (no description set) @@ -162,7 +164,9 @@ fn test_git_import_undo() { // Try "git import" again, which should re-import the branch "a". let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "import"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: a [new] untracked + "###); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" a: qpvuntsm 230dd059 (empty) (no description set) @git: qpvuntsm 230dd059 (empty) (no description set) @@ -191,7 +195,9 @@ fn test_git_import_move_export_with_default_undo() { let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "import"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: a [new] untracked + "###); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" a: qpvuntsm 230dd059 (empty) (no description set) @git: qpvuntsm 230dd059 (empty) (no description set) @@ -237,7 +243,9 @@ fn test_git_import_move_export_with_default_undo() { // intuitive result here. let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "import"]); insta::assert_snapshot!(stdout, @""); - insta::assert_snapshot!(stderr, @""); + insta::assert_snapshot!(stderr, @r###" + branch: a [new] untracked + "###); insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" a: yqosqzyt 096dc80d (empty) (no description set) @git: yqosqzyt 096dc80d (empty) (no description set) diff --git a/lib/src/git.rs b/lib/src/git.rs index d38636731d6..e05a6987c5a 100644 --- a/lib/src/git.rs +++ b/lib/src/git.rs @@ -201,10 +201,13 @@ impl GitImportError { } /// Describes changes made by `import_refs()` or `fetch()`. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Default)] pub struct GitImportStats { /// Commits superseded by newly imported commits. pub abandoned_commits: Vec, + /// Remote `(ref_name, (old_remote_ref, new_target))`s to be merged in to + /// the local refs. + pub changed_remote_refs: BTreeMap, } #[derive(Debug)] @@ -326,7 +329,10 @@ pub fn import_some_refs( } else { vec![] }; - let stats = GitImportStats { abandoned_commits }; + let stats = GitImportStats { + abandoned_commits, + changed_remote_refs, + }; Ok(stats) } @@ -1089,7 +1095,7 @@ pub enum GitFetchError { } /// Describes successful `fetch()` result. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Default)] pub struct GitFetchStats { /// Remote's default branch. pub default_branch: Option, @@ -1135,12 +1141,7 @@ pub fn fetch( .ok_or(GitFetchError::InvalidBranchPattern)?; if refspecs.is_empty() { // Don't fall back to the base refspecs. - let stats = GitFetchStats { - default_branch: None, - import_stats: GitImportStats { - abandoned_commits: vec![], - }, - }; + let stats = GitFetchStats::default(); return Ok(stats); } tracing::debug!("remote.download");