2022-11-26 23:57:50 +00:00
|
|
|
// Copyright 2020 The Jujutsu Authors
|
2021-09-12 06:52:38 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2023-07-10 15:17:00 +00:00
|
|
|
#![allow(missing_docs)]
|
|
|
|
|
2023-05-12 13:05:32 +00:00
|
|
|
use std::any::Any;
|
2021-09-12 06:52:38 +00:00
|
|
|
use std::collections::BTreeMap;
|
2023-07-26 18:39:43 +00:00
|
|
|
use std::fmt::Debug;
|
2021-09-12 06:52:38 +00:00
|
|
|
use std::io::Read;
|
|
|
|
use std::result::Result;
|
|
|
|
use std::vec::Vec;
|
|
|
|
|
|
|
|
use thiserror::Error;
|
|
|
|
|
2022-11-12 19:19:03 +00:00
|
|
|
use crate::content_hash::ContentHash;
|
2023-08-06 16:21:35 +00:00
|
|
|
use crate::merge::Merge;
|
2021-09-12 06:52:38 +00:00
|
|
|
use crate::repo_path::{RepoPath, RepoPathComponent};
|
|
|
|
|
2023-01-01 03:24:32 +00:00
|
|
|
pub trait ObjectId {
|
|
|
|
fn new(value: Vec<u8>) -> Self;
|
|
|
|
fn object_type(&self) -> String;
|
|
|
|
fn from_bytes(bytes: &[u8]) -> Self;
|
|
|
|
fn as_bytes(&self) -> &[u8];
|
|
|
|
fn to_bytes(&self) -> Vec<u8>;
|
|
|
|
fn from_hex(hex: &str) -> Self;
|
|
|
|
fn hex(&self) -> String;
|
|
|
|
}
|
|
|
|
|
2022-12-21 12:00:07 +00:00
|
|
|
macro_rules! id_type {
|
|
|
|
($vis:vis $name:ident) => {
|
|
|
|
content_hash! {
|
|
|
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
|
|
|
|
$vis struct $name(Vec<u8>);
|
|
|
|
}
|
2023-07-26 18:39:43 +00:00
|
|
|
$crate::backend::impl_id_type!($name);
|
2022-12-21 12:00:07 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-12-21 11:40:36 +00:00
|
|
|
macro_rules! impl_id_type {
|
|
|
|
($name:ident) => {
|
2023-07-26 18:39:43 +00:00
|
|
|
impl std::fmt::Debug for $name {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
2022-12-21 11:40:36 +00:00
|
|
|
f.debug_tuple(stringify!($name)).field(&self.hex()).finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-01 03:24:32 +00:00
|
|
|
impl crate::backend::ObjectId for $name {
|
|
|
|
fn new(value: Vec<u8>) -> Self {
|
2022-12-21 11:40:36 +00:00
|
|
|
Self(value)
|
|
|
|
}
|
|
|
|
|
2023-01-01 03:24:32 +00:00
|
|
|
fn object_type(&self) -> String {
|
|
|
|
stringify!($name)
|
|
|
|
.strip_suffix("Id")
|
|
|
|
.unwrap()
|
|
|
|
.to_ascii_lowercase()
|
|
|
|
.to_string()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn from_bytes(bytes: &[u8]) -> Self {
|
2022-12-21 11:40:36 +00:00
|
|
|
Self(bytes.to_vec())
|
|
|
|
}
|
|
|
|
|
2023-01-01 03:24:32 +00:00
|
|
|
fn as_bytes(&self) -> &[u8] {
|
2022-12-21 11:40:36 +00:00
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
|
2023-01-01 03:24:32 +00:00
|
|
|
fn to_bytes(&self) -> Vec<u8> {
|
2022-12-21 11:40:36 +00:00
|
|
|
self.0.clone()
|
|
|
|
}
|
|
|
|
|
2023-01-01 03:24:32 +00:00
|
|
|
fn from_hex(hex: &str) -> Self {
|
2022-12-21 11:40:36 +00:00
|
|
|
Self(hex::decode(hex).unwrap())
|
|
|
|
}
|
|
|
|
|
2023-01-01 03:24:32 +00:00
|
|
|
fn hex(&self) -> String {
|
2022-12-21 11:40:36 +00:00
|
|
|
hex::encode(&self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-07-26 18:39:43 +00:00
|
|
|
pub(crate) use {id_type, impl_id_type};
|
|
|
|
|
2022-12-21 12:00:07 +00:00
|
|
|
id_type!(pub CommitId);
|
|
|
|
id_type!(pub ChangeId);
|
|
|
|
id_type!(pub TreeId);
|
|
|
|
id_type!(pub FileId);
|
|
|
|
id_type!(pub SymlinkId);
|
|
|
|
id_type!(pub ConflictId);
|
2021-09-12 06:52:38 +00:00
|
|
|
|
|
|
|
pub enum Phase {
|
|
|
|
Public,
|
|
|
|
Draft,
|
|
|
|
}
|
|
|
|
|
2022-11-11 17:33:22 +00:00
|
|
|
content_hash! {
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
|
|
|
|
pub struct MillisSinceEpoch(pub i64);
|
|
|
|
}
|
2021-09-12 06:52:38 +00:00
|
|
|
|
2022-11-11 17:33:22 +00:00
|
|
|
content_hash! {
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
|
|
|
|
pub struct Timestamp {
|
|
|
|
pub timestamp: MillisSinceEpoch,
|
|
|
|
// time zone offset in minutes
|
|
|
|
pub tz_offset: i32,
|
|
|
|
}
|
2021-09-12 06:52:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Timestamp {
|
|
|
|
pub fn now() -> Self {
|
2022-03-05 06:33:15 +00:00
|
|
|
Self::from_datetime(chrono::offset::Local::now())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_datetime<Tz: chrono::TimeZone<Offset = chrono::offset::FixedOffset>>(
|
|
|
|
datetime: chrono::DateTime<Tz>,
|
|
|
|
) -> Self {
|
2021-09-12 06:52:38 +00:00
|
|
|
Self {
|
2022-09-30 04:29:45 +00:00
|
|
|
timestamp: MillisSinceEpoch(datetime.timestamp_millis()),
|
2022-03-05 06:33:15 +00:00
|
|
|
tz_offset: datetime.offset().local_minus_utc() / 60,
|
2021-09-12 06:52:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-12 19:19:03 +00:00
|
|
|
content_hash! {
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
pub struct Signature {
|
|
|
|
pub name: String,
|
|
|
|
pub email: String,
|
|
|
|
pub timestamp: Timestamp,
|
|
|
|
}
|
2021-09-12 06:52:38 +00:00
|
|
|
}
|
|
|
|
|
2023-08-24 23:59:07 +00:00
|
|
|
/// Identifies a single legacy tree, which may have path-level conflicts, or a
|
|
|
|
/// merge of multiple trees, where the individual trees do not have conflicts.
|
|
|
|
// TODO(#1624): Delete this type at some point in the future, when we decide to drop
|
|
|
|
// support for conflicts in older repos, or maybe after we have provided an upgrade
|
|
|
|
// mechanism.
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
pub enum MergedTreeId {
|
|
|
|
/// The tree id of a legacy tree
|
|
|
|
Legacy(TreeId),
|
|
|
|
/// The tree id(s) of a merge tree
|
|
|
|
Merge(Merge<TreeId>),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ContentHash for MergedTreeId {
|
|
|
|
fn hash(&self, state: &mut impl digest::Update) {
|
|
|
|
match self {
|
|
|
|
MergedTreeId::Legacy(tree_id) => {
|
|
|
|
state.update(b"0");
|
|
|
|
ContentHash::hash(tree_id, state);
|
|
|
|
}
|
|
|
|
MergedTreeId::Merge(tree_ids) => {
|
|
|
|
state.update(b"1");
|
|
|
|
ContentHash::hash(tree_ids, state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MergedTreeId {
|
|
|
|
/// Create a resolved `MergedTreeId` from a single regular tree.
|
|
|
|
pub fn resolved(tree_id: TreeId) -> Self {
|
|
|
|
MergedTreeId::Merge(Merge::resolved(tree_id))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// If this is a legacy tree, gets its tree id
|
|
|
|
// TODO(#1624): delete when all callers have been updated to support tree-level
|
|
|
|
// conflicts
|
|
|
|
pub fn as_legacy_tree_id(&self) -> &TreeId {
|
|
|
|
match &self {
|
|
|
|
MergedTreeId::Legacy(tree_id) => tree_id,
|
|
|
|
MergedTreeId::Merge(_) => panic!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-12 19:19:03 +00:00
|
|
|
content_hash! {
|
2023-01-20 19:40:16 +00:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
2022-11-12 19:19:03 +00:00
|
|
|
pub struct Commit {
|
|
|
|
pub parents: Vec<CommitId>,
|
|
|
|
pub predecessors: Vec<CommitId>,
|
2023-08-24 23:59:07 +00:00
|
|
|
pub root_tree: MergedTreeId,
|
2022-11-12 19:19:03 +00:00
|
|
|
pub change_id: ChangeId,
|
|
|
|
pub description: String,
|
|
|
|
pub author: Signature,
|
|
|
|
pub committer: Signature,
|
|
|
|
}
|
2021-09-12 06:52:38 +00:00
|
|
|
}
|
|
|
|
|
2022-11-12 19:19:03 +00:00
|
|
|
content_hash! {
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
2023-02-17 22:34:41 +00:00
|
|
|
pub struct ConflictTerm {
|
2022-11-12 19:19:03 +00:00
|
|
|
pub value: TreeValue,
|
|
|
|
}
|
2021-09-12 06:52:38 +00:00
|
|
|
}
|
|
|
|
|
2022-11-12 19:19:03 +00:00
|
|
|
content_hash! {
|
|
|
|
#[derive(Default, Debug, PartialEq, Eq, Clone)]
|
|
|
|
pub struct Conflict {
|
|
|
|
// A conflict is represented by a list of positive and negative states that need to be applied.
|
|
|
|
// In a simple 3-way merge of B and C with merge base A, the conflict will be { add: [B, C],
|
|
|
|
// remove: [A] }. Also note that a conflict of the form { add: [A], remove: [] } is the
|
|
|
|
// same as non-conflict A.
|
2023-02-17 22:34:41 +00:00
|
|
|
pub removes: Vec<ConflictTerm>,
|
|
|
|
pub adds: Vec<ConflictTerm>,
|
2022-11-12 19:19:03 +00:00
|
|
|
}
|
2021-09-12 06:52:38 +00:00
|
|
|
}
|
|
|
|
|
2023-07-06 04:20:24 +00:00
|
|
|
/// Error that may occur during backend initialization.
|
|
|
|
#[derive(Debug, Error)]
|
|
|
|
#[error(transparent)]
|
|
|
|
pub struct BackendInitError(pub Box<dyn std::error::Error + Send + Sync>);
|
|
|
|
|
|
|
|
/// Error that may occur during backend loading.
|
|
|
|
#[derive(Debug, Error)]
|
|
|
|
#[error(transparent)]
|
|
|
|
pub struct BackendLoadError(pub Box<dyn std::error::Error + Send + Sync>);
|
|
|
|
|
|
|
|
/// Commit-backend error that may occur after the backend is loaded.
|
2022-12-31 08:25:40 +00:00
|
|
|
#[derive(Debug, Error)]
|
2021-09-12 06:52:38 +00:00
|
|
|
pub enum BackendError {
|
2023-01-01 04:30:18 +00:00
|
|
|
#[error(
|
2023-01-02 16:53:11 +00:00
|
|
|
"Invalid hash length for object of type {object_type} (expected {expected} bytes, got \
|
|
|
|
{actual} bytes): {hash}"
|
2023-01-01 04:30:18 +00:00
|
|
|
)]
|
|
|
|
InvalidHashLength {
|
|
|
|
expected: usize,
|
|
|
|
actual: usize,
|
2023-01-02 16:53:11 +00:00
|
|
|
object_type: String,
|
|
|
|
hash: String,
|
|
|
|
},
|
|
|
|
#[error("Invalid hash for object of type {object_type} with hash {hash}: {source}")]
|
|
|
|
InvalidHash {
|
|
|
|
object_type: String,
|
2023-01-01 04:30:18 +00:00
|
|
|
hash: String,
|
2023-01-02 16:53:11 +00:00
|
|
|
source: Box<dyn std::error::Error + Send + Sync>,
|
|
|
|
},
|
|
|
|
#[error("Invalid UTF-8 for object {hash} of type {object_type}: {source}")]
|
|
|
|
InvalidUtf8 {
|
|
|
|
object_type: String,
|
|
|
|
hash: String,
|
2023-08-20 23:28:53 +00:00
|
|
|
// Box to reduce size for other error types that include this one:
|
|
|
|
// https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err
|
|
|
|
source: Box<std::string::FromUtf8Error>,
|
2023-01-02 16:53:11 +00:00
|
|
|
},
|
|
|
|
#[error("Object {hash} of type {object_type} not found: {source}")]
|
|
|
|
ObjectNotFound {
|
|
|
|
object_type: String,
|
|
|
|
hash: String,
|
|
|
|
source: Box<dyn std::error::Error + Send + Sync>,
|
|
|
|
},
|
|
|
|
#[error("Error when reading object {hash} of type {object_type}: {source}")]
|
|
|
|
ReadObject {
|
|
|
|
object_type: String,
|
|
|
|
hash: String,
|
|
|
|
source: Box<dyn std::error::Error + Send + Sync>,
|
|
|
|
},
|
|
|
|
#[error("Could not write object of type {object_type}: {source}")]
|
|
|
|
WriteObject {
|
|
|
|
object_type: &'static str,
|
|
|
|
source: Box<dyn std::error::Error + Send + Sync>,
|
2023-01-01 04:30:18 +00:00
|
|
|
},
|
2021-09-12 06:52:38 +00:00
|
|
|
#[error("Error: {0}")]
|
2023-07-05 10:55:54 +00:00
|
|
|
Other(Box<dyn std::error::Error + Send + Sync>),
|
2021-09-12 06:52:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub type BackendResult<T> = Result<T, BackendError>;
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
|
|
|
pub enum TreeValue {
|
2022-11-14 21:27:18 +00:00
|
|
|
File { id: FileId, executable: bool },
|
2021-09-12 06:52:38 +00:00
|
|
|
Symlink(SymlinkId),
|
|
|
|
Tree(TreeId),
|
|
|
|
GitSubmodule(CommitId),
|
|
|
|
Conflict(ConflictId),
|
|
|
|
}
|
|
|
|
|
2023-08-29 21:13:35 +00:00
|
|
|
impl TreeValue {
|
|
|
|
pub fn hex(&self) -> String {
|
|
|
|
match self {
|
|
|
|
TreeValue::File { id, .. } => id.hex(),
|
|
|
|
TreeValue::Symlink(id) => id.hex(),
|
|
|
|
TreeValue::Tree(id) => id.hex(),
|
|
|
|
TreeValue::GitSubmodule(id) => id.hex(),
|
|
|
|
TreeValue::Conflict(id) => id.hex(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-12 19:19:03 +00:00
|
|
|
impl ContentHash for TreeValue {
|
|
|
|
fn hash(&self, state: &mut impl digest::Update) {
|
|
|
|
use TreeValue::*;
|
2023-01-14 17:51:13 +00:00
|
|
|
match self {
|
|
|
|
File { id, executable } => {
|
2022-11-12 19:19:03 +00:00
|
|
|
state.update(&0u32.to_le_bytes());
|
|
|
|
id.hash(state);
|
|
|
|
executable.hash(state);
|
|
|
|
}
|
2023-01-14 17:51:13 +00:00
|
|
|
Symlink(id) => {
|
2022-11-12 19:19:03 +00:00
|
|
|
state.update(&1u32.to_le_bytes());
|
|
|
|
id.hash(state);
|
|
|
|
}
|
2023-01-14 17:51:13 +00:00
|
|
|
Tree(id) => {
|
2022-11-12 19:19:03 +00:00
|
|
|
state.update(&2u32.to_le_bytes());
|
|
|
|
id.hash(state);
|
|
|
|
}
|
2023-01-14 17:51:13 +00:00
|
|
|
GitSubmodule(id) => {
|
2022-11-12 19:19:03 +00:00
|
|
|
state.update(&3u32.to_le_bytes());
|
|
|
|
id.hash(state);
|
|
|
|
}
|
2023-01-14 17:51:13 +00:00
|
|
|
Conflict(id) => {
|
2022-11-12 19:19:03 +00:00
|
|
|
state.update(&4u32.to_le_bytes());
|
|
|
|
id.hash(state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-12 06:52:38 +00:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
pub struct TreeEntry<'a> {
|
|
|
|
name: &'a RepoPathComponent,
|
|
|
|
value: &'a TreeValue,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> TreeEntry<'a> {
|
|
|
|
pub fn new(name: &'a RepoPathComponent, value: &'a TreeValue) -> Self {
|
|
|
|
TreeEntry { name, value }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn name(&self) -> &'a RepoPathComponent {
|
|
|
|
self.name
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn value(&self) -> &'a TreeValue {
|
|
|
|
self.value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-15 05:16:06 +00:00
|
|
|
pub struct TreeEntriesNonRecursiveIterator<'a> {
|
2021-09-12 06:52:38 +00:00
|
|
|
iter: std::collections::btree_map::Iter<'a, RepoPathComponent, TreeValue>,
|
|
|
|
}
|
|
|
|
|
2021-11-15 05:16:06 +00:00
|
|
|
impl<'a> Iterator for TreeEntriesNonRecursiveIterator<'a> {
|
2021-09-12 06:52:38 +00:00
|
|
|
type Item = TreeEntry<'a>;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
self.iter
|
|
|
|
.next()
|
|
|
|
.map(|(name, value)| TreeEntry { name, value })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-12 19:19:03 +00:00
|
|
|
content_hash! {
|
2023-01-20 19:40:16 +00:00
|
|
|
#[derive(Default, PartialEq, Eq, Debug, Clone)]
|
2022-11-12 19:19:03 +00:00
|
|
|
pub struct Tree {
|
|
|
|
entries: BTreeMap<RepoPathComponent, TreeValue>,
|
|
|
|
}
|
2021-09-12 06:52:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Tree {
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.entries.is_empty()
|
|
|
|
}
|
|
|
|
|
2023-07-06 07:05:27 +00:00
|
|
|
pub fn names(&self) -> impl Iterator<Item = &RepoPathComponent> {
|
|
|
|
self.entries.keys()
|
|
|
|
}
|
|
|
|
|
2021-11-15 05:16:06 +00:00
|
|
|
pub fn entries(&self) -> TreeEntriesNonRecursiveIterator {
|
|
|
|
TreeEntriesNonRecursiveIterator {
|
2021-09-12 06:52:38 +00:00
|
|
|
iter: self.entries.iter(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set(&mut self, name: RepoPathComponent, value: TreeValue) {
|
|
|
|
self.entries.insert(name, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove(&mut self, name: &RepoPathComponent) {
|
|
|
|
self.entries.remove(name);
|
|
|
|
}
|
|
|
|
|
2023-06-28 10:09:54 +00:00
|
|
|
pub fn set_or_remove(&mut self, name: &RepoPathComponent, value: Option<TreeValue>) {
|
|
|
|
match value {
|
|
|
|
None => {
|
|
|
|
self.entries.remove(name);
|
|
|
|
}
|
|
|
|
Some(value) => {
|
|
|
|
self.entries.insert(name.clone(), value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-12 06:52:38 +00:00
|
|
|
pub fn entry(&self, name: &RepoPathComponent) -> Option<TreeEntry> {
|
|
|
|
self.entries
|
|
|
|
.get_key_value(name)
|
|
|
|
.map(|(name, value)| TreeEntry { name, value })
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn value(&self, name: &RepoPathComponent) -> Option<&TreeValue> {
|
|
|
|
self.entries.get(name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-18 11:18:57 +00:00
|
|
|
/// Calculates common prefix length of two bytes. The length to be returned is
|
|
|
|
/// a number of hexadecimal digits.
|
|
|
|
pub fn common_hex_len(bytes_a: &[u8], bytes_b: &[u8]) -> usize {
|
|
|
|
iter_half_bytes(bytes_a)
|
|
|
|
.zip(iter_half_bytes(bytes_b))
|
|
|
|
.take_while(|(a, b)| a == b)
|
|
|
|
.count()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn iter_half_bytes(bytes: &[u8]) -> impl ExactSizeIterator<Item = u8> + '_ {
|
|
|
|
(0..bytes.len() * 2).map(|i| {
|
|
|
|
let v = bytes[i / 2];
|
|
|
|
if i & 1 == 0 {
|
|
|
|
v >> 4
|
|
|
|
} else {
|
|
|
|
v & 0xf
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-02-06 18:15:01 +00:00
|
|
|
pub fn make_root_commit(root_change_id: ChangeId, empty_tree_id: TreeId) -> Commit {
|
2022-09-19 00:33:39 +00:00
|
|
|
let timestamp = Timestamp {
|
|
|
|
timestamp: MillisSinceEpoch(0),
|
|
|
|
tz_offset: 0,
|
|
|
|
};
|
|
|
|
let signature = Signature {
|
|
|
|
name: String::new(),
|
|
|
|
email: String::new(),
|
|
|
|
timestamp,
|
|
|
|
};
|
|
|
|
Commit {
|
|
|
|
parents: vec![],
|
|
|
|
predecessors: vec![],
|
2023-08-24 23:59:07 +00:00
|
|
|
root_tree: MergedTreeId::Legacy(empty_tree_id),
|
2023-02-06 18:15:01 +00:00
|
|
|
change_id: root_change_id,
|
2022-09-19 00:33:39 +00:00
|
|
|
description: String::new(),
|
|
|
|
author: signature.clone(),
|
|
|
|
committer: signature,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-12 06:52:38 +00:00
|
|
|
pub trait Backend: Send + Sync + Debug {
|
2023-05-12 13:05:32 +00:00
|
|
|
fn as_any(&self) -> &dyn Any;
|
|
|
|
|
2022-09-23 04:01:38 +00:00
|
|
|
/// A unique name that identifies this backend. Written to
|
|
|
|
/// `.jj/repo/store/backend` when the repo is created.
|
|
|
|
fn name(&self) -> &str;
|
|
|
|
|
2023-02-06 18:05:09 +00:00
|
|
|
/// The length of commit IDs in bytes.
|
|
|
|
fn commit_id_length(&self) -> usize;
|
2021-09-12 06:52:38 +00:00
|
|
|
|
2023-02-06 18:15:01 +00:00
|
|
|
/// The length of change IDs in bytes.
|
|
|
|
fn change_id_length(&self) -> usize;
|
|
|
|
|
2021-09-12 06:52:38 +00:00
|
|
|
fn read_file(&self, path: &RepoPath, id: &FileId) -> BackendResult<Box<dyn Read>>;
|
|
|
|
|
|
|
|
fn write_file(&self, path: &RepoPath, contents: &mut dyn Read) -> BackendResult<FileId>;
|
|
|
|
|
|
|
|
fn read_symlink(&self, path: &RepoPath, id: &SymlinkId) -> BackendResult<String>;
|
|
|
|
|
|
|
|
fn write_symlink(&self, path: &RepoPath, target: &str) -> BackendResult<SymlinkId>;
|
|
|
|
|
2022-09-19 00:33:39 +00:00
|
|
|
fn root_commit_id(&self) -> &CommitId;
|
|
|
|
|
2023-02-06 18:15:01 +00:00
|
|
|
fn root_change_id(&self) -> &ChangeId;
|
|
|
|
|
2021-09-12 06:52:38 +00:00
|
|
|
fn empty_tree_id(&self) -> &TreeId;
|
|
|
|
|
|
|
|
fn read_tree(&self, path: &RepoPath, id: &TreeId) -> BackendResult<Tree>;
|
|
|
|
|
|
|
|
fn write_tree(&self, path: &RepoPath, contents: &Tree) -> BackendResult<TreeId>;
|
|
|
|
|
2022-03-31 16:21:50 +00:00
|
|
|
fn read_conflict(&self, path: &RepoPath, id: &ConflictId) -> BackendResult<Conflict>;
|
2021-09-12 06:52:38 +00:00
|
|
|
|
2022-03-31 16:21:50 +00:00
|
|
|
fn write_conflict(&self, path: &RepoPath, contents: &Conflict) -> BackendResult<ConflictId>;
|
2022-04-28 20:31:28 +00:00
|
|
|
|
|
|
|
fn read_commit(&self, id: &CommitId) -> BackendResult<Commit>;
|
|
|
|
|
2023-05-11 22:40:24 +00:00
|
|
|
/// Writes a commit and returns its ID and the commit itself. The commit
|
|
|
|
/// should contain the data that was actually written, which may differ
|
|
|
|
/// from the data passed in. For example, the backend may change the
|
|
|
|
/// committer name to an authenticated user's name, or the backend's
|
|
|
|
/// timestamps may have less precision than the millisecond precision in
|
|
|
|
/// `Commit`.
|
|
|
|
fn write_commit(&self, contents: Commit) -> BackendResult<(CommitId, Commit)>;
|
2021-09-12 06:52:38 +00:00
|
|
|
}
|