Skip to main content

GitFileTools

Struct GitFileTools 

Source
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: bool

turbovault-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

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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).

Source

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.

Source

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.

Source

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).

Source

pub async fn write_file(&self, path: &str, content: &str) -> Result<()>

Overwrite shortcut — equivalent to write_file_with_mode(.., Overwrite, None).

Source

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.

Source

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.

Source

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.

Source

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>.

Source

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.

Source

pub async fn delete_file_with_hash( &self, path: &str, expected_hash: Option<&str>, ) -> Result<()>

Delete with optional blob-oid CAS.

Source

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.

Source

pub async fn move_file(&self, from: &str, to: &str) -> Result<()>

Move a file — remove(from) + upsert(to, bytes) in one commit.

Source

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.

Source

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.

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.

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.

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.

Source

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.

Source

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).

Source

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

Source§

fn clone(&self) -> GitFileTools

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts 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>

Converts 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)

Converts &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)

Converts &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
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Sync + Send>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Fruit for T
where T: Send + Downcast,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more