diff --git a/cli/src/cli_util.rs b/cli/src/cli_util.rs index 08350ac7dd..fbf278581f 100644 --- a/cli/src/cli_util.rs +++ b/cli/src/cli_util.rs @@ -27,7 +27,7 @@ use std::time::SystemTime; use clap::builder::{NonEmptyStringValueParser, TypedValueParser, ValueParserFactory}; use clap::{Arg, ArgAction, ArgMatches, Command, FromArgMatches}; -use git2::{Oid, Repository}; +use git2::Repository; use indexmap::IndexSet; use itertools::Itertools; use jj_lib::backend::{BackendError, ChangeId, CommitId, MergedTreeId, ObjectId}; @@ -43,7 +43,7 @@ use jj_lib::id_prefix::IdPrefixContext; use jj_lib::matchers::{EverythingMatcher, Matcher, PrefixMatcher, Visit}; use jj_lib::merged_tree::{MergedTree, MergedTreeBuilder}; use jj_lib::op_heads_store::{self, OpHeadResolutionError, OpHeadsStore}; -use jj_lib::op_store::{OpStore, OpStoreError, OperationId, RefTarget, WorkspaceId}; +use jj_lib::op_store::{OpStore, OpStoreError, OperationId, WorkspaceId}; use jj_lib::operation::Operation; use jj_lib::repo::{ CheckOutCommitError, EditCommitError, MutableRepo, ReadonlyRepo, Repo, RepoLoader, @@ -842,30 +842,6 @@ impl WorkspaceCommandHelper { Ok(()) } - fn export_head_to_git( - &self, - mut_repo: &mut MutableRepo, - git_repo: &git2::Repository, - ) -> Result<(), CommandError> { - if let Some(wc_commit_id) = mut_repo.view().get_wc_commit_id(self.workspace_id()) { - let wc_commit = mut_repo.store().get_commit(wc_commit_id)?; - let first_parent_id = wc_commit.parent_ids()[0].clone(); - if first_parent_id != *mut_repo.store().root_commit_id() { - let new_git_commit_id = Oid::from_bytes(first_parent_id.as_bytes()).unwrap(); - let new_git_commit = git_repo.find_commit(new_git_commit_id)?; - git_repo.set_head_detached(new_git_commit_id)?; - git_repo.reset(new_git_commit.as_object(), git2::ResetType::Mixed, None)?; - mut_repo.set_git_head_target(RefTarget::normal(first_parent_id)); - } - } else { - // The workspace was removed (maybe the user undid the - // initialization of the workspace?), which is weird, - // but we should probably just not do anything else here. - // Except maybe print a note about it? - } - Ok(()) - } - pub fn repo(&self) -> &Arc { &self.user_repo.repo } @@ -1406,25 +1382,19 @@ See https://github.com/martinvonz/jj/blob/main/docs/working-copy.md#stale-workin &mut self, ui: &mut Ui, maybe_old_commit: Option<&Commit>, + new_commit: &Commit, ) -> Result<(), CommandError> { assert!(self.may_update_working_copy); - let new_commit = match self.get_wc_commit_id() { - Some(commit_id) => self.repo().store().get_commit(commit_id)?, - None => { - // It seems the workspace was deleted, so we shouldn't try to update it. - return Ok(()); - } - }; let stats = update_working_copy( &self.user_repo.repo, self.workspace.working_copy_mut(), maybe_old_commit, - &new_commit, + new_commit, )?; - if Some(&new_commit) != maybe_old_commit { + if Some(new_commit) != maybe_old_commit { ui.write("Working copy now at: ")?; ui.stdout_formatter().with_label("working_copy", |fmt| { - self.write_commit_summary(fmt, &new_commit) + self.write_commit_summary(fmt, new_commit) })?; ui.write("\n")?; for parent in new_commit.parents() { @@ -1447,31 +1417,43 @@ See https://github.com/martinvonz/jj/blob/main/docs/working-copy.md#stale-workin } fn finish_transaction(&mut self, ui: &mut Ui, mut tx: Transaction) -> Result<(), CommandError> { - let mut_repo = tx.mut_repo(); - let store = mut_repo.store().clone(); - if !mut_repo.has_changes() { + if !tx.mut_repo().has_changes() { writeln!(ui, "Nothing changed.")?; return Ok(()); } - let num_rebased = mut_repo.rebase_descendants(&self.settings)?; + let num_rebased = tx.mut_repo().rebase_descendants(&self.settings)?; if num_rebased > 0 { writeln!(ui, "Rebased {num_rebased} descendant commits")?; } - if self.working_copy_shared_with_git { - let git_repo = self.git_backend().unwrap().open_git_repo()?; - self.export_head_to_git(mut_repo, &git_repo)?; - let failed_branches = git::export_refs(mut_repo, &git_repo)?; - print_failed_git_export(ui, &failed_branches)?; - } - let maybe_old_commit = tx + + let maybe_old_wc_commit = tx .base_repo() .view() .get_wc_commit_id(self.workspace_id()) - .map(|commit_id| store.get_commit(commit_id)) + .map(|commit_id| tx.base_repo().store().get_commit(commit_id)) .transpose()?; + let maybe_new_wc_commit = tx + .repo() + .view() + .get_wc_commit_id(self.workspace_id()) + .map(|commit_id| tx.repo().store().get_commit(commit_id)) + .transpose()?; + if self.working_copy_shared_with_git { + let git_repo = self.git_backend().unwrap().open_git_repo()?; + if let Some(wc_commit) = &maybe_new_wc_commit { + git::reset_head(tx.mut_repo(), &git_repo, wc_commit)?; + } + let failed_branches = git::export_refs(tx.mut_repo(), &git_repo)?; + print_failed_git_export(ui, &failed_branches)?; + } self.user_repo = ReadonlyUserRepo::new(tx.commit()); if self.may_update_working_copy { - self.update_working_copy(ui, maybe_old_commit.as_ref())?; + if let Some(new_commit) = &maybe_new_wc_commit { + self.update_working_copy(ui, maybe_old_wc_commit.as_ref(), new_commit)?; + } else { + // It seems the workspace was deleted, so we shouldn't try to + // update it. + } } let settings = &self.settings; if settings.user_name().is_empty() || settings.user_email().is_empty() { diff --git a/lib/src/git.rs b/lib/src/git.rs index 39f617fef9..db7c7eec2a 100644 --- a/lib/src/git.rs +++ b/lib/src/git.rs @@ -26,6 +26,7 @@ use tempfile::NamedTempFile; use thiserror::Error; use crate::backend::{BackendError, CommitId, ObjectId}; +use crate::commit::Commit; use crate::git_backend::GitBackend; use crate::op_store::{BranchTarget, RefTarget, RefTargetOptionExt}; use crate::repo::{MutableRepo, Repo}; @@ -675,6 +676,24 @@ fn update_git_ref( Ok(()) } +/// Sets `HEAD@git` to the parent of the given working-copy commit and resets +/// the Git index. +pub fn reset_head( + mut_repo: &mut MutableRepo, + git_repo: &git2::Repository, + wc_commit: &Commit, +) -> Result<(), git2::Error> { + let first_parent_id = &wc_commit.parent_ids()[0]; + if first_parent_id != mut_repo.store().root_commit_id() { + let new_git_commit_id = Oid::from_bytes(first_parent_id.as_bytes()).unwrap(); + let new_git_commit = git_repo.find_commit(new_git_commit_id)?; + git_repo.set_head_detached(new_git_commit_id)?; + git_repo.reset(new_git_commit.as_object(), git2::ResetType::Mixed, None)?; + mut_repo.set_git_head_target(RefTarget::normal(first_parent_id.clone())); + } + Ok(()) +} + #[derive(Debug, Error)] pub enum GitRemoteManagementError { #[error("No git remote named '{0}'")]