diff --git a/CHANGELOG.md b/CHANGELOG.md index 593fb01494..9ed1e9bdc2 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 bcf4beeeed..49b5b921a7 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 8831721512..3a7868c3d0 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 df0b467e03..29c77adfeb 100644 --- a/cli/src/git_util.rs +++ b/cli/src/git_util.rs @@ -21,11 +21,14 @@ 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::progress::Progress; @@ -172,7 +175,29 @@ 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(|(refn_name, (remote_ref, ref_target))| { + RefStatus::new(refn_name, remote_ref, ref_target, repo) + }) + .collect_vec(); + let max_width = refs_stats.iter().map(|x| x.ref_name.width()).max(); + if let Some(max_width) = max_width { + let stderr = &mut ui.stderr(); + for status in refs_stats { + status.output(max_width, stderr)?; + } + } + } + if !stats.abandoned_commits.is_empty() { writeln!( ui.stderr(), @@ -180,9 +205,83 @@ pub fn print_git_import_stats(ui: &mut Ui, stats: &GitImportStats) -> Result<(), stats.abandoned_commits.len() )?; } + Ok(()) } +struct RefStatus { + 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 = match ref_name { + RefName::RemoteBranch { branch, remote } => { + if repo.view().get_remote_branch(branch, remote).is_tracking() { + tracking_status = TrackingStatus::Tracked; + } + format!("branch: {branch}@{remote}") + } + RefName::Tag(tag) => format!("tag: {tag}"), + RefName::LocalBranch(branch) => format!("branch: {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, + } + } + + fn output(&self, max_ref_name_len: usize, out: &mut dyn Write) -> std::io::Result<()> { + const MAX_REF_NAME_LEN: usize = 40; + let tracking_status = match self.tracking_status { + TrackingStatus::Tracked => "tracked", + TrackingStatus::Untracked => "untracked", + }; + + 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 = std::cmp::min(max_ref_name_len, MAX_REF_NAME_LEN) + ); + + writeln!(out, "{padded_ref_name} [{import_status}] {tracking_status}") + } +} + +enum TrackingStatus { + Tracked, + Untracked, +} + +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 e957e6d667..3795ae1893 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 dbf756459c..a13e860530 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 d59bbf459c..3cc281c6a2 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 7b45f8faf1..034d998b15 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 74953d0f2e..2598e5fb5a 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 d38636731d..e05a6987c5 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");