mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-24 20:54:15 +00:00
lib: replace protobuf crate with prost
This commit is contained in:
parent
300f744e41
commit
aaa175eca7
16 changed files with 702 additions and 287 deletions
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
|
@ -42,6 +42,20 @@ jobs:
|
|||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
check-protos:
|
||||
name: Check protos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b
|
||||
- uses: dtolnay/rust-toolchain@e645b0cf01249a964ec099494d38d2da0f0b349f
|
||||
with:
|
||||
toolchain: stable
|
||||
- run: sudo apt update && sudo apt-get -y install protobuf-compiler
|
||||
- name: Generate Rust code from .proto files
|
||||
run: cargo run -p gen-protos
|
||||
- name: Check for uncommitted changes
|
||||
run: git diff --exit-code
|
||||
|
||||
rustfmt:
|
||||
name: Check formatting
|
||||
runs-on: ubuntu-latest
|
||||
|
|
111
Cargo.lock
generated
111
Cargo.lock
generated
|
@ -545,6 +545,12 @@ dependencies = [
|
|||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.9.0"
|
||||
|
@ -564,6 +570,13 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gen-protos"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"prost-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.6"
|
||||
|
@ -830,8 +843,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"protobuf",
|
||||
"protobuf-codegen",
|
||||
"prost",
|
||||
"rand",
|
||||
"regex",
|
||||
"serde_json",
|
||||
|
@ -984,6 +996,12 @@ dependencies = [
|
|||
"windows-sys 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multimap"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.1"
|
||||
|
@ -1180,6 +1198,16 @@ dependencies = [
|
|||
"sha1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
|
@ -1256,6 +1284,16 @@ dependencies = [
|
|||
"termtree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c8992a85d8e93a28bdf76137db888d3874e3b230dee5ed8bebac4c9f7617773"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
|
@ -1290,55 +1328,58 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "3.2.0"
|
||||
name = "prost"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e"
|
||||
checksum = "c01db6702aa05baa3f57dec92b8eeeeb4cb19e894e73996b32a4093289e54592"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"once_cell",
|
||||
"protobuf-support",
|
||||
"thiserror",
|
||||
"prost-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf-codegen"
|
||||
version = "3.2.0"
|
||||
name = "prost-build"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dd418ac3c91caa4032d37cb80ff0d44e2ebe637b2fb243b6234bf89cdac4901"
|
||||
checksum = "cb5320c680de74ba083512704acb90fe00f28f79207286a848e730c45dd73ed6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"once_cell",
|
||||
"protobuf",
|
||||
"protobuf-parse",
|
||||
"regex",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf-parse"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d39b14605eaa1f6a340aec7f320b34064feb26c93aec35d6a9a2272a8ddfa49"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"bytes",
|
||||
"heck",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"protobuf",
|
||||
"protobuf-support",
|
||||
"multimap",
|
||||
"petgraph",
|
||||
"prettyplease",
|
||||
"prost",
|
||||
"prost-types",
|
||||
"regex",
|
||||
"syn",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf-support"
|
||||
version = "3.2.0"
|
||||
name = "prost-derive"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372"
|
||||
checksum = "c8842bad1a5419bca14eac663ba798f6bc19c413c2fdceb5f3ba3b0932d96720"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prost-types"
|
||||
version = "0.11.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "017f79637768cde62820bc2d4fe0e45daaa027755c323ad077767c6c5f173091"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"prost",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -31,7 +31,7 @@ name = "diff_bench"
|
|||
harness = false
|
||||
|
||||
[workspace]
|
||||
members = ["lib", "lib/testutils"]
|
||||
members = ["lib", "lib/testutils", "lib/gen-protos"]
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.23", default-features = false, features = ["std", "clock"] }
|
||||
|
|
|
@ -14,7 +14,6 @@ readme = "../README.md"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
protobuf-codegen = "3.2.0"
|
||||
version_check = "0.9.4"
|
||||
|
||||
[dependencies]
|
||||
|
@ -32,7 +31,6 @@ maplit = "1.0.2"
|
|||
once_cell = "1.16.0"
|
||||
pest = "2.5.1"
|
||||
pest_derive = "2.5.1"
|
||||
protobuf = { version = "3.0.1", features = ["with-bytes"] }
|
||||
regex = "1.7.0"
|
||||
serde_json = "1.0.91"
|
||||
tempfile = "3.3.0"
|
||||
|
@ -42,6 +40,7 @@ uuid = { version = "1.2.2", features = ["v4"] }
|
|||
whoami = "1.2.3"
|
||||
zstd = "0.12.1"
|
||||
tracing = "0.1.37"
|
||||
prost = "0.11.5"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.5.0"
|
||||
|
|
18
lib/build.rs
18
lib/build.rs
|
@ -12,24 +12,12 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
fn main() {
|
||||
let input = &[
|
||||
"src/protos/op_store.proto",
|
||||
"src/protos/store.proto",
|
||||
"src/protos/working_copy.proto",
|
||||
];
|
||||
protobuf_codegen::Codegen::new()
|
||||
.pure()
|
||||
.inputs(input)
|
||||
.include("src/protos")
|
||||
.cargo_out_dir("protos")
|
||||
.run_from_script();
|
||||
fn main() -> std::io::Result<()> {
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
for file in input {
|
||||
println!("cargo:rerun-if-changed={file}");
|
||||
}
|
||||
|
||||
if let Some(true) = version_check::supports_feature("map_first_last") {
|
||||
println!("cargo:rustc-cfg=feature=\"map_first_last\"");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
8
lib/gen-protos/Cargo.toml
Normal file
8
lib/gen-protos/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "gen-protos"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
prost-build = "0.11.5"
|
34
lib/gen-protos/src/main.rs
Normal file
34
lib/gen-protos/src/main.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2022 The Jujutsu Authors
|
||||
//
|
||||
// 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.
|
||||
|
||||
use std::io::Result;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let input = ["op_store.proto", "store.proto", "working_copy.proto"];
|
||||
|
||||
let root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
|
||||
let protos_dir = root.join("src").join("protos");
|
||||
|
||||
prost_build::Config::new()
|
||||
.out_dir(&protos_dir)
|
||||
.include_file("mod.rs")
|
||||
.compile_protos(
|
||||
&input
|
||||
.into_iter()
|
||||
.map(|x| protos_dir.join(x))
|
||||
.collect::<Vec<_>>(),
|
||||
&[protos_dir],
|
||||
)
|
||||
}
|
|
@ -20,7 +20,7 @@ use std::sync::{Arc, Mutex};
|
|||
|
||||
use git2::Oid;
|
||||
use itertools::Itertools;
|
||||
use protobuf::Message;
|
||||
use prost::Message;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::backend::{
|
||||
|
@ -126,17 +126,18 @@ fn signature_to_git(signature: &Signature) -> git2::Signature {
|
|||
}
|
||||
|
||||
fn serialize_extras(commit: &Commit) -> Vec<u8> {
|
||||
let mut proto = crate::protos::store::Commit::new();
|
||||
proto.change_id = commit.change_id.to_bytes();
|
||||
let mut proto = crate::protos::store::Commit {
|
||||
change_id: commit.change_id.to_bytes(),
|
||||
..Default::default()
|
||||
};
|
||||
for predecessor in &commit.predecessors {
|
||||
proto.predecessors.push(predecessor.to_bytes());
|
||||
}
|
||||
proto.write_to_bytes().unwrap()
|
||||
proto.encode_to_vec()
|
||||
}
|
||||
|
||||
fn deserialize_extras(commit: &mut Commit, bytes: &[u8]) {
|
||||
let mut cursor = Cursor::new(bytes);
|
||||
let proto: crate::protos::store::Commit = Message::parse_from_reader(&mut cursor).unwrap();
|
||||
let proto = crate::protos::store::Commit::decode(bytes).unwrap();
|
||||
commit.change_id = ChangeId::new(proto.change_id);
|
||||
for predecessor in &proto.predecessors {
|
||||
commit.predecessors.push(CommitId::from_bytes(predecessor));
|
||||
|
|
|
@ -19,7 +19,7 @@ use std::io::{ErrorKind, Read, Write};
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use blake2::{Blake2b512, Digest};
|
||||
use protobuf::{Message, MessageField};
|
||||
use prost::Message;
|
||||
use tempfile::{NamedTempFile, PersistError};
|
||||
|
||||
use crate::backend::{
|
||||
|
@ -43,8 +43,8 @@ impl From<PersistError> for BackendError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<protobuf::Error> for BackendError {
|
||||
fn from(err: protobuf::Error) -> Self {
|
||||
impl From<prost::DecodeError> for BackendError {
|
||||
fn from(err: prost::DecodeError) -> Self {
|
||||
BackendError::Other(err.to_string())
|
||||
}
|
||||
}
|
||||
|
@ -183,17 +183,17 @@ impl Backend for LocalBackend {
|
|||
|
||||
fn read_tree(&self, _path: &RepoPath, id: &TreeId) -> BackendResult<Tree> {
|
||||
let path = self.tree_path(id);
|
||||
let mut file = File::open(path).map_err(not_found_to_backend_error)?;
|
||||
let buf = fs::read(path).map_err(not_found_to_backend_error)?;
|
||||
|
||||
let proto: crate::protos::store::Tree = Message::parse_from_reader(&mut file)?;
|
||||
Ok(tree_from_proto(&proto))
|
||||
let proto = crate::protos::store::Tree::decode(&*buf)?;
|
||||
Ok(tree_from_proto(proto))
|
||||
}
|
||||
|
||||
fn write_tree(&self, _path: &RepoPath, tree: &Tree) -> BackendResult<TreeId> {
|
||||
let temp_file = NamedTempFile::new_in(&self.path)?;
|
||||
|
||||
let proto = tree_to_proto(tree);
|
||||
proto.write_to_writer(&mut temp_file.as_file())?;
|
||||
temp_file.as_file().write_all(&proto.encode_to_vec())?;
|
||||
|
||||
let id = TreeId::new(blake2b_hash(tree).to_vec());
|
||||
|
||||
|
@ -203,17 +203,17 @@ impl Backend for LocalBackend {
|
|||
|
||||
fn read_conflict(&self, _path: &RepoPath, id: &ConflictId) -> BackendResult<Conflict> {
|
||||
let path = self.conflict_path(id);
|
||||
let mut file = File::open(path).map_err(not_found_to_backend_error)?;
|
||||
let buf = fs::read(path).map_err(not_found_to_backend_error)?;
|
||||
|
||||
let proto: crate::protos::store::Conflict = Message::parse_from_reader(&mut file)?;
|
||||
Ok(conflict_from_proto(&proto))
|
||||
let proto = crate::protos::store::Conflict::decode(&*buf)?;
|
||||
Ok(conflict_from_proto(proto))
|
||||
}
|
||||
|
||||
fn write_conflict(&self, _path: &RepoPath, conflict: &Conflict) -> BackendResult<ConflictId> {
|
||||
let temp_file = NamedTempFile::new_in(&self.path)?;
|
||||
|
||||
let proto = conflict_to_proto(conflict);
|
||||
proto.write_to_writer(&mut temp_file.as_file())?;
|
||||
temp_file.as_file().write_all(&proto.encode_to_vec())?;
|
||||
|
||||
let id = ConflictId::new(blake2b_hash(conflict).to_vec());
|
||||
|
||||
|
@ -227,17 +227,17 @@ impl Backend for LocalBackend {
|
|||
}
|
||||
|
||||
let path = self.commit_path(id);
|
||||
let mut file = File::open(path).map_err(not_found_to_backend_error)?;
|
||||
let buf = fs::read(path).map_err(not_found_to_backend_error)?;
|
||||
|
||||
let proto: crate::protos::store::Commit = Message::parse_from_reader(&mut file)?;
|
||||
Ok(commit_from_proto(&proto))
|
||||
let proto = crate::protos::store::Commit::decode(&*buf)?;
|
||||
Ok(commit_from_proto(proto))
|
||||
}
|
||||
|
||||
fn write_commit(&self, commit: &Commit) -> BackendResult<CommitId> {
|
||||
let temp_file = NamedTempFile::new_in(&self.path)?;
|
||||
|
||||
let proto = commit_to_proto(commit);
|
||||
proto.write_to_writer(&mut temp_file.as_file())?;
|
||||
temp_file.as_file().write_all(&proto.encode_to_vec())?;
|
||||
|
||||
let id = CommitId::new(blake2b_hash(commit).to_vec());
|
||||
|
||||
|
@ -247,7 +247,7 @@ impl Backend for LocalBackend {
|
|||
}
|
||||
|
||||
pub fn commit_to_proto(commit: &Commit) -> crate::protos::store::Commit {
|
||||
let mut proto = crate::protos::store::Commit::new();
|
||||
let mut proto = crate::protos::store::Commit::default();
|
||||
for parent in &commit.parents {
|
||||
proto.parents.push(parent.to_bytes());
|
||||
}
|
||||
|
@ -257,115 +257,116 @@ pub fn commit_to_proto(commit: &Commit) -> crate::protos::store::Commit {
|
|||
proto.root_tree = commit.root_tree.to_bytes();
|
||||
proto.change_id = commit.change_id.to_bytes();
|
||||
proto.description = commit.description.clone();
|
||||
proto.author = MessageField::some(signature_to_proto(&commit.author));
|
||||
proto.committer = MessageField::some(signature_to_proto(&commit.committer));
|
||||
proto.author = Some(signature_to_proto(&commit.author));
|
||||
proto.committer = Some(signature_to_proto(&commit.committer));
|
||||
proto
|
||||
}
|
||||
|
||||
fn commit_from_proto(proto: &crate::protos::store::Commit) -> Commit {
|
||||
let commit_id_from_proto = |parent: &Vec<u8>| CommitId::new(parent.clone());
|
||||
let parents = proto.parents.iter().map(commit_id_from_proto).collect();
|
||||
let predecessors = proto
|
||||
.predecessors
|
||||
.iter()
|
||||
.map(commit_id_from_proto)
|
||||
.collect();
|
||||
let root_tree = TreeId::new(proto.root_tree.to_vec());
|
||||
let change_id = ChangeId::new(proto.change_id.to_vec());
|
||||
fn commit_from_proto(proto: crate::protos::store::Commit) -> Commit {
|
||||
let parents = proto.parents.into_iter().map(CommitId::new).collect();
|
||||
let predecessors = proto.predecessors.into_iter().map(CommitId::new).collect();
|
||||
let root_tree = TreeId::new(proto.root_tree);
|
||||
let change_id = ChangeId::new(proto.change_id);
|
||||
Commit {
|
||||
parents,
|
||||
predecessors,
|
||||
root_tree,
|
||||
change_id,
|
||||
description: proto.description.clone(),
|
||||
author: signature_from_proto(&proto.author),
|
||||
committer: signature_from_proto(&proto.committer),
|
||||
description: proto.description,
|
||||
author: signature_from_proto(proto.author.unwrap_or_default()),
|
||||
committer: signature_from_proto(proto.committer.unwrap_or_default()),
|
||||
}
|
||||
}
|
||||
|
||||
fn tree_to_proto(tree: &Tree) -> crate::protos::store::Tree {
|
||||
let mut proto = crate::protos::store::Tree::new();
|
||||
let mut proto = crate::protos::store::Tree::default();
|
||||
for entry in tree.entries() {
|
||||
let mut proto_entry = crate::protos::store::tree::Entry::new();
|
||||
proto_entry.name = entry.name().string();
|
||||
proto_entry.value = MessageField::some(tree_value_to_proto(entry.value()));
|
||||
proto.entries.push(proto_entry);
|
||||
proto.entries.push(crate::protos::store::tree::Entry {
|
||||
name: entry.name().string(),
|
||||
value: Some(tree_value_to_proto(entry.value())),
|
||||
});
|
||||
}
|
||||
proto
|
||||
}
|
||||
|
||||
fn tree_from_proto(proto: &crate::protos::store::Tree) -> Tree {
|
||||
fn tree_from_proto(proto: crate::protos::store::Tree) -> Tree {
|
||||
let mut tree = Tree::default();
|
||||
for proto_entry in &proto.entries {
|
||||
let value = tree_value_from_proto(proto_entry.value.as_ref().unwrap());
|
||||
tree.set(RepoPathComponent::from(proto_entry.name.as_str()), value);
|
||||
for proto_entry in proto.entries {
|
||||
let value = tree_value_from_proto(proto_entry.value.unwrap());
|
||||
tree.set(RepoPathComponent::from(proto_entry.name), value);
|
||||
}
|
||||
tree
|
||||
}
|
||||
|
||||
fn tree_value_to_proto(value: &TreeValue) -> crate::protos::store::TreeValue {
|
||||
let mut proto = crate::protos::store::TreeValue::new();
|
||||
let mut proto = crate::protos::store::TreeValue::default();
|
||||
match value {
|
||||
TreeValue::File { id, executable } => {
|
||||
let mut file = crate::protos::store::tree_value::File::new();
|
||||
file.id = id.to_bytes();
|
||||
file.executable = *executable;
|
||||
proto.set_file(file);
|
||||
proto.value = Some(crate::protos::store::tree_value::Value::File(
|
||||
crate::protos::store::tree_value::File {
|
||||
id: id.to_bytes(),
|
||||
executable: *executable,
|
||||
},
|
||||
));
|
||||
}
|
||||
TreeValue::Symlink(id) => {
|
||||
proto.set_symlink_id(id.to_bytes());
|
||||
proto.value = Some(crate::protos::store::tree_value::Value::SymlinkId(
|
||||
id.to_bytes(),
|
||||
));
|
||||
}
|
||||
TreeValue::GitSubmodule(_id) => {
|
||||
panic!("cannot store git submodules");
|
||||
}
|
||||
TreeValue::Tree(id) => {
|
||||
proto.set_tree_id(id.to_bytes());
|
||||
proto.value = Some(crate::protos::store::tree_value::Value::TreeId(
|
||||
id.to_bytes(),
|
||||
));
|
||||
}
|
||||
TreeValue::Conflict(id) => {
|
||||
proto.set_conflict_id(id.to_bytes());
|
||||
proto.value = Some(crate::protos::store::tree_value::Value::ConflictId(
|
||||
id.to_bytes(),
|
||||
));
|
||||
}
|
||||
}
|
||||
proto
|
||||
}
|
||||
|
||||
fn tree_value_from_proto(proto: &crate::protos::store::TreeValue) -> TreeValue {
|
||||
match proto.value.as_ref().unwrap() {
|
||||
crate::protos::store::tree_value::Value::TreeId(id) => {
|
||||
TreeValue::Tree(TreeId::new(id.clone()))
|
||||
}
|
||||
fn tree_value_from_proto(proto: crate::protos::store::TreeValue) -> TreeValue {
|
||||
match proto.value.unwrap() {
|
||||
crate::protos::store::tree_value::Value::TreeId(id) => TreeValue::Tree(TreeId::new(id)),
|
||||
crate::protos::store::tree_value::Value::File(crate::protos::store::tree_value::File {
|
||||
id,
|
||||
executable,
|
||||
..
|
||||
}) => TreeValue::File {
|
||||
id: FileId::new(id.clone()),
|
||||
executable: *executable,
|
||||
id: FileId::new(id),
|
||||
executable,
|
||||
},
|
||||
crate::protos::store::tree_value::Value::SymlinkId(id) => {
|
||||
TreeValue::Symlink(SymlinkId::new(id.clone()))
|
||||
TreeValue::Symlink(SymlinkId::new(id))
|
||||
}
|
||||
crate::protos::store::tree_value::Value::ConflictId(id) => {
|
||||
TreeValue::Conflict(ConflictId::new(id.clone()))
|
||||
TreeValue::Conflict(ConflictId::new(id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn signature_to_proto(signature: &Signature) -> crate::protos::store::commit::Signature {
|
||||
let mut proto = crate::protos::store::commit::Signature::new();
|
||||
proto.name = signature.name.clone();
|
||||
proto.email = signature.email.clone();
|
||||
let mut timestamp_proto = crate::protos::store::commit::Timestamp::new();
|
||||
timestamp_proto.millis_since_epoch = signature.timestamp.timestamp.0;
|
||||
timestamp_proto.tz_offset = signature.timestamp.tz_offset;
|
||||
proto.timestamp = MessageField::some(timestamp_proto);
|
||||
proto
|
||||
crate::protos::store::commit::Signature {
|
||||
name: signature.name.clone(),
|
||||
email: signature.email.clone(),
|
||||
timestamp: Some(crate::protos::store::commit::Timestamp {
|
||||
millis_since_epoch: signature.timestamp.timestamp.0,
|
||||
tz_offset: signature.timestamp.tz_offset,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn signature_from_proto(proto: &crate::protos::store::commit::Signature) -> Signature {
|
||||
let timestamp = &proto.timestamp;
|
||||
fn signature_from_proto(proto: crate::protos::store::commit::Signature) -> Signature {
|
||||
let timestamp = proto.timestamp.unwrap_or_default();
|
||||
Signature {
|
||||
name: proto.name.clone(),
|
||||
email: proto.email.clone(),
|
||||
name: proto.name,
|
||||
email: proto.email,
|
||||
timestamp: Timestamp {
|
||||
timestamp: MillisSinceEpoch(timestamp.millis_since_epoch),
|
||||
tz_offset: timestamp.tz_offset,
|
||||
|
@ -374,7 +375,7 @@ fn signature_from_proto(proto: &crate::protos::store::commit::Signature) -> Sign
|
|||
}
|
||||
|
||||
fn conflict_to_proto(conflict: &Conflict) -> crate::protos::store::Conflict {
|
||||
let mut proto = crate::protos::store::Conflict::new();
|
||||
let mut proto = crate::protos::store::Conflict::default();
|
||||
for part in &conflict.adds {
|
||||
proto.adds.push(conflict_part_to_proto(part));
|
||||
}
|
||||
|
@ -384,25 +385,25 @@ fn conflict_to_proto(conflict: &Conflict) -> crate::protos::store::Conflict {
|
|||
proto
|
||||
}
|
||||
|
||||
fn conflict_from_proto(proto: &crate::protos::store::Conflict) -> Conflict {
|
||||
fn conflict_from_proto(proto: crate::protos::store::Conflict) -> Conflict {
|
||||
let mut conflict = Conflict::default();
|
||||
for part in &proto.removes {
|
||||
for part in proto.removes {
|
||||
conflict.removes.push(conflict_part_from_proto(part))
|
||||
}
|
||||
for part in &proto.adds {
|
||||
for part in proto.adds {
|
||||
conflict.adds.push(conflict_part_from_proto(part))
|
||||
}
|
||||
conflict
|
||||
}
|
||||
|
||||
fn conflict_part_from_proto(proto: &crate::protos::store::conflict::Part) -> ConflictPart {
|
||||
fn conflict_part_from_proto(proto: crate::protos::store::conflict::Part) -> ConflictPart {
|
||||
ConflictPart {
|
||||
value: tree_value_from_proto(proto.content.as_ref().unwrap()),
|
||||
value: tree_value_from_proto(proto.content.unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
fn conflict_part_to_proto(part: &ConflictPart) -> crate::protos::store::conflict::Part {
|
||||
let mut proto = crate::protos::store::conflict::Part::new();
|
||||
proto.content = MessageField::some(tree_value_to_proto(&part.value));
|
||||
proto
|
||||
crate::protos::store::conflict::Part {
|
||||
content: Some(tree_value_to_proto(&part.value)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,12 +15,11 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::ErrorKind;
|
||||
use std::io::{ErrorKind, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use itertools::Itertools;
|
||||
use protobuf::{Message, MessageField};
|
||||
use prost::Message;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
use crate::backend::{CommitId, MillisSinceEpoch, Timestamp};
|
||||
|
@ -31,8 +30,8 @@ use crate::op_store::{
|
|||
RefTarget, View, ViewId, WorkspaceId,
|
||||
};
|
||||
|
||||
impl From<protobuf::Error> for OpStoreError {
|
||||
fn from(err: protobuf::Error) -> Self {
|
||||
impl From<prost::DecodeError> for OpStoreError {
|
||||
fn from(err: prost::DecodeError) -> Self {
|
||||
OpStoreError::Other(err.to_string())
|
||||
}
|
||||
}
|
||||
|
@ -63,17 +62,17 @@ impl ProtoOpStore {
|
|||
|
||||
pub fn read_view(&self, id: &ViewId) -> OpStoreResult<View> {
|
||||
let path = self.view_path(id);
|
||||
let mut file = File::open(path).map_err(not_found_to_store_error)?;
|
||||
let buf = fs::read(path)?;
|
||||
|
||||
let proto: crate::protos::op_store::View = Message::parse_from_reader(&mut file)?;
|
||||
Ok(view_from_proto(&proto))
|
||||
let proto = crate::protos::op_store::View::decode(&*buf)?;
|
||||
Ok(view_from_proto(proto))
|
||||
}
|
||||
|
||||
pub fn write_view(&self, view: &View) -> OpStoreResult<ViewId> {
|
||||
let temp_file = NamedTempFile::new_in(&self.path)?;
|
||||
|
||||
let proto = view_to_proto(view);
|
||||
proto.write_to_writer(&mut temp_file.as_file())?;
|
||||
temp_file.as_file().write_all(&proto.encode_to_vec())?;
|
||||
|
||||
let id = ViewId::new(blake2b_hash(view).to_vec());
|
||||
|
||||
|
@ -83,17 +82,17 @@ impl ProtoOpStore {
|
|||
|
||||
pub fn read_operation(&self, id: &OperationId) -> OpStoreResult<Operation> {
|
||||
let path = self.operation_path(id);
|
||||
let mut file = File::open(path).map_err(not_found_to_store_error)?;
|
||||
let buf = fs::read(path).map_err(not_found_to_store_error)?;
|
||||
|
||||
let proto: crate::protos::op_store::Operation = Message::parse_from_reader(&mut file)?;
|
||||
Ok(operation_from_proto(&proto))
|
||||
let proto = crate::protos::op_store::Operation::decode(&*buf)?;
|
||||
Ok(operation_from_proto(proto))
|
||||
}
|
||||
|
||||
pub fn write_operation(&self, operation: &Operation) -> OpStoreResult<OperationId> {
|
||||
let temp_file = NamedTempFile::new_in(&self.path)?;
|
||||
|
||||
let proto = operation_to_proto(operation);
|
||||
proto.write_to_writer(&mut temp_file.as_file())?;
|
||||
temp_file.as_file().write_all(&proto.encode_to_vec())?;
|
||||
|
||||
let id = OperationId::new(blake2b_hash(operation).to_vec());
|
||||
|
||||
|
@ -111,13 +110,13 @@ fn not_found_to_store_error(err: std::io::Error) -> OpStoreError {
|
|||
}
|
||||
|
||||
fn timestamp_to_proto(timestamp: &Timestamp) -> crate::protos::op_store::Timestamp {
|
||||
let mut proto = crate::protos::op_store::Timestamp::new();
|
||||
proto.millis_since_epoch = timestamp.timestamp.0;
|
||||
proto.tz_offset = timestamp.tz_offset;
|
||||
proto
|
||||
crate::protos::op_store::Timestamp {
|
||||
millis_since_epoch: timestamp.timestamp.0,
|
||||
tz_offset: timestamp.tz_offset,
|
||||
}
|
||||
}
|
||||
|
||||
fn timestamp_from_proto(proto: &crate::protos::op_store::Timestamp) -> Timestamp {
|
||||
fn timestamp_from_proto(proto: crate::protos::op_store::Timestamp) -> Timestamp {
|
||||
Timestamp {
|
||||
timestamp: MillisSinceEpoch(proto.millis_since_epoch),
|
||||
tz_offset: proto.tz_offset,
|
||||
|
@ -127,50 +126,47 @@ fn timestamp_from_proto(proto: &crate::protos::op_store::Timestamp) -> Timestamp
|
|||
fn operation_metadata_to_proto(
|
||||
metadata: &OperationMetadata,
|
||||
) -> crate::protos::op_store::OperationMetadata {
|
||||
let mut proto = crate::protos::op_store::OperationMetadata::new();
|
||||
proto.start_time = MessageField::some(timestamp_to_proto(&metadata.start_time));
|
||||
proto.end_time = MessageField::some(timestamp_to_proto(&metadata.end_time));
|
||||
proto.description = metadata.description.clone();
|
||||
proto.hostname = metadata.hostname.clone();
|
||||
proto.username = metadata.username.clone();
|
||||
proto.tags = metadata.tags.clone();
|
||||
proto
|
||||
crate::protos::op_store::OperationMetadata {
|
||||
start_time: Some(timestamp_to_proto(&metadata.start_time)),
|
||||
end_time: Some(timestamp_to_proto(&metadata.end_time)),
|
||||
description: metadata.description.clone(),
|
||||
hostname: metadata.hostname.clone(),
|
||||
username: metadata.username.clone(),
|
||||
tags: metadata.tags.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn operation_metadata_from_proto(
|
||||
proto: &crate::protos::op_store::OperationMetadata,
|
||||
proto: crate::protos::op_store::OperationMetadata,
|
||||
) -> OperationMetadata {
|
||||
let start_time = timestamp_from_proto(&proto.start_time);
|
||||
let end_time = timestamp_from_proto(&proto.end_time);
|
||||
let description = proto.description.to_owned();
|
||||
let hostname = proto.hostname.to_owned();
|
||||
let username = proto.username.to_owned();
|
||||
let tags = proto.tags.clone();
|
||||
let start_time = timestamp_from_proto(proto.start_time.unwrap_or_default());
|
||||
let end_time = timestamp_from_proto(proto.end_time.unwrap_or_default());
|
||||
OperationMetadata {
|
||||
start_time,
|
||||
end_time,
|
||||
description,
|
||||
hostname,
|
||||
username,
|
||||
tags,
|
||||
description: proto.description,
|
||||
hostname: proto.hostname,
|
||||
username: proto.username,
|
||||
tags: proto.tags,
|
||||
}
|
||||
}
|
||||
|
||||
fn operation_to_proto(operation: &Operation) -> crate::protos::op_store::Operation {
|
||||
let mut proto = crate::protos::op_store::Operation::new();
|
||||
proto.view_id = operation.view_id.as_bytes().to_vec();
|
||||
let mut proto = crate::protos::op_store::Operation {
|
||||
view_id: operation.view_id.as_bytes().to_vec(),
|
||||
metadata: Some(operation_metadata_to_proto(&operation.metadata)),
|
||||
..Default::default()
|
||||
};
|
||||
for parent in &operation.parents {
|
||||
proto.parents.push(parent.to_bytes());
|
||||
}
|
||||
proto.metadata = MessageField::some(operation_metadata_to_proto(&operation.metadata));
|
||||
proto
|
||||
}
|
||||
|
||||
fn operation_from_proto(proto: &crate::protos::op_store::Operation) -> Operation {
|
||||
let operation_id_from_proto = |parent: &Vec<u8>| OperationId::new(parent.clone());
|
||||
let parents = proto.parents.iter().map(operation_id_from_proto).collect();
|
||||
let view_id = ViewId::new(proto.view_id.clone());
|
||||
let metadata = operation_metadata_from_proto(&proto.metadata);
|
||||
fn operation_from_proto(proto: crate::protos::op_store::Operation) -> Operation {
|
||||
let parents = proto.parents.into_iter().map(OperationId::new).collect();
|
||||
let view_id = ViewId::new(proto.view_id);
|
||||
let metadata = operation_metadata_from_proto(proto.metadata.unwrap_or_default());
|
||||
Operation {
|
||||
view_id,
|
||||
parents,
|
||||
|
@ -179,7 +175,7 @@ fn operation_from_proto(proto: &crate::protos::op_store::Operation) -> Operation
|
|||
}
|
||||
|
||||
fn view_to_proto(view: &View) -> crate::protos::op_store::View {
|
||||
let mut proto = crate::protos::op_store::View::new();
|
||||
let mut proto = crate::protos::op_store::View::default();
|
||||
for (workspace_id, commit_id) in &view.wc_commit_ids {
|
||||
proto
|
||||
.wc_commit_ids
|
||||
|
@ -193,32 +189,38 @@ fn view_to_proto(view: &View) -> crate::protos::op_store::View {
|
|||
}
|
||||
|
||||
for (name, target) in &view.branches {
|
||||
let mut branch_proto = crate::protos::op_store::Branch::new();
|
||||
let mut branch_proto = crate::protos::op_store::Branch {
|
||||
name: name.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
branch_proto.name = name.clone();
|
||||
if let Some(local_target) = &target.local_target {
|
||||
branch_proto.local_target = MessageField::some(ref_target_to_proto(local_target));
|
||||
branch_proto.local_target = Some(ref_target_to_proto(local_target));
|
||||
}
|
||||
for (remote_name, target) in &target.remote_targets {
|
||||
let mut remote_branch_proto = crate::protos::op_store::RemoteBranch::new();
|
||||
remote_branch_proto.remote_name = remote_name.clone();
|
||||
remote_branch_proto.target = MessageField::some(ref_target_to_proto(target));
|
||||
branch_proto.remote_branches.push(remote_branch_proto);
|
||||
branch_proto
|
||||
.remote_branches
|
||||
.push(crate::protos::op_store::RemoteBranch {
|
||||
remote_name: remote_name.clone(),
|
||||
target: Some(ref_target_to_proto(target)),
|
||||
});
|
||||
}
|
||||
proto.branches.push(branch_proto);
|
||||
}
|
||||
|
||||
for (name, target) in &view.tags {
|
||||
let mut tag_proto = crate::protos::op_store::Tag::new();
|
||||
tag_proto.name = name.clone();
|
||||
tag_proto.target = MessageField::some(ref_target_to_proto(target));
|
||||
proto.tags.push(tag_proto);
|
||||
proto.tags.push(crate::protos::op_store::Tag {
|
||||
name: name.clone(),
|
||||
target: Some(ref_target_to_proto(target)),
|
||||
});
|
||||
}
|
||||
|
||||
for (git_ref_name, target) in &view.git_refs {
|
||||
let mut git_ref_proto = crate::protos::op_store::GitRef::new();
|
||||
git_ref_proto.name = git_ref_name.clone();
|
||||
git_ref_proto.target = MessageField::some(ref_target_to_proto(target));
|
||||
proto.git_refs.push(git_ref_proto);
|
||||
proto.git_refs.push(crate::protos::op_store::GitRef {
|
||||
name: git_ref_name.clone(),
|
||||
target: Some(ref_target_to_proto(target)),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(git_head) = &view.git_head {
|
||||
|
@ -228,41 +230,34 @@ fn view_to_proto(view: &View) -> crate::protos::op_store::View {
|
|||
proto
|
||||
}
|
||||
|
||||
fn view_from_proto(proto: &crate::protos::op_store::View) -> View {
|
||||
fn view_from_proto(proto: crate::protos::op_store::View) -> View {
|
||||
let mut view = View::default();
|
||||
// For compatibility with old repos before we had support for multiple working
|
||||
// copies
|
||||
#[allow(deprecated)]
|
||||
if !proto.wc_commit_id.is_empty() {
|
||||
view.wc_commit_ids.insert(
|
||||
WorkspaceId::default(),
|
||||
CommitId::new(proto.wc_commit_id.clone()),
|
||||
);
|
||||
view.wc_commit_ids
|
||||
.insert(WorkspaceId::default(), CommitId::new(proto.wc_commit_id));
|
||||
}
|
||||
for (workspace_id, commit_id) in &proto.wc_commit_ids {
|
||||
view.wc_commit_ids.insert(
|
||||
WorkspaceId::new(workspace_id.clone()),
|
||||
CommitId::new(commit_id.clone()),
|
||||
);
|
||||
for (workspace_id, commit_id) in proto.wc_commit_ids {
|
||||
view.wc_commit_ids
|
||||
.insert(WorkspaceId::new(workspace_id), CommitId::new(commit_id));
|
||||
}
|
||||
for head_id_bytes in &proto.head_ids {
|
||||
view.head_ids.insert(CommitId::from_bytes(head_id_bytes));
|
||||
for head_id_bytes in proto.head_ids {
|
||||
view.head_ids.insert(CommitId::new(head_id_bytes));
|
||||
}
|
||||
for head_id_bytes in &proto.public_head_ids {
|
||||
view.public_head_ids
|
||||
.insert(CommitId::from_bytes(head_id_bytes));
|
||||
for head_id_bytes in proto.public_head_ids {
|
||||
view.public_head_ids.insert(CommitId::new(head_id_bytes));
|
||||
}
|
||||
|
||||
for branch_proto in &proto.branches {
|
||||
let local_target = branch_proto
|
||||
.local_target
|
||||
.as_ref()
|
||||
.map(ref_target_from_proto);
|
||||
for branch_proto in proto.branches {
|
||||
let local_target = branch_proto.local_target.map(ref_target_from_proto);
|
||||
|
||||
let mut remote_targets = BTreeMap::new();
|
||||
for remote_branch in &branch_proto.remote_branches {
|
||||
for remote_branch in branch_proto.remote_branches {
|
||||
remote_targets.insert(
|
||||
remote_branch.remote_name.clone(),
|
||||
ref_target_from_proto(&remote_branch.target),
|
||||
remote_branch.remote_name,
|
||||
ref_target_from_proto(remote_branch.target.unwrap_or_default()),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -275,69 +270,69 @@ fn view_from_proto(proto: &crate::protos::op_store::View) -> View {
|
|||
);
|
||||
}
|
||||
|
||||
for tag_proto in &proto.tags {
|
||||
for tag_proto in proto.tags {
|
||||
view.tags.insert(
|
||||
tag_proto.name.clone(),
|
||||
ref_target_from_proto(&tag_proto.target),
|
||||
tag_proto.name,
|
||||
ref_target_from_proto(tag_proto.target.unwrap_or_default()),
|
||||
);
|
||||
}
|
||||
|
||||
for git_ref in &proto.git_refs {
|
||||
if let Some(target) = git_ref.target.as_ref() {
|
||||
for git_ref in proto.git_refs {
|
||||
if let Some(target) = git_ref.target {
|
||||
view.git_refs
|
||||
.insert(git_ref.name.clone(), ref_target_from_proto(target));
|
||||
.insert(git_ref.name, ref_target_from_proto(target));
|
||||
} else {
|
||||
// Legacy format
|
||||
view.git_refs.insert(
|
||||
git_ref.name.clone(),
|
||||
RefTarget::Normal(CommitId::new(git_ref.commit_id.clone())),
|
||||
git_ref.name,
|
||||
RefTarget::Normal(CommitId::new(git_ref.commit_id)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if !proto.git_head.is_empty() {
|
||||
view.git_head = Some(CommitId::new(proto.git_head.clone()));
|
||||
view.git_head = Some(CommitId::new(proto.git_head));
|
||||
}
|
||||
|
||||
view
|
||||
}
|
||||
|
||||
fn ref_target_to_proto(value: &RefTarget) -> crate::protos::op_store::RefTarget {
|
||||
let mut proto = crate::protos::op_store::RefTarget::new();
|
||||
let mut proto = crate::protos::op_store::RefTarget::default();
|
||||
match value {
|
||||
RefTarget::Normal(id) => {
|
||||
proto.set_commit_id(id.to_bytes());
|
||||
proto.value = Some(crate::protos::op_store::ref_target::Value::CommitId(
|
||||
id.to_bytes(),
|
||||
));
|
||||
}
|
||||
RefTarget::Conflict { removes, adds } => {
|
||||
let mut ref_conflict_proto = crate::protos::op_store::RefConflict::new();
|
||||
let mut ref_conflict_proto = crate::protos::op_store::RefConflict::default();
|
||||
for id in removes {
|
||||
ref_conflict_proto.removes.push(id.to_bytes());
|
||||
}
|
||||
for id in adds {
|
||||
ref_conflict_proto.adds.push(id.to_bytes());
|
||||
}
|
||||
proto.set_conflict(ref_conflict_proto);
|
||||
proto.value = Some(crate::protos::op_store::ref_target::Value::Conflict(
|
||||
ref_conflict_proto,
|
||||
));
|
||||
}
|
||||
}
|
||||
proto
|
||||
}
|
||||
|
||||
fn ref_target_from_proto(proto: &crate::protos::op_store::RefTarget) -> RefTarget {
|
||||
match proto.value.as_ref().unwrap() {
|
||||
fn ref_target_from_proto(proto: crate::protos::op_store::RefTarget) -> RefTarget {
|
||||
match proto.value.unwrap() {
|
||||
crate::protos::op_store::ref_target::Value::CommitId(id) => {
|
||||
RefTarget::Normal(CommitId::from_bytes(id))
|
||||
RefTarget::Normal(CommitId::new(id))
|
||||
}
|
||||
crate::protos::op_store::ref_target::Value::Conflict(conflict) => {
|
||||
let removes = conflict
|
||||
.removes
|
||||
.iter()
|
||||
.map(|id_bytes| CommitId::from_bytes(id_bytes))
|
||||
.collect_vec();
|
||||
let adds = conflict
|
||||
.adds
|
||||
.iter()
|
||||
.map(|id_bytes| CommitId::from_bytes(id_bytes))
|
||||
.into_iter()
|
||||
.map(CommitId::new)
|
||||
.collect_vec();
|
||||
let adds = conflict.adds.into_iter().map(CommitId::new).collect_vec();
|
||||
RefTarget::Conflict { removes, adds }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
// Copyright 2020 The Jujutsu Authors
|
||||
//
|
||||
// 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.
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));
|
||||
pub mod op_store {
|
||||
include!("op_store.rs");
|
||||
}
|
||||
pub mod store {
|
||||
include!("store.rs");
|
||||
}
|
||||
pub mod working_copy {
|
||||
include!("working_copy.rs");
|
||||
}
|
||||
|
|
132
lib/src/protos/op_store.rs
Normal file
132
lib/src/protos/op_store.rs
Normal file
|
@ -0,0 +1,132 @@
|
|||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct RefConflict {
|
||||
#[prost(bytes = "vec", repeated, tag = "1")]
|
||||
pub removes: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
|
||||
#[prost(bytes = "vec", repeated, tag = "2")]
|
||||
pub adds: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct RefTarget {
|
||||
#[prost(oneof = "ref_target::Value", tags = "1, 2")]
|
||||
pub value: ::core::option::Option<ref_target::Value>,
|
||||
}
|
||||
/// Nested message and enum types in `RefTarget`.
|
||||
pub mod ref_target {
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Value {
|
||||
#[prost(bytes, tag = "1")]
|
||||
CommitId(::prost::alloc::vec::Vec<u8>),
|
||||
#[prost(message, tag = "2")]
|
||||
Conflict(super::RefConflict),
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct RemoteBranch {
|
||||
#[prost(string, tag = "1")]
|
||||
pub remote_name: ::prost::alloc::string::String,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub target: ::core::option::Option<RefTarget>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Branch {
|
||||
#[prost(string, tag = "1")]
|
||||
pub name: ::prost::alloc::string::String,
|
||||
/// Unset if the branch has been deleted locally.
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub local_target: ::core::option::Option<RefTarget>,
|
||||
/// TODO: How would we support renaming remotes while having undo work? If
|
||||
/// the remote name is stored in config, it's going to become a mess if the
|
||||
/// remote is renamed but the configs are left unchanged. Should each remote
|
||||
/// be identified (here and in configs) by a UUID?
|
||||
#[prost(message, repeated, tag = "3")]
|
||||
pub remote_branches: ::prost::alloc::vec::Vec<RemoteBranch>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GitRef {
|
||||
#[prost(string, tag = "1")]
|
||||
pub name: ::prost::alloc::string::String,
|
||||
/// This field is just for historical reasons (before we had the RefTarget
|
||||
/// type). New GitRefs have (only) the target field.
|
||||
/// TODO: Delete support for the old format.
|
||||
#[prost(bytes = "vec", tag = "2")]
|
||||
pub commit_id: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(message, optional, tag = "3")]
|
||||
pub target: ::core::option::Option<RefTarget>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Tag {
|
||||
#[prost(string, tag = "1")]
|
||||
pub name: ::prost::alloc::string::String,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub target: ::core::option::Option<RefTarget>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct View {
|
||||
#[prost(bytes = "vec", repeated, tag = "1")]
|
||||
pub head_ids: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
|
||||
#[prost(bytes = "vec", repeated, tag = "4")]
|
||||
pub public_head_ids: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
|
||||
#[deprecated]
|
||||
#[prost(bytes = "vec", tag = "2")]
|
||||
pub wc_commit_id: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(map = "string, bytes", tag = "8")]
|
||||
pub wc_commit_ids: ::std::collections::HashMap<
|
||||
::prost::alloc::string::String,
|
||||
::prost::alloc::vec::Vec<u8>,
|
||||
>,
|
||||
#[prost(message, repeated, tag = "5")]
|
||||
pub branches: ::prost::alloc::vec::Vec<Branch>,
|
||||
#[prost(message, repeated, tag = "6")]
|
||||
pub tags: ::prost::alloc::vec::Vec<Tag>,
|
||||
/// Only a subset of the refs. For example, does not include refs/notes/.
|
||||
#[prost(message, repeated, tag = "3")]
|
||||
pub git_refs: ::prost::alloc::vec::Vec<GitRef>,
|
||||
#[prost(bytes = "vec", tag = "7")]
|
||||
pub git_head: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Operation {
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub view_id: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(bytes = "vec", repeated, tag = "2")]
|
||||
pub parents: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
|
||||
#[prost(message, optional, tag = "3")]
|
||||
pub metadata: ::core::option::Option<OperationMetadata>,
|
||||
}
|
||||
/// TODO: Share with store.proto? Do we even need the timezone here?
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Timestamp {
|
||||
#[prost(int64, tag = "1")]
|
||||
pub millis_since_epoch: i64,
|
||||
#[prost(int32, tag = "2")]
|
||||
pub tz_offset: i32,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct OperationMetadata {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub start_time: ::core::option::Option<Timestamp>,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub end_time: ::core::option::Option<Timestamp>,
|
||||
#[prost(string, tag = "3")]
|
||||
pub description: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "4")]
|
||||
pub hostname: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "5")]
|
||||
pub username: ::prost::alloc::string::String,
|
||||
#[prost(map = "string, string", tag = "6")]
|
||||
pub tags: ::std::collections::HashMap<
|
||||
::prost::alloc::string::String,
|
||||
::prost::alloc::string::String,
|
||||
>,
|
||||
}
|
108
lib/src/protos/store.rs
Normal file
108
lib/src/protos/store.rs
Normal file
|
@ -0,0 +1,108 @@
|
|||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct TreeValue {
|
||||
#[prost(oneof = "tree_value::Value", tags = "2, 3, 4, 5")]
|
||||
pub value: ::core::option::Option<tree_value::Value>,
|
||||
}
|
||||
/// Nested message and enum types in `TreeValue`.
|
||||
pub mod tree_value {
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct File {
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub id: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(bool, tag = "2")]
|
||||
pub executable: bool,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Value {
|
||||
#[prost(message, tag = "2")]
|
||||
File(File),
|
||||
#[prost(bytes, tag = "3")]
|
||||
SymlinkId(::prost::alloc::vec::Vec<u8>),
|
||||
#[prost(bytes, tag = "4")]
|
||||
TreeId(::prost::alloc::vec::Vec<u8>),
|
||||
#[prost(bytes, tag = "5")]
|
||||
ConflictId(::prost::alloc::vec::Vec<u8>),
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Tree {
|
||||
#[prost(message, repeated, tag = "1")]
|
||||
pub entries: ::prost::alloc::vec::Vec<tree::Entry>,
|
||||
}
|
||||
/// Nested message and enum types in `Tree`.
|
||||
pub mod tree {
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Entry {
|
||||
#[prost(string, tag = "1")]
|
||||
pub name: ::prost::alloc::string::String,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub value: ::core::option::Option<super::TreeValue>,
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Commit {
|
||||
#[prost(bytes = "vec", repeated, tag = "1")]
|
||||
pub parents: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
|
||||
#[prost(bytes = "vec", repeated, tag = "2")]
|
||||
pub predecessors: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec<u8>>,
|
||||
#[prost(bytes = "vec", tag = "3")]
|
||||
pub root_tree: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(bytes = "vec", tag = "4")]
|
||||
pub change_id: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(string, tag = "5")]
|
||||
pub description: ::prost::alloc::string::String,
|
||||
#[prost(message, optional, tag = "6")]
|
||||
pub author: ::core::option::Option<commit::Signature>,
|
||||
#[prost(message, optional, tag = "7")]
|
||||
pub committer: ::core::option::Option<commit::Signature>,
|
||||
#[deprecated]
|
||||
#[prost(bool, tag = "8")]
|
||||
pub is_open: bool,
|
||||
#[deprecated]
|
||||
#[prost(bool, tag = "9")]
|
||||
pub is_pruned: bool,
|
||||
}
|
||||
/// Nested message and enum types in `Commit`.
|
||||
pub mod commit {
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Timestamp {
|
||||
#[prost(int64, tag = "1")]
|
||||
pub millis_since_epoch: i64,
|
||||
#[prost(int32, tag = "2")]
|
||||
pub tz_offset: i32,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Signature {
|
||||
#[prost(string, tag = "1")]
|
||||
pub name: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "2")]
|
||||
pub email: ::prost::alloc::string::String,
|
||||
#[prost(message, optional, tag = "3")]
|
||||
pub timestamp: ::core::option::Option<Timestamp>,
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Conflict {
|
||||
#[prost(message, repeated, tag = "1")]
|
||||
pub removes: ::prost::alloc::vec::Vec<conflict::Part>,
|
||||
#[prost(message, repeated, tag = "2")]
|
||||
pub adds: ::prost::alloc::vec::Vec<conflict::Part>,
|
||||
}
|
||||
/// Nested message and enum types in `Conflict`.
|
||||
pub mod conflict {
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Part {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub content: ::core::option::Option<super::TreeValue>,
|
||||
}
|
||||
}
|
85
lib/src/protos/working_copy.rs
Normal file
85
lib/src/protos/working_copy.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct FileState {
|
||||
#[prost(int64, tag = "1")]
|
||||
pub mtime_millis_since_epoch: i64,
|
||||
#[prost(uint64, tag = "2")]
|
||||
pub size: u64,
|
||||
#[prost(enumeration = "FileType", tag = "3")]
|
||||
pub file_type: i32,
|
||||
/// Set only if file_type is Conflict
|
||||
#[prost(bytes = "vec", tag = "4")]
|
||||
pub conflict_id: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SparsePatterns {
|
||||
#[prost(string, repeated, tag = "1")]
|
||||
pub prefixes: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct TreeState {
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub tree_id: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(map = "string, message", tag = "2")]
|
||||
pub file_states: ::std::collections::HashMap<
|
||||
::prost::alloc::string::String,
|
||||
FileState,
|
||||
>,
|
||||
#[prost(message, optional, tag = "3")]
|
||||
pub sparse_patterns: ::core::option::Option<SparsePatterns>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Checkout {
|
||||
/// The operation at which the working copy was updated.
|
||||
#[prost(bytes = "vec", tag = "2")]
|
||||
pub operation_id: ::prost::alloc::vec::Vec<u8>,
|
||||
/// An identifier for this workspace. It is used for looking up the current
|
||||
/// checkout in the repo view. Currently a human-readable name.
|
||||
/// TODO: Is it better to make this a UUID and a have map that to a name in
|
||||
/// config? That way users can rename a workspace.
|
||||
#[prost(string, tag = "3")]
|
||||
pub workspace_id: ::prost::alloc::string::String,
|
||||
/// The checked-out commit, which can be viewed as a cache of the working-copy
|
||||
/// commit ID recorded in `operation_id`'s operation. No longer used.
|
||||
/// TODO: Delete this mid 2022 or so
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub commit_id: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
|
||||
#[repr(i32)]
|
||||
pub enum FileType {
|
||||
Normal = 0,
|
||||
Symlink = 1,
|
||||
Executable = 2,
|
||||
Conflict = 3,
|
||||
GitSubmodule = 4,
|
||||
}
|
||||
impl FileType {
|
||||
/// 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 {
|
||||
FileType::Normal => "Normal",
|
||||
FileType::Symlink => "Symlink",
|
||||
FileType::Executable => "Executable",
|
||||
FileType::Conflict => "Conflict",
|
||||
FileType::GitSubmodule => "GitSubmodule",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"Normal" => Some(Self::Normal),
|
||||
"Symlink" => Some(Self::Symlink),
|
||||
"Executable" => Some(Self::Executable),
|
||||
"Conflict" => Some(Self::Conflict),
|
||||
"GitSubmodule" => Some(Self::GitSubmodule),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -39,10 +39,14 @@ impl RepoPathComponent {
|
|||
|
||||
impl From<&str> for RepoPathComponent {
|
||||
fn from(value: &str) -> Self {
|
||||
RepoPathComponent::from(value.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for RepoPathComponent {
|
||||
fn from(value: String) -> Self {
|
||||
assert!(!value.contains('/'));
|
||||
RepoPathComponent {
|
||||
value: value.to_owned(),
|
||||
}
|
||||
RepoPathComponent { value }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ use std::sync::Arc;
|
|||
use std::time::UNIX_EPOCH;
|
||||
|
||||
use once_cell::unsync::OnceCell;
|
||||
use protobuf::{EnumOrUnknown, Message, MessageField};
|
||||
use prost::Message;
|
||||
use tempfile::NamedTempFile;
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -125,13 +125,13 @@ pub struct TreeState {
|
|||
own_mtime: MillisSinceEpoch,
|
||||
}
|
||||
|
||||
fn file_state_from_proto(proto: &crate::protos::working_copy::FileState) -> FileState {
|
||||
let file_type = match proto.file_type.enum_value_or_default() {
|
||||
fn file_state_from_proto(proto: crate::protos::working_copy::FileState) -> FileState {
|
||||
let file_type = match proto.file_type() {
|
||||
crate::protos::working_copy::FileType::Normal => FileType::Normal { executable: false },
|
||||
crate::protos::working_copy::FileType::Executable => FileType::Normal { executable: true },
|
||||
crate::protos::working_copy::FileType::Symlink => FileType::Symlink,
|
||||
crate::protos::working_copy::FileType::Conflict => {
|
||||
let id = ConflictId::new(proto.conflict_id.to_vec());
|
||||
let id = ConflictId::new(proto.conflict_id);
|
||||
FileType::Conflict { id }
|
||||
}
|
||||
crate::protos::working_copy::FileType::GitSubmodule => FileType::GitSubmodule,
|
||||
|
@ -144,7 +144,7 @@ fn file_state_from_proto(proto: &crate::protos::working_copy::FileState) -> File
|
|||
}
|
||||
|
||||
fn file_state_to_proto(file_state: &FileState) -> crate::protos::working_copy::FileState {
|
||||
let mut proto = crate::protos::working_copy::FileState::new();
|
||||
let mut proto = crate::protos::working_copy::FileState::default();
|
||||
let file_type = match &file_state.file_type {
|
||||
FileType::Normal { executable: false } => crate::protos::working_copy::FileType::Normal,
|
||||
FileType::Normal { executable: true } => crate::protos::working_copy::FileType::Executable,
|
||||
|
@ -155,7 +155,7 @@ fn file_state_to_proto(file_state: &FileState) -> crate::protos::working_copy::F
|
|||
}
|
||||
FileType::GitSubmodule => crate::protos::working_copy::FileType::GitSubmodule,
|
||||
};
|
||||
proto.file_type = EnumOrUnknown::new(file_type);
|
||||
proto.file_type = file_type as i32;
|
||||
proto.mtime_millis_since_epoch = file_state.mtime.0;
|
||||
proto.size = file_state.size;
|
||||
proto
|
||||
|
@ -167,7 +167,7 @@ fn file_states_from_proto(
|
|||
let mut file_states = BTreeMap::new();
|
||||
for (path_str, proto_file_state) in &proto.file_states {
|
||||
let path = RepoPath::from_internal_string(path_str.as_str());
|
||||
file_states.insert(path, file_state_from_proto(proto_file_state));
|
||||
file_states.insert(path, file_state_from_proto(proto_file_state.clone()));
|
||||
}
|
||||
file_states
|
||||
}
|
||||
|
@ -401,32 +401,38 @@ impl TreeState {
|
|||
|
||||
fn read(&mut self, mut file: File) {
|
||||
self.update_own_mtime();
|
||||
let proto: crate::protos::working_copy::TreeState =
|
||||
Message::parse_from_reader(&mut file).unwrap();
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf).unwrap();
|
||||
let proto = crate::protos::working_copy::TreeState::decode(&*buf).unwrap();
|
||||
self.tree_id = TreeId::new(proto.tree_id.clone());
|
||||
self.file_states = file_states_from_proto(&proto);
|
||||
self.sparse_patterns = sparse_patterns_from_proto(&proto);
|
||||
}
|
||||
|
||||
fn save(&mut self) {
|
||||
let mut proto = crate::protos::working_copy::TreeState::new();
|
||||
proto.tree_id = self.tree_id.to_bytes();
|
||||
let mut proto = crate::protos::working_copy::TreeState {
|
||||
tree_id: self.tree_id.to_bytes(),
|
||||
..Default::default()
|
||||
};
|
||||
for (file, file_state) in &self.file_states {
|
||||
proto.file_states.insert(
|
||||
file.to_internal_file_string(),
|
||||
file_state_to_proto(file_state),
|
||||
);
|
||||
}
|
||||
let mut sparse_patterns = crate::protos::working_copy::SparsePatterns::new();
|
||||
let mut sparse_patterns = crate::protos::working_copy::SparsePatterns::default();
|
||||
for path in &self.sparse_patterns {
|
||||
sparse_patterns
|
||||
.prefixes
|
||||
.push(path.to_internal_file_string());
|
||||
}
|
||||
proto.sparse_patterns = MessageField::some(sparse_patterns);
|
||||
proto.sparse_patterns = Some(sparse_patterns);
|
||||
|
||||
let mut temp_file = NamedTempFile::new_in(&self.state_path).unwrap();
|
||||
proto.write_to_writer(temp_file.as_file_mut()).unwrap();
|
||||
temp_file
|
||||
.as_file_mut()
|
||||
.write_all(&proto.encode_to_vec())
|
||||
.unwrap();
|
||||
// update own write time while we before we rename it, so we know
|
||||
// there is no unknown data in it
|
||||
self.update_own_mtime();
|
||||
|
@ -1012,15 +1018,17 @@ impl WorkingCopy {
|
|||
operation_id: OperationId,
|
||||
workspace_id: WorkspaceId,
|
||||
) -> WorkingCopy {
|
||||
let mut proto = crate::protos::working_copy::Checkout::new();
|
||||
proto.operation_id = operation_id.to_bytes();
|
||||
proto.workspace_id = workspace_id.as_str().to_string();
|
||||
let proto = crate::protos::working_copy::Checkout {
|
||||
operation_id: operation_id.to_bytes(),
|
||||
workspace_id: workspace_id.as_str().to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
let mut file = OpenOptions::new()
|
||||
.create_new(true)
|
||||
.write(true)
|
||||
.open(state_path.join("checkout"))
|
||||
.unwrap();
|
||||
proto.write_to_writer(&mut file).unwrap();
|
||||
file.write_all(&proto.encode_to_vec()).unwrap();
|
||||
WorkingCopy {
|
||||
store,
|
||||
working_copy_path,
|
||||
|
@ -1050,7 +1058,10 @@ impl WorkingCopy {
|
|||
|
||||
fn write_proto(&self, proto: crate::protos::working_copy::Checkout) {
|
||||
let mut temp_file = NamedTempFile::new_in(&self.state_path).unwrap();
|
||||
proto.write_to_writer(temp_file.as_file_mut()).unwrap();
|
||||
temp_file
|
||||
.as_file_mut()
|
||||
.write_all(&proto.encode_to_vec())
|
||||
.unwrap();
|
||||
// TODO: Retry if persisting fails (it will on Windows if the file happened to
|
||||
// be open for read).
|
||||
temp_file.persist(self.state_path.join("checkout")).unwrap();
|
||||
|
@ -1058,9 +1069,8 @@ impl WorkingCopy {
|
|||
|
||||
fn checkout_state(&self) -> &CheckoutState {
|
||||
self.checkout_state.get_or_init(|| {
|
||||
let mut file = File::open(self.state_path.join("checkout")).unwrap();
|
||||
let proto: crate::protos::working_copy::Checkout =
|
||||
Message::parse_from_reader(&mut file).unwrap();
|
||||
let buf = fs::read(self.state_path.join("checkout")).unwrap();
|
||||
let proto = crate::protos::working_copy::Checkout::decode(&*buf).unwrap();
|
||||
CheckoutState {
|
||||
operation_id: OperationId::new(proto.operation_id),
|
||||
workspace_id: if proto.workspace_id.is_empty() {
|
||||
|
@ -1115,10 +1125,11 @@ impl WorkingCopy {
|
|||
}
|
||||
|
||||
fn save(&mut self) {
|
||||
let mut proto = crate::protos::working_copy::Checkout::new();
|
||||
proto.operation_id = self.operation_id().to_bytes();
|
||||
proto.workspace_id = self.workspace_id().as_str().to_string();
|
||||
self.write_proto(proto);
|
||||
self.write_proto(crate::protos::working_copy::Checkout {
|
||||
operation_id: self.operation_id().to_bytes(),
|
||||
workspace_id: self.workspace_id().as_str().to_string(),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
pub fn start_mutation(&mut self) -> LockedWorkingCopy {
|
||||
|
|
Loading…
Reference in a new issue