pub struct GitFileTools {
pub manager: Arc<VaultManager>,
pub vault_path: PathBuf,
pub commit_locks: Arc<CommitLocks>,
pub commit_hook: Option<CommitHook>,
pub flush_on_collision: Option<CasCollisionFlush>,
pub include_ignored: bool,
pub cached_repo: Option<CachedRepo>,
}Expand description
Write-side tools backed by the git substrate.
Holds the vault path + a shared CommitLocks registry rather than an
Arc<VaultRepo> — VaultRepo wraps a git2::Repository which is !Sync
(raw pointer), so it cannot live inside an async fn future that needs
to be Send. The substrate handle is opened fresh inside a
spawn_blocking task per call (open is ~µs); the shared CommitLocks
keeps cross-call commit-section serialization intact.
Fields§
§manager: Arc<VaultManager>§vault_path: PathBuf§commit_locks: Arc<CommitLocks>§commit_hook: Option<CommitHook>Optional post-commit hook installed on every VaultRepo opened
inside apply_txn. Plumbed for GWS.14 lazy GSU: the MCP server
passes a closure that pushes the new commit onto a per-vault
ReindexQueue. None = no reindex wiring (acceptable for tests
that don’t care about derived state).
flush_on_collision: Option<CasCollisionFlush>Optional flush callback fired BEFORE returning a ConcurrencyError
(GWS.14b). Drains the reindex queue so the agent’s re-read sees
coherent derived state. None = skip flush; callers see the raw
concurrency error and the graph stays as stale as the last
flush-on-query did.
include_ignored: boolturbovault-lri: when false, every mutation pre-checks each
touched path against the worktree’s .gitignore matcher and
refuses the changeset if any path would be ignored. Default
true preserves pre-lri “always-write” behavior. Wired from
VaultGitConfig::include_ignored by the MCP server.
cached_repo: Option<CachedRepo>turbovault-a0l (PERF-1): optional cached per-vault VaultRepo handle.
When Some, apply_txn reuses it instead of opening a fresh repo per
call (saving the ~140µs Repository::open). The MCP server installs one
shared across all in-process writes to the vault. Bare Self::new*
leaves it None, falling back to per-call open (tests / migrations that
don’t run the server-side cache).
Implementations§
Source§impl GitFileTools
impl GitFileTools
Sourcepub fn new(
manager: Arc<VaultManager>,
vault_path: PathBuf,
commit_locks: Arc<CommitLocks>,
) -> Self
pub fn new( manager: Arc<VaultManager>, vault_path: PathBuf, commit_locks: Arc<CommitLocks>, ) -> Self
Construct without a reindex hook (graph + search stay stale until
another path triggers their rebuild). Tests use this; the MCP
server uses Self::new_with_hook.
Sourcepub fn new_with_hook(
manager: Arc<VaultManager>,
vault_path: PathBuf,
commit_locks: Arc<CommitLocks>,
commit_hook: CommitHook,
) -> Self
pub fn new_with_hook( manager: Arc<VaultManager>, vault_path: PathBuf, commit_locks: Arc<CommitLocks>, commit_hook: CommitHook, ) -> Self
Construct with a reindex hook fired post-commit. The MCP server
installs one that pushes onto a per-vault crate::ReindexQueue.
Sourcepub fn new_with_hook_and_flush(
manager: Arc<VaultManager>,
vault_path: PathBuf,
commit_locks: Arc<CommitLocks>,
commit_hook: CommitHook,
flush_on_collision: CasCollisionFlush,
) -> Self
pub fn new_with_hook_and_flush( manager: Arc<VaultManager>, vault_path: PathBuf, commit_locks: Arc<CommitLocks>, commit_hook: CommitHook, flush_on_collision: CasCollisionFlush, ) -> Self
Construct with both a reindex hook AND a CAS-collision flush callback
(GWS.14b). The flush callback runs BEFORE the ConcurrencyError is
returned to the caller, so the agent’s re-read sees coherent derived
state.
Sourcepub fn with_include_ignored(self, include_ignored: bool) -> Self
pub fn with_include_ignored(self, include_ignored: bool) -> Self
turbovault-lri: builder-style override for include_ignored.
false makes every subsequent mutation pre-check each touched
path against the worktree’s .gitignore matcher and refuse the
changeset if any path would be ignored. Default true.
Sourcepub fn with_cached_repo(self, cached_repo: CachedRepo) -> Self
pub fn with_cached_repo(self, cached_repo: CachedRepo) -> Self
turbovault-a0l (PERF-1): install a cached per-vault VaultRepo handle so
writes reuse it instead of opening a fresh repo per call. The handle must
already carry the shared CommitLocks + reindex CommitHook (the MCP
server opens it that way via get_or_init_git_repo). When set, apply_txn
ignores commit_locks/commit_hook on self — the cached handle owns
both.
Sourcepub async fn read_file(&self, path: &str) -> Result<String>
pub async fn read_file(&self, path: &str) -> Result<String>
Read a file from the vault (working tree == HEAD, so this is the committed bytes).
Sourcepub async fn get_notes_info(&self, paths: &[String]) -> Result<Vec<NoteInfo>>
pub async fn get_notes_info(&self, paths: &[String]) -> Result<Vec<NoteInfo>>
Lightweight metadata for multiple files — same shape as
crate::FileTools::get_notes_info.
Sourcepub async fn write_file_with_mode(
&self,
path: &str,
content: &str,
mode: WriteMode,
expected_hash: Option<&str>,
) -> Result<()>
pub async fn write_file_with_mode( &self, path: &str, content: &str, mode: WriteMode, expected_hash: Option<&str>, ) -> Result<()>
Write a file — overwrite by default, append/prepend for the other
modes (mirrors crate::FileTools::write_file_with_mode).
expected_hash, when present, must be a git blob oid hex string
(40 hex chars). The substrate’s version token is the blob oid (not a
SHA-256 content hash). A non-Oid string is rejected loudly rather than
silently dropping CAS protection.
Sourcepub async fn write_file_with_mode_and_message(
&self,
path: &str,
content: &str,
mode: WriteMode,
expected_hash: Option<&str>,
message: &str,
) -> Result<()>
pub async fn write_file_with_mode_and_message( &self, path: &str, content: &str, mode: WriteMode, expected_hash: Option<&str>, message: &str, ) -> Result<()>
turbovault-0bh: caller-supplied commit message variant of
Self::write_file_with_mode. Substrate auto-derives the message
otherwise (write_file <path>); this override lets the MCP layer
pass a richer message (caller’s text + verb=tool_name per TV-008).
Sourcepub async fn write_file(&self, path: &str, content: &str) -> Result<()>
pub async fn write_file(&self, path: &str, content: &str) -> Result<()>
Overwrite shortcut — equivalent to write_file_with_mode(.., Overwrite, None).
Sourcepub async fn create_file(&self, path: &str, content: &str) -> Result<()>
pub async fn create_file(&self, path: &str, content: &str) -> Result<()>
Strict create: write a NEW file with an expect_absent precondition.
If the path becomes occupied between the caller’s check and the
substrate’s CAS, apply_txn returns ConcurrencyError — the create
race the MCP layer’s pre-check cannot close on its own.
This is the substrate-side guarantee for turbovault-947 / write-note CAS-by-default: even with parallel subagents racing to create the same absent path, exactly one commit lands; the loser sees a loud ConcurrencyError and re-decides.
Sourcepub async fn create_file_with_message(
&self,
path: &str,
content: &str,
message: &str,
) -> Result<()>
pub async fn create_file_with_message( &self, path: &str, content: &str, message: &str, ) -> Result<()>
turbovault-0bh: caller-supplied commit message variant of
Self::create_file. The message becomes the commit subject (and
body, when newline-separated). All other semantics are identical.
Sourcepub async fn edit_file(
&self,
path: &str,
edits: &str,
expected_hash: Option<&str>,
dry_run: bool,
) -> Result<EditResult>
pub async fn edit_file( &self, path: &str, edits: &str, expected_hash: Option<&str>, dry_run: bool, ) -> Result<EditResult>
Edit a file via SEARCH/REPLACE blocks. Reads working-tree bytes,
applies the blocks in memory, and commits the result as one
changeset. dry_run = true returns the preview without committing.
Sourcepub async fn edit_file_with_message(
&self,
path: &str,
edits: &str,
expected_hash: Option<&str>,
dry_run: bool,
message: &str,
) -> Result<EditResult>
pub async fn edit_file_with_message( &self, path: &str, edits: &str, expected_hash: Option<&str>, dry_run: bool, message: &str, ) -> Result<EditResult>
turbovault-0bh: caller-supplied commit message variant of
Self::edit_file. Behaviorally identical except the commit
subject is the caller’s message instead of the auto-derived
edit_file <path>.
Sourcepub async fn delete_file(&self, path: &str) -> Result<()>
pub async fn delete_file(&self, path: &str) -> Result<()>
Delete a file. expected_hash (blob oid hex) enforces a CAS
precondition — pass None for a blind delete.
Sourcepub async fn delete_file_with_hash(
&self,
path: &str,
expected_hash: Option<&str>,
) -> Result<()>
pub async fn delete_file_with_hash( &self, path: &str, expected_hash: Option<&str>, ) -> Result<()>
Delete with optional blob-oid CAS.
Sourcepub async fn delete_file_with_hash_and_message(
&self,
path: &str,
expected_hash: Option<&str>,
message: &str,
) -> Result<()>
pub async fn delete_file_with_hash_and_message( &self, path: &str, expected_hash: Option<&str>, message: &str, ) -> Result<()>
turbovault-0bh: caller-supplied commit message variant of
Self::delete_file_with_hash.
Sourcepub async fn move_file(&self, from: &str, to: &str) -> Result<()>
pub async fn move_file(&self, from: &str, to: &str) -> Result<()>
Move a file — remove(from) + upsert(to, bytes) in one commit.
Sourcepub async fn move_file_with_hash(
&self,
from: &str,
to: &str,
expected_hash: Option<&str>,
) -> Result<()>
pub async fn move_file_with_hash( &self, from: &str, to: &str, expected_hash: Option<&str>, ) -> Result<()>
Move with optional blob-oid CAS on the source path.
Sourcepub async fn move_file_with_hash_and_message(
&self,
from: &str,
to: &str,
expected_hash: Option<&str>,
message: &str,
) -> Result<()>
pub async fn move_file_with_hash_and_message( &self, from: &str, to: &str, expected_hash: Option<&str>, message: &str, ) -> Result<()>
turbovault-0bh: caller-supplied commit message variant of
Self::move_file_with_hash.
Sourcepub async fn delete_file_with_link_rewrite_to_stale(
&self,
path: &str,
expected_hash: Option<&str>,
message: &str,
) -> Result<MoveWithLinksResult>
pub async fn delete_file_with_link_rewrite_to_stale( &self, path: &str, expected_hash: Option<&str>, message: &str, ) -> Result<MoveWithLinksResult>
turbovault-oz6: atomic delete + inbound-wikilink wrap-as-stale.
Removes path AND rewrites every backlinking source’s wikilinks
targeting it as ~~[[old]]~~ strikethrough (signaling a dead
reference) — all in one substrate changeset.
Each source carries an expect_blob precondition; a concurrent
edit to ANY source aborts the whole delete with
ConcurrencyError. expected_hash (optional, blob OID hex)
guards the target page itself.
Returns the list of source paths whose content was rewritten so the caller can surface what changed.
Sourcepub async fn list_inbound_backlinks(&self, path: &str) -> Result<Vec<String>>
pub async fn list_inbound_backlinks(&self, path: &str) -> Result<Vec<String>>
turbovault-oz6: return the list of vault-relative source paths
that have inbound wikilinks targeting path. Used by the MCP
layer’s “refuse-if-backlinks” pre-check (option A) before
committing to a delete.
Sourcepub async fn move_file_with_link_updates(
&self,
from: &str,
to: &str,
expected_hash: Option<&str>,
message: &str,
) -> Result<MoveWithLinksResult>
pub async fn move_file_with_link_updates( &self, from: &str, to: &str, expected_hash: Option<&str>, message: &str, ) -> Result<MoveWithLinksResult>
turbovault-lqr: atomic move + inbound-wikilink rewrite. Renames
from -> to AND rewrites every backlinking source’s
[[from-basename]] / [[from-path]] (plus alias / section /
block-anchor / embed forms) to point at the new target, all in
one substrate changeset.
Per-source CAS: each rewritten source carries an expect_blob
precondition. If ANY source’s blob OID changed between the
read-modify and the substrate apply, the WHOLE changeset
aborts (architecture §6.3 reconsideration domino). The
destination always carries expect_absent (no clobber).
expected_hash (optional, blob OID hex) protects the SOURCE
against a concurrent edit between the caller’s read and this
call.
Returns the list of source paths whose content was rewritten so the caller can surface what changed.
Sourcepub async fn copy_file(&self, from: &str, to: &str) -> Result<()>
pub async fn copy_file(&self, from: &str, to: &str) -> Result<()>
Copy a file — read source, commit target (no source change). One
commit. Same expect-absent guard on the destination as move_file.
Sourcepub async fn batch_execute(
&self,
operations: Vec<BatchOperation>,
) -> Result<BatchResult>
pub async fn batch_execute( &self, operations: Vec<BatchOperation>, ) -> Result<BatchResult>
Translate every BatchOperation to a single Changeset and
commit as one atomic commit — either every op lands or none do.
This is the spec-promised behavior the legacy crate::BatchTools never
actually delivered (the legacy path stopped at failed_at and left
partial state on disk).
Sourcepub async fn batch_execute_with_message(
&self,
operations: Vec<BatchOperation>,
message: &str,
) -> Result<BatchResult>
pub async fn batch_execute_with_message( &self, operations: Vec<BatchOperation>, message: &str, ) -> Result<BatchResult>
turbovault-0bh: caller-supplied commit message variant of
Self::batch_execute. Overrides the auto-derived
batch_execute (N ops) subject with the caller’s message.
Trait Implementations§
Source§impl Clone for GitFileTools
impl Clone for GitFileTools
Source§fn clone(&self) -> GitFileTools
fn clone(&self) -> GitFileTools
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl !RefUnwindSafe for GitFileTools
impl !UnwindSafe for GitFileTools
impl Freeze for GitFileTools
impl Send for GitFileTools
impl Sync for GitFileTools
impl Unpin for GitFileTools
impl UnsafeUnpin for GitFileTools
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<T> Fruit for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more