History

fix!: ref namespaces are now thread-local (#263)

Previously these were shared in the shared Repo instance, which makes threaded applications impossible to remain deterministic across multiple connections. Now they are local to the thread, which allowed some methods to remove their Result<> as they cannot fail anymore, the reason for this being a breaking change.

Sebastian Thiel committed 4 years ago

Commit fc8e85c1 parent 38c814611 files · +58 −66Browse files

Unified · Split

11 files
  1. git-repository/src/easy/ext/reference.rs +14 −20
  2. git-repository/src/easy/impls.rs +16 −0
  3. git-repository/src/easy/mod.rs +5 −2
  4. git-repository/src/easy/reference/errors.rs +0 −18
  5. git-repository/src/easy/reference/iter.rs +8 −11
  6. git-repository/src/easy/reference/mod.rs +3 −3
  7. git-repository/src/easy/state.rs +3 −6
  8. git-repository/src/lib.rs +4 −1
  9. git-repository/src/path/is.rs +1 −1
  10. git-repository/src/repository.rs +1 −1
  11. git-repository/tests/easy/ext/reference.rs +3 −3
git-repository/src/easy/ext/reference.rs+14−20
@@ -1,7 +1,4 @@
1use std::{1use std::convert::TryInto;
2 convert::TryInto,
3 ops::{Deref, DerefMut},
4};
5 2
6use git_actor as actor;3use git_actor as actor;
7use git_hash::ObjectId;4use git_hash::ObjectId;
@@ -60,14 +57,14 @@ pub trait ReferenceAccessExt: easy::Access + Sized { · 52 unmodified lines
60 57
61 /// Returns the currently set namespace for references, or `None` if it is not set.58 /// Returns the currently set namespace for references, or `None` if it is not set.
62 ///59 ///
63 /// Namespaces allow to partition references.60 /// Namespaces allow to partition references, and is configured per `Easy`.
64 fn namespace(&self) -> Result<Option<git_ref::Namespace>, easy::borrow::repo::Error> {61 fn namespace(&self) -> Option<&git_ref::Namespace> {
65 self.repo().map(|repo| repo.deref().refs.namespace.clone())62 self.state().refs.namespace.as_ref()
66 }63 }
67 64
68 /// Remove the currently set reference namespace and return it.65 /// Remove the currently set reference namespace and return it, affecting only this `Easy`.
69 fn clear_namespace(&mut self) -> Result<Option<git_ref::Namespace>, easy::borrow::repo::Error> {66 fn clear_namespace(&mut self) -> Option<git_ref::Namespace> {
70 self.repo_mut().map(|mut repo| repo.deref_mut().refs.namespace.take())67 self.state_mut().refs.namespace.take()
71 }68 }
72 69
73 /// Set the reference namespace to the given value, like `"foo"` or `"foo/bar"`.70 /// Set the reference namespace to the given value, like `"foo"` or `"foo/bar"`.
@@ -76,13 +73,13 @@ pub trait ReferenceAccessExt: easy::Access + Sized { · 2 unmodified lines
76 fn set_namespace<'a, Name, E>(73 fn set_namespace<'a, Name, E>(
77 &mut self,74 &mut self,
78 namespace: Name,75 namespace: Name,
79 ) -> Result<Option<git_ref::Namespace>, easy::reference::namespace::set::Error>76 ) -> Result<Option<git_ref::Namespace>, git_validate::refname::Error>
80 where77 where
81 Name: TryInto<PartialNameRef<'a>, Error = E>,78 Name: TryInto<PartialNameRef<'a>, Error = E>,
82 git_validate::refname::Error: From<E>,79 git_validate::refname::Error: From<E>,
83 {80 {
84 let namespace = git_ref::namespace::expand(namespace)?;81 let namespace = git_ref::namespace::expand(namespace)?;
85 Ok(self.repo_mut()?.deref_mut().refs.namespace.replace(namespace))82 Ok(self.state_mut().refs.namespace.replace(namespace))
86 }83 }
87 84
88 // TODO: more tests or usage85 // TODO: more tests or usage
@@ -166,8 +163,8 @@ pub trait ReferenceAccessExt: easy::Access + Sized { · 77 unmodified lines
166 &committer_storage163 &committer_storage
167 }164 }
168 };165 };
169 let repo = self.repo()?;166 self.state()
170 repo.refs167 .refs
171 .transaction()168 .transaction()
172 .prepare(edits, lock_mode)?169 .prepare(edits, lock_mode)?
173 .commit(committer)170 .commit(committer)
@@ -213,10 +210,8 @@ pub trait ReferenceAccessExt: easy::Access + Sized { · 39 unmodified lines
213 /// references.210 /// references.
214 fn references(&self) -> Result<easy::reference::iter::Platform<'_, Self>, easy::reference::iter::Error> {211 fn references(&self) -> Result<easy::reference::iter::Platform<'_, Self>, easy::reference::iter::Error> {
215 let state = self.state();212 let state = self.state();
216 let repo = self.repo()?;213 let packed_refs = state.assure_packed_refs_uptodate()?;
217 let packed_refs = state.assure_packed_refs_uptodate(&repo.refs)?;
218 Ok(easy::reference::iter::Platform {214 Ok(easy::reference::iter::Platform {
219 repo,
220 packed_refs,215 packed_refs,
221 access: self,216 access: self,
222 })217 })
@@ -232,10 +227,9 @@ pub trait ReferenceAccessExt: easy::Access + Sized { · 9 unmodified lines
232 git_ref::file::find::Error: From<E>,227 git_ref::file::find::Error: From<E>,
233 {228 {
234 let state = self.state();229 let state = self.state();
235 let repo = self.repo()?;230 match state
236 match repo
237 .refs231 .refs
238 .try_find(name, state.assure_packed_refs_uptodate(&repo.refs)?.buffer.as_ref())232 .try_find(name, state.assure_packed_refs_uptodate()?.buffer.as_ref())
239 {233 {
240 Ok(r) => match r {234 Ok(r) => match r {
241 Some(r) => Ok(Some(Reference::from_ref(r, self))),235 Some(r) => Ok(Some(Reference::from_ref(r, self))),
git-repository/src/easy/impls.rs+16−0
@@ -103,6 +103,10 @@ impl<'repo> easy::Access for EasyShared<'repo> { · 102 unmodified lines
103 fn state(&self) -> &easy::State {103 fn state(&self) -> &easy::State {
104 &self.state104 &self.state
105 }105 }
106
107 fn state_mut(&mut self) -> &mut easy::State {
108 &mut self.state
109 }
106}110}
107 111
108impl easy::Access for Easy {112impl easy::Access for Easy {
@@ -121,6 +125,10 @@ impl easy::Access for Easy { · 12 unmodified lines
121 fn state(&self) -> &easy::State {125 fn state(&self) -> &easy::State {
122 &self.state126 &self.state
123 }127 }
128
129 fn state_mut(&mut self) -> &mut easy::State {
130 &mut self.state
131 }
124}132}
125 133
126impl easy::Access for EasyArc {134impl easy::Access for EasyArc {
@@ -136,6 +144,10 @@ impl easy::Access for EasyArc { · 9 unmodified lines
136 fn state(&self) -> &easy::State {144 fn state(&self) -> &easy::State {
137 &self.state145 &self.state
138 }146 }
147
148 fn state_mut(&mut self) -> &mut easy::State {
149 &mut self.state
150 }
139}151}
140 152
141impl easy::Access for EasyArcExclusive {153impl easy::Access for EasyArcExclusive {
@@ -151,4 +163,8 @@ impl easy::Access for EasyArcExclusive { · 9 unmodified lines
151 fn state(&self) -> &easy::State {163 fn state(&self) -> &easy::State {
152 &self.state164 &self.state
153 }165 }
166
167 fn state_mut(&mut self) -> &mut easy::State {
168 &mut self.state
169 }
154}170}
git-repository/src/easy/mod.rs+5−2
@@ -114,7 +114,7 @@ pub struct State { · 113 unmodified lines
114 /// As the packed-buffer may hold onto a memory map, so ideally this State is freed after use instead of keeping it around114 /// As the packed-buffer may hold onto a memory map, so ideally this State is freed after use instead of keeping it around
115 /// for too long. At least `packed_refs` is lazily initialized.115 /// for too long. At least `packed_refs` is lazily initialized.
116 packed_refs: RefCell<reference::packed::ModifieablePackedRefsBuffer>,116 packed_refs: RefCell<reference::packed::ModifieablePackedRefsBuffer>,
117 refs: git_ref::file::Store,117 refs: crate::RefStore,
118 pack_cache: RefCell<PackCache>,118 pack_cache: RefCell<PackCache>,
119 object_cache: RefCell<Option<object::cache::MemoryCappedHashmap>>,119 object_cache: RefCell<Option<object::cache::MemoryCappedHashmap>>,
120 buf: RefCell<Vec<u8>>,120 buf: RefCell<Vec<u8>>,
@@ -148,6 +148,9 @@ pub trait Access { · 27 unmodified lines
148 /// relevant for long-running applications that make changes to the repository.148 /// relevant for long-running applications that make changes to the repository.
149 fn repo_mut(&self) -> borrow::repo::Result<Self::RepoRefMut>;149 fn repo_mut(&self) -> borrow::repo::Result<Self::RepoRefMut>;
150 150
151 /// Return a shared borrow of the repository state, with support for interior mutability.151 /// Return a shared borrow of the repository state.
152 fn state(&self) -> &State;152 fn state(&self) -> &State;
153
154 /// Return a shared borrow of the repository state.
155 fn state_mut(&mut self) -> &mut State;
153}156}
git-repository/src/easy/reference/errors.rs+0−18
@@ -1,21 +1,3 @@
1///
2pub mod namespace {
3 ///
4 pub mod set {
5 use crate::easy;
6
7 /// The error returned by [ReferenceAccessExt::set_namespace(…)][easy::ext::ReferenceAccessExt::set_namespace()].
8 #[derive(Debug, thiserror::Error)]
9 #[allow(missing_docs)]
10 pub enum Error {
11 #[error(transparent)]
12 BorrowRepoMut(#[from] easy::borrow::repo::Error),
13 #[error(transparent)]
14 NameValidation(#[from] git_validate::refname::Error),
15 }
16 }
17}
18
19///1///
20pub mod edit {2pub mod edit {
21 use crate::easy;3 use crate::easy;
git-repository/src/easy/reference/iter.rs+8−11
@@ -1,9 +1,5 @@
1//!1//!
2use std::{2use std::{cell::Ref, ops::DerefMut, path::Path};
3 cell::Ref,
4 ops::{Deref, DerefMut},
5 path::Path,
6};
7 3
8use git_odb::Find;4use git_odb::Find;
9use git_ref::file::ReferenceExt;5use git_ref::file::ReferenceExt;
@@ -16,7 +12,6 @@ pub struct Platform<'r, A> · 6 unmodified lines
16where12where
17 A: easy::Access + Sized,13 A: easy::Access + Sized,
18{14{
19 pub(crate) repo: A::RepoRef,
20 pub(crate) packed_refs: Ref<'r, easy::reference::packed::ModifieablePackedRefsBuffer>,15 pub(crate) packed_refs: Ref<'r, easy::reference::packed::ModifieablePackedRefsBuffer>,
21 pub(crate) access: &'r A,16 pub(crate) access: &'r A,
22}17}
@@ -38,9 +33,8 @@ where · 15 unmodified lines
38 /// Even broken or otherwise unparsible or inaccessible references are returned and have to be handled by the caller on a33 /// Even broken or otherwise unparsible or inaccessible references are returned and have to be handled by the caller on a
39 /// case by case basis.34 /// case by case basis.
40 pub fn all(&self) -> Result<Iter<'_, A>, init::Error> {35 pub fn all(&self) -> Result<Iter<'_, A>, init::Error> {
41 let repo = self.repo.deref();
42 Ok(Iter {36 Ok(Iter {
43 inner: repo.refs.iter(self.packed_refs.buffer.as_ref())?,37 inner: self.access.state().refs.iter(self.packed_refs.buffer.as_ref())?,
44 packed_refs: self.packed_refs.buffer.as_ref(),38 packed_refs: self.packed_refs.buffer.as_ref(),
45 peel: false,39 peel: false,
46 access: self.access,40 access: self.access,
@@ -51,9 +45,12 @@ where · 4 unmodified lines
51 ///45 ///
52 /// These are of the form `refs/heads` or `refs/remotes/origin`, and must not contain relative paths components like `.` or `..`.46 /// These are of the form `refs/heads` or `refs/remotes/origin`, and must not contain relative paths components like `.` or `..`.
53 pub fn prefixed(&self, prefix: impl AsRef<Path>) -> Result<Iter<'_, A>, init::Error> {47 pub fn prefixed(&self, prefix: impl AsRef<Path>) -> Result<Iter<'_, A>, init::Error> {
54 let repo = self.repo.deref();
55 Ok(Iter {48 Ok(Iter {
56 inner: repo.refs.iter_prefixed(self.packed_refs.buffer.as_ref(), prefix)?,49 inner: self
50 .access
51 .state()
52 .refs
53 .iter_prefixed(self.packed_refs.buffer.as_ref(), prefix)?,
57 packed_refs: self.packed_refs.buffer.as_ref(),54 packed_refs: self.packed_refs.buffer.as_ref(),
58 peel: false,55 peel: false,
59 access: self.access,56 access: self.access,
@@ -90,7 +87,7 @@ where · 30 unmodified lines
90 let repo = self.access.repo()?;87 let repo = self.access.repo()?;
91 let state = self.access.state();88 let state = self.access.state();
92 let mut pack_cache = state.try_borrow_mut_pack_cache()?;89 let mut pack_cache = state.try_borrow_mut_pack_cache()?;
93 r.peel_to_id_in_place(&repo.refs, self.packed_refs, |oid, buf| {90 r.peel_to_id_in_place(&state.refs, self.packed_refs, |oid, buf| {
94 repo.odb91 repo.odb
95 .try_find(oid, buf, pack_cache.deref_mut())92 .try_find(oid, buf, pack_cache.deref_mut())
96 .map(|po| po.map(|o| (o.kind, o.data)))93 .map(|po| po.map(|o| (o.kind, o.data)))
git-repository/src/easy/reference/mod.rs+3−3
@@ -14,7 +14,7 @@ pub mod iter; · 13 unmodified lines
14mod errors;14mod errors;
15use std::{borrow::Borrow, cell::RefMut, marker::PhantomData};15use std::{borrow::Borrow, cell::RefMut, marker::PhantomData};
16 16
17pub use errors::{edit, find, namespace, peel};17pub use errors::{edit, find, peel};
18 18
19use crate::ext::ObjectIdExt;19use crate::ext::ObjectIdExt;
20 20
@@ -78,8 +78,8 @@ where · 57 unmodified lines
78 let state = self.access.state();78 let state = self.access.state();
79 let mut pack_cache = state.try_borrow_mut_pack_cache()?;79 let mut pack_cache = state.try_borrow_mut_pack_cache()?;
80 let oid = self.inner.peel_to_id_in_place(80 let oid = self.inner.peel_to_id_in_place(
81 &repo.refs,81 &state.refs,
82 state.assure_packed_refs_uptodate(&repo.refs)?.buffer.as_ref(),82 state.assure_packed_refs_uptodate()?.buffer.as_ref(),
83 |oid, buf| {83 |oid, buf| {
84 repo.odb84 repo.odb
85 .try_find(oid, buf, pack_cache.deref_mut())85 .try_find(oid, buf, pack_cache.deref_mut())
git-repository/src/easy/state.rs+3−6
@@ -1,8 +1,6 @@
1//!1//!
2use std::cell::{Ref, RefCell, RefMut};2use std::cell::{Ref, RefCell, RefMut};
3 3
4use git_ref::file;
5
6use crate::{easy, easy::borrow, Repository};4use crate::{easy, easy::borrow, Repository};
7 5
8impl Clone for easy::State {6impl Clone for easy::State {
@@ -12,8 +10,8 @@ impl Clone for easy::State { · 3 unmodified lines
12}10}
13 11
14// TODO: Expand this as needed as more higher-level stores exist (i.e. odb)12// TODO: Expand this as needed as more higher-level stores exist (i.e. odb)
15impl From<git_ref::file::Store> for easy::State {13impl From<crate::RefStore> for easy::State {
16 fn from(refs: git_ref::file::Store) -> Self {14 fn from(refs: crate::RefStore) -> Self {
17 easy::State {15 easy::State {
18 packed_refs: RefCell::new(Default::default()),16 packed_refs: RefCell::new(Default::default()),
19 #[cfg(not(feature = "max-performance"))]17 #[cfg(not(feature = "max-performance"))]
@@ -36,10 +34,9 @@ impl From<&Repository> for easy::State { · 16 unmodified lines
36impl easy::State {34impl easy::State {
37 pub(crate) fn assure_packed_refs_uptodate(35 pub(crate) fn assure_packed_refs_uptodate(
38 &self,36 &self,
39 file: &file::Store,
40 ) -> Result<Ref<'_, easy::reference::packed::ModifieablePackedRefsBuffer>, easy::reference::packed::Error> {37 ) -> Result<Ref<'_, easy::reference::packed::ModifieablePackedRefsBuffer>, easy::reference::packed::Error> {
41 let mut packed_refs = self.packed_refs.try_borrow_mut()?;38 let mut packed_refs = self.packed_refs.try_borrow_mut()?;
42 packed_refs.assure_packed_refs_uptodate(file)?;39 packed_refs.assure_packed_refs_uptodate(&self.refs)?;
43 drop(packed_refs);40 drop(packed_refs);
44 Ok(self.packed_refs.try_borrow()?)41 Ok(self.packed_refs.try_borrow()?)
45 }42 }
git-repository/src/lib.rs+4−1
@@ -154,6 +154,9 @@ pub mod path; · 153 unmodified lines
154mod repository;154mod repository;
155pub use repository::{discover, init, open};155pub use repository::{discover, init, open};
156 156
157/// The standard type for a store to handle git references.
158pub type RefStore = git_ref::file::Store;
159
157/// A repository path which either points to a work tree or the `.git` repository itself.160/// A repository path which either points to a work tree or the `.git` repository itself.
158#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]161#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
159pub enum Path {162pub enum Path {
@@ -173,7 +176,7 @@ pub enum Path { · 13 unmodified lines
173/// Namely, this is an object database, a reference database to point to objects.176/// Namely, this is an object database, a reference database to point to objects.
174pub struct Repository {177pub struct Repository {
175 /// A store for references to point at objects178 /// A store for references to point at objects
176 pub refs: git_ref::file::Store,179 pub refs: RefStore,
177 /// A store for objects that contain data180 /// A store for objects that contain data
178 #[cfg(feature = "unstable")]181 #[cfg(feature = "unstable")]
179 pub odb: git_odb::linked::Store,182 pub odb: git_odb::linked::Store,
git-repository/src/path/is.rs+1−1
@@ -35,7 +35,7 @@ pub fn git(git_dir: impl AsRef<Path>) -> Result<crate::Kind, Error> { · 34 unmodified lines
35 let dot_git = git_dir.as_ref();35 let dot_git = git_dir.as_ref();
36 36
37 {37 {
38 let refs = git_ref::file::Store::at(&dot_git, Default::default());38 let refs = crate::RefStore::at(&dot_git, Default::default());
39 let head = refs.find_loose("HEAD")?;39 let head = refs.find_loose("HEAD")?;
40 if head.name.as_bstr() != "HEAD" {40 if head.name.as_bstr() != "HEAD" {
41 return Err(Error::MisplacedHead {41 return Err(Error::MisplacedHead {
git-repository/src/repository.rs+1−1
@@ -101,7 +101,7 @@ pub mod open { · 100 unmodified lines
101 101
102 Ok(crate::Repository {102 Ok(crate::Repository {
103 odb: git_odb::linked::Store::at(git_dir.join("objects"))?,103 odb: git_odb::linked::Store::at(git_dir.join("objects"))?,
104 refs: git_ref::file::Store::at(104 refs: crate::RefStore::at(
105 git_dir,105 git_dir,
106 if worktree_dir.is_none() {106 if worktree_dir.is_none() {
107 git_ref::file::WriteReflog::Disable107 git_ref::file::WriteReflog::Disable
git-repository/tests/easy/ext/reference.rs+3−3
@@ -14,7 +14,7 @@ mod set_namespace { · 13 unmodified lines
14 15,14 15,
15 "there are plenty of references in the default namespace"15 "there are plenty of references in the default namespace"
16 );16 );
17 assert!(repo.namespace()?.is_none(), "no namespace is set initially");17 assert!(repo.namespace().is_none(), "no namespace is set initially");
18 assert!(repo.set_namespace("foo")?.is_none(), "there is no previous namespace");18 assert!(repo.set_namespace("foo")?.is_none(), "there is no previous namespace");
19 19
20 assert_eq!(20 assert_eq!(
@@ -67,9 +67,9 @@ mod set_namespace { · 46 unmodified lines
67 "namespaces are transparent"67 "namespaces are transparent"
68 );68 );
69 69
70 let previous_ns = repo.clear_namespace()?.expect("namespace set");70 let previous_ns = repo.clear_namespace().expect("namespace set");
71 assert_eq!(previous_ns.as_bstr(), "refs/namespaces/foo/");71 assert_eq!(previous_ns.as_bstr(), "refs/namespaces/foo/");
72 assert!(repo.clear_namespace()?.is_none(), "it doesn't invent namespaces");72 assert!(repo.clear_namespace().is_none(), "it doesn't invent namespaces");
73 73
74 assert_eq!(74 assert_eq!(
75 repo.references()?.all()?.count(),75 repo.references()?.all()?.count(),