mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-26 14:00:51 +00:00
object_id: move HexPrefix and PrefixResolution from index module
This commit is contained in:
parent
fa5e40719c
commit
31b236a70d
14 changed files with 160 additions and 163 deletions
|
@ -20,7 +20,7 @@ use std::time::Instant;
|
|||
use clap::Subcommand;
|
||||
use criterion::measurement::Measurement;
|
||||
use criterion::{BatchSize, BenchmarkGroup, BenchmarkId, Criterion};
|
||||
use jj_lib::index::HexPrefix;
|
||||
use jj_lib::object_id::HexPrefix;
|
||||
use jj_lib::repo::Repo;
|
||||
|
||||
use crate::cli_util::{CommandError, CommandHelper, WorkspaceCommandHelper};
|
||||
|
|
|
@ -27,8 +27,8 @@ use super::entry::{
|
|||
use super::readonly::ReadonlyIndexSegment;
|
||||
use super::rev_walk::RevWalk;
|
||||
use crate::backend::{ChangeId, CommitId};
|
||||
use crate::index::{HexPrefix, Index, PrefixResolution};
|
||||
use crate::object_id::ObjectId;
|
||||
use crate::index::Index;
|
||||
use crate::object_id::{HexPrefix, ObjectId, PrefixResolution};
|
||||
use crate::revset::{ResolvedExpression, Revset, RevsetEvaluationError};
|
||||
use crate::store::Store;
|
||||
use crate::{default_revset_engine, hex_util};
|
||||
|
|
|
@ -44,8 +44,8 @@ mod tests {
|
|||
use super::mutable::MutableIndexSegment;
|
||||
use super::*;
|
||||
use crate::backend::{ChangeId, CommitId};
|
||||
use crate::index::{HexPrefix, Index, PrefixResolution};
|
||||
use crate::object_id::ObjectId;
|
||||
use crate::index::Index;
|
||||
use crate::object_id::{HexPrefix, ObjectId, PrefixResolution};
|
||||
|
||||
/// Generator of unique 16-byte ChangeId excluding root id
|
||||
fn change_id_generator() -> impl FnMut() -> ChangeId {
|
||||
|
|
|
@ -35,8 +35,8 @@ use super::readonly::{DefaultReadonlyIndex, ReadonlyIndexSegment};
|
|||
use crate::backend::{ChangeId, CommitId};
|
||||
use crate::commit::Commit;
|
||||
use crate::file_util::persist_content_addressed_temp_file;
|
||||
use crate::index::{HexPrefix, Index, MutableIndex, PrefixResolution, ReadonlyIndex};
|
||||
use crate::object_id::ObjectId;
|
||||
use crate::index::{Index, MutableIndex, ReadonlyIndex};
|
||||
use crate::object_id::{HexPrefix, ObjectId, PrefixResolution};
|
||||
use crate::revset::{ResolvedExpression, Revset, RevsetEvaluationError};
|
||||
use crate::store::Store;
|
||||
|
||||
|
|
|
@ -31,8 +31,8 @@ use super::entry::{IndexPosition, LocalPosition, SmallIndexPositionsVec};
|
|||
use super::mutable::DefaultMutableIndex;
|
||||
use crate::backend::{ChangeId, CommitId};
|
||||
use crate::default_revset_engine;
|
||||
use crate::index::{HexPrefix, Index, MutableIndex, PrefixResolution, ReadonlyIndex};
|
||||
use crate::object_id::ObjectId;
|
||||
use crate::index::{Index, MutableIndex, ReadonlyIndex};
|
||||
use crate::object_id::{HexPrefix, ObjectId, PrefixResolution};
|
||||
use crate::revset::{ResolvedExpression, Revset, RevsetEvaluationError};
|
||||
use crate::store::Store;
|
||||
|
||||
|
|
|
@ -27,8 +27,8 @@ use crate::backend::{ChangeId, CommitId, MillisSinceEpoch};
|
|||
use crate::default_index::{AsCompositeIndex, CompositeIndex, IndexEntry, IndexPosition};
|
||||
use crate::default_revset_graph_iterator::RevsetGraphIterator;
|
||||
use crate::id_prefix::{IdIndex, IdIndexSource, IdIndexSourceEntry};
|
||||
use crate::index::{HexPrefix, PrefixResolution};
|
||||
use crate::matchers::{EverythingMatcher, Matcher, PrefixMatcher, Visit};
|
||||
use crate::object_id::{HexPrefix, PrefixResolution};
|
||||
use crate::repo_path::RepoPath;
|
||||
use crate::revset::{
|
||||
ChangeIdIndex, ResolvedExpression, ResolvedPredicateExpression, Revset, RevsetEvaluationError,
|
||||
|
|
|
@ -23,8 +23,7 @@ use once_cell::unsync::OnceCell;
|
|||
|
||||
use crate::backend::{ChangeId, CommitId};
|
||||
use crate::hex_util;
|
||||
use crate::index::{HexPrefix, PrefixResolution};
|
||||
use crate::object_id::ObjectId;
|
||||
use crate::object_id::{HexPrefix, ObjectId, PrefixResolution};
|
||||
use crate::repo::Repo;
|
||||
use crate::revset::{DefaultSymbolResolver, RevsetExpression};
|
||||
|
||||
|
|
142
lib/src/index.rs
142
lib/src/index.rs
|
@ -22,7 +22,7 @@ use thiserror::Error;
|
|||
|
||||
use crate::backend::CommitId;
|
||||
use crate::commit::Commit;
|
||||
use crate::object_id::ObjectId;
|
||||
use crate::object_id::{HexPrefix, PrefixResolution};
|
||||
use crate::op_store::OperationId;
|
||||
use crate::operation::Operation;
|
||||
use crate::revset::{ResolvedExpression, Revset, RevsetEvaluationError};
|
||||
|
@ -106,143 +106,3 @@ pub trait MutableIndex {
|
|||
|
||||
fn merge_in(&mut self, other: &dyn ReadonlyIndex);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct HexPrefix {
|
||||
// For odd-length prefix, lower 4 bits of the last byte is padded with 0
|
||||
min_prefix_bytes: Vec<u8>,
|
||||
has_odd_byte: bool,
|
||||
}
|
||||
|
||||
impl HexPrefix {
|
||||
pub fn new(prefix: &str) -> Option<HexPrefix> {
|
||||
let has_odd_byte = prefix.len() & 1 != 0;
|
||||
let min_prefix_bytes = if has_odd_byte {
|
||||
hex::decode(prefix.to_owned() + "0").ok()?
|
||||
} else {
|
||||
hex::decode(prefix).ok()?
|
||||
};
|
||||
Some(HexPrefix {
|
||||
min_prefix_bytes,
|
||||
has_odd_byte,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Self {
|
||||
HexPrefix {
|
||||
min_prefix_bytes: bytes.to_owned(),
|
||||
has_odd_byte: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hex(&self) -> String {
|
||||
let mut hex_string = hex::encode(&self.min_prefix_bytes);
|
||||
if self.has_odd_byte {
|
||||
hex_string.pop().unwrap();
|
||||
}
|
||||
hex_string
|
||||
}
|
||||
|
||||
/// Minimum bytes that would match this prefix. (e.g. "abc0" for "abc")
|
||||
///
|
||||
/// Use this to partition a sorted slice, and test `matches(id)` from there.
|
||||
pub fn min_prefix_bytes(&self) -> &[u8] {
|
||||
&self.min_prefix_bytes
|
||||
}
|
||||
|
||||
/// Returns the bytes representation if this prefix can be a full id.
|
||||
pub fn as_full_bytes(&self) -> Option<&[u8]> {
|
||||
(!self.has_odd_byte).then_some(&self.min_prefix_bytes)
|
||||
}
|
||||
|
||||
fn split_odd_byte(&self) -> (Option<u8>, &[u8]) {
|
||||
if self.has_odd_byte {
|
||||
let (&odd, prefix) = self.min_prefix_bytes.split_last().unwrap();
|
||||
(Some(odd), prefix)
|
||||
} else {
|
||||
(None, &self.min_prefix_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn matches<Q: ObjectId>(&self, id: &Q) -> bool {
|
||||
let id_bytes = id.as_bytes();
|
||||
let (maybe_odd, prefix) = self.split_odd_byte();
|
||||
if id_bytes.starts_with(prefix) {
|
||||
if let Some(odd) = maybe_odd {
|
||||
matches!(id_bytes.get(prefix.len()), Some(v) if v & 0xf0 == odd)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PrefixResolution<T> {
|
||||
NoMatch,
|
||||
SingleMatch(T),
|
||||
AmbiguousMatch,
|
||||
}
|
||||
|
||||
impl<T> PrefixResolution<T> {
|
||||
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> PrefixResolution<U> {
|
||||
match self {
|
||||
PrefixResolution::NoMatch => PrefixResolution::NoMatch,
|
||||
PrefixResolution::SingleMatch(x) => PrefixResolution::SingleMatch(f(x)),
|
||||
PrefixResolution::AmbiguousMatch => PrefixResolution::AmbiguousMatch,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> PrefixResolution<T> {
|
||||
pub fn plus(&self, other: &PrefixResolution<T>) -> PrefixResolution<T> {
|
||||
match (self, other) {
|
||||
(PrefixResolution::NoMatch, other) => other.clone(),
|
||||
(local, PrefixResolution::NoMatch) => local.clone(),
|
||||
(PrefixResolution::AmbiguousMatch, _) => PrefixResolution::AmbiguousMatch,
|
||||
(_, PrefixResolution::AmbiguousMatch) => PrefixResolution::AmbiguousMatch,
|
||||
(PrefixResolution::SingleMatch(_), PrefixResolution::SingleMatch(_)) => {
|
||||
PrefixResolution::AmbiguousMatch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_hex_prefix_prefixes() {
|
||||
let prefix = HexPrefix::new("").unwrap();
|
||||
assert_eq!(prefix.min_prefix_bytes(), b"");
|
||||
|
||||
let prefix = HexPrefix::new("1").unwrap();
|
||||
assert_eq!(prefix.min_prefix_bytes(), b"\x10");
|
||||
|
||||
let prefix = HexPrefix::new("12").unwrap();
|
||||
assert_eq!(prefix.min_prefix_bytes(), b"\x12");
|
||||
|
||||
let prefix = HexPrefix::new("123").unwrap();
|
||||
assert_eq!(prefix.min_prefix_bytes(), b"\x12\x30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hex_prefix_matches() {
|
||||
let id = CommitId::from_hex("1234");
|
||||
|
||||
assert!(HexPrefix::new("").unwrap().matches(&id));
|
||||
assert!(HexPrefix::new("1").unwrap().matches(&id));
|
||||
assert!(HexPrefix::new("12").unwrap().matches(&id));
|
||||
assert!(HexPrefix::new("123").unwrap().matches(&id));
|
||||
assert!(HexPrefix::new("1234").unwrap().matches(&id));
|
||||
assert!(!HexPrefix::new("12345").unwrap().matches(&id));
|
||||
|
||||
assert!(!HexPrefix::new("a").unwrap().matches(&id));
|
||||
assert!(!HexPrefix::new("1a").unwrap().matches(&id));
|
||||
assert!(!HexPrefix::new("12a").unwrap().matches(&id));
|
||||
assert!(!HexPrefix::new("123a").unwrap().matches(&id));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,3 +79,144 @@ macro_rules! impl_id_type {
|
|||
}
|
||||
|
||||
pub(crate) use {id_type, impl_id_type};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct HexPrefix {
|
||||
// For odd-length prefix, lower 4 bits of the last byte is padded with 0
|
||||
min_prefix_bytes: Vec<u8>,
|
||||
has_odd_byte: bool,
|
||||
}
|
||||
|
||||
impl HexPrefix {
|
||||
pub fn new(prefix: &str) -> Option<HexPrefix> {
|
||||
let has_odd_byte = prefix.len() & 1 != 0;
|
||||
let min_prefix_bytes = if has_odd_byte {
|
||||
hex::decode(prefix.to_owned() + "0").ok()?
|
||||
} else {
|
||||
hex::decode(prefix).ok()?
|
||||
};
|
||||
Some(HexPrefix {
|
||||
min_prefix_bytes,
|
||||
has_odd_byte,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Self {
|
||||
HexPrefix {
|
||||
min_prefix_bytes: bytes.to_owned(),
|
||||
has_odd_byte: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hex(&self) -> String {
|
||||
let mut hex_string = hex::encode(&self.min_prefix_bytes);
|
||||
if self.has_odd_byte {
|
||||
hex_string.pop().unwrap();
|
||||
}
|
||||
hex_string
|
||||
}
|
||||
|
||||
/// Minimum bytes that would match this prefix. (e.g. "abc0" for "abc")
|
||||
///
|
||||
/// Use this to partition a sorted slice, and test `matches(id)` from there.
|
||||
pub fn min_prefix_bytes(&self) -> &[u8] {
|
||||
&self.min_prefix_bytes
|
||||
}
|
||||
|
||||
/// Returns the bytes representation if this prefix can be a full id.
|
||||
pub fn as_full_bytes(&self) -> Option<&[u8]> {
|
||||
(!self.has_odd_byte).then_some(&self.min_prefix_bytes)
|
||||
}
|
||||
|
||||
fn split_odd_byte(&self) -> (Option<u8>, &[u8]) {
|
||||
if self.has_odd_byte {
|
||||
let (&odd, prefix) = self.min_prefix_bytes.split_last().unwrap();
|
||||
(Some(odd), prefix)
|
||||
} else {
|
||||
(None, &self.min_prefix_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn matches<Q: ObjectId>(&self, id: &Q) -> bool {
|
||||
let id_bytes = id.as_bytes();
|
||||
let (maybe_odd, prefix) = self.split_odd_byte();
|
||||
if id_bytes.starts_with(prefix) {
|
||||
if let Some(odd) = maybe_odd {
|
||||
matches!(id_bytes.get(prefix.len()), Some(v) if v & 0xf0 == odd)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PrefixResolution<T> {
|
||||
NoMatch,
|
||||
SingleMatch(T),
|
||||
AmbiguousMatch,
|
||||
}
|
||||
|
||||
impl<T> PrefixResolution<T> {
|
||||
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> PrefixResolution<U> {
|
||||
match self {
|
||||
PrefixResolution::NoMatch => PrefixResolution::NoMatch,
|
||||
PrefixResolution::SingleMatch(x) => PrefixResolution::SingleMatch(f(x)),
|
||||
PrefixResolution::AmbiguousMatch => PrefixResolution::AmbiguousMatch,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> PrefixResolution<T> {
|
||||
pub fn plus(&self, other: &PrefixResolution<T>) -> PrefixResolution<T> {
|
||||
match (self, other) {
|
||||
(PrefixResolution::NoMatch, other) => other.clone(),
|
||||
(local, PrefixResolution::NoMatch) => local.clone(),
|
||||
(PrefixResolution::AmbiguousMatch, _) => PrefixResolution::AmbiguousMatch,
|
||||
(_, PrefixResolution::AmbiguousMatch) => PrefixResolution::AmbiguousMatch,
|
||||
(PrefixResolution::SingleMatch(_), PrefixResolution::SingleMatch(_)) => {
|
||||
PrefixResolution::AmbiguousMatch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::backend::CommitId;
|
||||
|
||||
#[test]
|
||||
fn test_hex_prefix_prefixes() {
|
||||
let prefix = HexPrefix::new("").unwrap();
|
||||
assert_eq!(prefix.min_prefix_bytes(), b"");
|
||||
|
||||
let prefix = HexPrefix::new("1").unwrap();
|
||||
assert_eq!(prefix.min_prefix_bytes(), b"\x10");
|
||||
|
||||
let prefix = HexPrefix::new("12").unwrap();
|
||||
assert_eq!(prefix.min_prefix_bytes(), b"\x12");
|
||||
|
||||
let prefix = HexPrefix::new("123").unwrap();
|
||||
assert_eq!(prefix.min_prefix_bytes(), b"\x12\x30");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hex_prefix_matches() {
|
||||
let id = CommitId::from_hex("1234");
|
||||
|
||||
assert!(HexPrefix::new("").unwrap().matches(&id));
|
||||
assert!(HexPrefix::new("1").unwrap().matches(&id));
|
||||
assert!(HexPrefix::new("12").unwrap().matches(&id));
|
||||
assert!(HexPrefix::new("123").unwrap().matches(&id));
|
||||
assert!(HexPrefix::new("1234").unwrap().matches(&id));
|
||||
assert!(!HexPrefix::new("12345").unwrap().matches(&id));
|
||||
|
||||
assert!(!HexPrefix::new("a").unwrap().matches(&id));
|
||||
assert!(!HexPrefix::new("1a").unwrap().matches(&id));
|
||||
assert!(!HexPrefix::new("12a").unwrap().matches(&id));
|
||||
assert!(!HexPrefix::new("123a").unwrap().matches(&id));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,7 @@ use std::sync::Arc;
|
|||
use itertools::Itertools as _;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::index::HexPrefix;
|
||||
use crate::object_id::ObjectId as _;
|
||||
use crate::object_id::{HexPrefix, ObjectId as _};
|
||||
use crate::op_heads_store::{OpHeadResolutionError, OpHeadsStore};
|
||||
use crate::op_store::{OpStore, OpStoreError, OpStoreResult, OperationId};
|
||||
use crate::operation::Operation;
|
||||
|
|
|
@ -38,9 +38,9 @@ use crate::default_index::DefaultIndexStore;
|
|||
use crate::default_submodule_store::DefaultSubmoduleStore;
|
||||
use crate::file_util::{IoResultExt as _, PathError};
|
||||
use crate::git_backend::GitBackend;
|
||||
use crate::index::{HexPrefix, Index, IndexStore, MutableIndex, PrefixResolution, ReadonlyIndex};
|
||||
use crate::index::{Index, IndexStore, MutableIndex, ReadonlyIndex};
|
||||
use crate::local_backend::LocalBackend;
|
||||
use crate::object_id::ObjectId;
|
||||
use crate::object_id::{HexPrefix, ObjectId, PrefixResolution};
|
||||
use crate::op_heads_store::{self, OpHeadResolutionError, OpHeadsStore};
|
||||
use crate::op_store::{
|
||||
OpStore, OpStoreError, OperationId, RefTarget, RemoteRef, RemoteRefState, WorkspaceId,
|
||||
|
|
|
@ -35,7 +35,7 @@ use crate::backend::{BackendError, BackendResult, ChangeId, CommitId};
|
|||
use crate::commit::Commit;
|
||||
use crate::git;
|
||||
use crate::hex_util::to_forward_hex;
|
||||
use crate::index::{HexPrefix, PrefixResolution};
|
||||
use crate::object_id::{HexPrefix, PrefixResolution};
|
||||
use crate::op_store::WorkspaceId;
|
||||
use crate::repo::Repo;
|
||||
use crate::repo_path::{FsPathParseError, RepoPathBuf};
|
||||
|
|
|
@ -15,9 +15,8 @@
|
|||
use itertools::Itertools;
|
||||
use jj_lib::backend::{CommitId, MillisSinceEpoch, Signature, Timestamp};
|
||||
use jj_lib::id_prefix::IdPrefixContext;
|
||||
use jj_lib::index::HexPrefix;
|
||||
use jj_lib::index::PrefixResolution::{AmbiguousMatch, NoMatch, SingleMatch};
|
||||
use jj_lib::object_id::ObjectId;
|
||||
use jj_lib::object_id::PrefixResolution::{AmbiguousMatch, NoMatch, SingleMatch};
|
||||
use jj_lib::object_id::{HexPrefix, ObjectId};
|
||||
use jj_lib::repo::Repo;
|
||||
use jj_lib::revset::RevsetExpression;
|
||||
use testutils::{TestRepo, TestRepoBackend};
|
||||
|
|
|
@ -25,8 +25,7 @@ use jj_lib::backend::{ChangeId, CommitId, MillisSinceEpoch, Signature, Timestamp
|
|||
use jj_lib::commit::Commit;
|
||||
use jj_lib::git;
|
||||
use jj_lib::git_backend::GitBackend;
|
||||
use jj_lib::index::{HexPrefix, PrefixResolution};
|
||||
use jj_lib::object_id::ObjectId;
|
||||
use jj_lib::object_id::{HexPrefix, ObjectId, PrefixResolution};
|
||||
use jj_lib::op_store::{RefTarget, RemoteRef, RemoteRefState, WorkspaceId};
|
||||
use jj_lib::repo::Repo;
|
||||
use jj_lib::repo_path::RepoPath;
|
||||
|
|
Loading…
Reference in a new issue