{"record":{"id":"726682314199c2c2","repo":"GitoxideLabs/gitoxide","slug":"bug-cannot-call-commit-before-prepare","errorCode":null,"errorMessage":"BUG: cannot call commit() before prepare(…)","messagePattern":"BUG: cannot call commit\\(\\) before prepare\\(…\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-ref/src/store/packed/transaction.rs","lineNumber":158,"sourceCode":"                .take()\n                .map(gix_lock::File::close)\n                .transpose()\n                .map_err(prepare::Error::CloseLock)?;\n        } else {\n            // NOTE that we don't do any additional checks here but apply all edits unconditionally.\n            // This is because this transaction system is internal and will be used correctly from the\n            // loose ref store transactions, which do the necessary checking.\n        }\n        self.edits = Some(edits);\n        Ok(self)\n    }\n\n    /// Commit the prepared transaction.\n    ///\n    /// Please note that actual edits invalidated existing packed buffers.\n    /// Note: There is the potential to write changes into memory and return such a packed-refs buffer for reuse.\n    pub fn commit(self) -> Result<(), commit::Error> {\n        let mut edits = self.edits.expect(\"BUG: cannot call commit() before prepare(…)\");\n        if edits.is_empty() {\n            return Ok(());\n        }\n\n        let mut file = self.lock.expect(\"a write lock for applying changes\");\n        let refs_sorted: Box<dyn Iterator<Item = Result<packed::Reference<'_>, packed::iter::Error>>> =\n            match self.buffer.as_ref() {\n                Some(buffer) => Box::new(buffer.iter()?),\n                None => Box::new(std::iter::empty()),\n            };\n\n        let mut refs_sorted = refs_sorted.peekable();\n\n        edits.sort_by(|l, r| l.inner.name.as_bstr().cmp(r.inner.name.as_bstr()));\n        let mut peekable_sorted_edits = edits.iter().peekable();\n\n        file.with_mut(|f| f.write_all(HEADER_LINE))?;\n","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-ref/src/store/packed/transaction.rs#L140-L176","documentation":"Panic raised when `packed::Transaction::commit()` is called without a preceding `prepare(...)`. `self.edits` is an `Option` populated only by `prepare`, so `expect` proves the caller skipped the required phase. This is a deliberate API-misuse guard, analogous to the loose-ref transaction guard.","triggerScenarios":"Creating a `gix_ref::store::packed::Transaction` via `Store::packed_transaction()` or `File::transaction()`, then calling `.commit()` directly instead of `.prepare(...)` first.","commonSituations":"Code that conditionally prepares but unconditionally commits; refactors moving prepare into another function; misunderstandings of the two-phase packed-refs API.","solutions":["Call `transaction.prepare(...)` (with a lock context) before `transaction.commit()`.","Ensure the prepare step's Result is checked — a failed prepare must abort, not fall through to commit.","Restructure so prepare and commit are always paired in the same code path."],"exampleFix":"// before\nlet tx = store.packed_transaction(lock, gix_ref::transaction::PackedRefs::DeletionsAndNonSymbolicUpdates)?;\ntx.commit()?;\n// after\nlet mut tx = store.packed_transaction(lock, gix_ref::transaction::PackedRefs::DeletionsAndNonSymbolicUpdates)?;\ntx.prepare(edits, &objects, forward_or_reverse)?;\ntx.commit()?;","handlingStrategy":"type-guard","validationCode":"// always prepare a packed transaction before committing\nlet mut tx = store.packed_transaction(lock, packed_refs_opt)?;\ntx.prepare(edits, &objects, /*move_to_top*/ true)?;\ntx.commit()?;","typeGuard":"fn commit_packed(mut tx: gix_ref::transaction::PackedTransaction, edits: Vec<RefEdit>, objects: &dyn gix_object::Find) -> Result<(), gix_ref::store::packed::transaction::commit::Error> {\n    tx.prepare(edits, objects, true)?;\n    tx.commit()\n}","tryCatchPattern":"// panics are not Result errors; enforce phase order by construction\nlet mut tx = ...;\nif !tx.is_prepared() { tx.prepare(edits, &objects, true)?; }\ntx.commit()?;","preventionTips":["Never call commit() on a freshly created packed transaction.","Abort the whole flow if prepare() returns Err.","Centralize packed-ref edits in one helper that pairs prepare/commit."],"tags":["panic","transaction","packed-refs","api-misuse"],"backgroundTag":"invalid-state-transition","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}