From dde4691c4b8a8cce65fa57cecbfb414bb843d8da Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Fri, 13 Oct 2023 01:32:18 +0900 Subject: [PATCH] op_store: minimal change to load/store tracking state of remote branches We could instead migrate the storage types to (local_branches, remote_views), but that would be more involved and break forward compatibility with little benefit. Maybe we can do that later when we introduce remote tags. --- lib/src/protos/op_store.proto | 7 +++++ lib/src/protos/op_store.rs | 29 +++++++++++++++++++ lib/src/simple_op_store.rs | 54 +++++++++++++++++++++++++++-------- 3 files changed, 78 insertions(+), 12 deletions(-) diff --git a/lib/src/protos/op_store.proto b/lib/src/protos/op_store.proto index f7b4799656a..f2514bf1636 100644 --- a/lib/src/protos/op_store.proto +++ b/lib/src/protos/op_store.proto @@ -38,9 +38,16 @@ message RefTarget { } } +enum RemoteRefState { + New = 0; + Tracking = 1; +} + message RemoteBranch { string remote_name = 1; RefTarget target = 2; + // Introduced in jj 0.11. + optional RemoteRefState state = 3; } message Branch { diff --git a/lib/src/protos/op_store.rs b/lib/src/protos/op_store.rs index fdd86716973..be31066118d 100644 --- a/lib/src/protos/op_store.rs +++ b/lib/src/protos/op_store.rs @@ -53,6 +53,9 @@ pub struct RemoteBranch { pub remote_name: ::prost::alloc::string::String, #[prost(message, optional, tag = "2")] pub target: ::core::option::Option, + /// Introduced in jj 0.11. + #[prost(enumeration = "RemoteRefState", optional, tag = "3")] + pub state: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -162,3 +165,29 @@ pub struct OperationMetadata { ::prost::alloc::string::String, >, } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum RemoteRefState { + New = 0, + Tracking = 1, +} +impl RemoteRefState { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + RemoteRefState::New => "New", + RemoteRefState::Tracking => "Tracking", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "New" => Some(Self::New), + "Tracking" => Some(Self::Tracking), + _ => None, + } + } +} diff --git a/lib/src/simple_op_store.rs b/lib/src/simple_op_store.rs index 1e900acae64..f6c30bf5aa5 100644 --- a/lib/src/simple_op_store.rs +++ b/lib/src/simple_op_store.rs @@ -352,6 +352,7 @@ fn branch_views_to_proto_legacy( |&(remote_name, remote_ref)| crate::protos::op_store::RemoteBranch { remote_name: remote_name.to_owned(), target: ref_target_to_proto(&remote_ref.target), + state: remote_ref_state_to_proto(remote_ref.state), }, ) .collect(); @@ -372,17 +373,27 @@ fn branch_views_from_proto_legacy( for branch_proto in branches_legacy { let local_target = ref_target_from_proto(branch_proto.local_target); for remote_branch in branch_proto.remote_branches { - // If local branch doesn't exist, we assume that the remote branch hasn't been - // merged because git.auto-local-branch was off. That's probably more common - // than deleted but yet-to-be-pushed local branch. Alternatively, we could read - // git.auto-local-branch setting here, but that wouldn't always work since the - // setting could be toggled after the branch got merged. - let is_git_tracking = remote_branch.remote_name == git::REMOTE_NAME_FOR_LOCAL_GIT_REPO; - let state = if is_git_tracking || local_target.is_present() { - RemoteRefState::Tracking - } else { - RemoteRefState::New - }; + let state = remote_ref_state_from_proto(remote_branch.state).unwrap_or_else(|| { + // If local branch doesn't exist, we assume that the remote branch hasn't been + // merged because git.auto-local-branch was off. That's probably more common + // than deleted but yet-to-be-pushed local branch. Alternatively, we could read + // git.auto-local-branch setting here, but that wouldn't always work since the + // setting could be toggled after the branch got merged. + let is_git_tracking = + remote_branch.remote_name == git::REMOTE_NAME_FOR_LOCAL_GIT_REPO; + let default_state = if is_git_tracking || local_target.is_present() { + RemoteRefState::Tracking + } else { + RemoteRefState::New + }; + tracing::trace!( + ?branch_proto.name, + ?remote_branch.remote_name, + ?default_state, + "generated tracking state", + ); + default_state + }); let remote_view = remote_views.entry(remote_branch.remote_name).or_default(); let remote_ref = RemoteRef { target: ref_target_from_proto(remote_branch.target), @@ -501,6 +512,23 @@ fn ref_target_from_proto(maybe_proto: Option } } +fn remote_ref_state_to_proto(state: RemoteRefState) -> Option { + let proto_state = match state { + RemoteRefState::New => crate::protos::op_store::RemoteRefState::New, + RemoteRefState::Tracking => crate::protos::op_store::RemoteRefState::Tracking, + }; + Some(proto_state as i32) +} + +fn remote_ref_state_from_proto(proto_value: Option) -> Option { + let proto_state = proto_value.and_then(crate::protos::op_store::RemoteRefState::from_i32)?; + let state = match proto_state { + crate::protos::op_store::RemoteRefState::New => RemoteRefState::New, + crate::protos::op_store::RemoteRefState::Tracking => RemoteRefState::Tracking, + }; + Some(state) +} + #[cfg(test)] mod tests { use insta::assert_snapshot; @@ -663,8 +691,9 @@ mod tests { }, "remote2".to_owned() => RemoteView { branches: btreemap! { + // "branch2" is non-tracking. "branch4" is tracking, but locally deleted. "branch2".to_owned() => new_remote_ref(&remote2_branch2_target), - "branch4".to_owned() => new_remote_ref(&remote2_branch4_target), + "branch4".to_owned() => tracking_remote_ref(&remote2_branch4_target), }, }, }; @@ -706,6 +735,7 @@ mod tests { |remote_name: &str, ref_target| crate::protos::op_store::RemoteBranch { remote_name: remote_name.to_owned(), target: ref_target_to_proto(ref_target), + state: None, // to be generated based on local branch existence }; let git_ref_to_proto = |name: &str, ref_target| crate::protos::op_store::GitRef { name: name.to_owned(),