Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
ddf159a
fix(cache): guard CLOCKS_PER_MS against low resolution
flyingrobots Sep 21, 2025
53d7672
fix(cache/tests): clean error handler and guard clock rate
flyingrobots Sep 21, 2025
25841d0
refactor(git): Introduce Git repository port abstraction
flyingrobots Sep 27, 2025
640acf3
refactor(git): Abstract commit tree size calculation to port
flyingrobots Sep 27, 2025
79371f8
refactor(port): Abstract commit walk and branch info
flyingrobots Sep 27, 2025
4a6be16
refactor(journal): Abstract git write operations to port
flyingrobots Sep 27, 2025
063664d
refactor(port): Generalize commit creation and abstract journal writer
flyingrobots Sep 27, 2025
7ef7de2
docs: update for AGENTS.md
flyingrobots Sep 27, 2025
e1d71b5
refactor(port): Improve API clarity and dramatically enhance fake git…
flyingrobots Sep 27, 2025
c8a6659
This diff implements a significant refactoring effort to move Git blo…
flyingrobots Sep 27, 2025
4acd312
refactor(core): Migrate post-commit hook and journal writer to gm_git…
flyingrobots Sep 27, 2025
87b7570
docs: update AGENTS.md
flyingrobots Sep 27, 2025
c5451e5
feat(core): Migrate augment hooks to git_repository_port with commit-…
flyingrobots Sep 27, 2025
8d3ed7c
feat(core): Port merge detection to git_repository_port by adding com…
flyingrobots Sep 27, 2025
68fc826
refactor(core): Retire legacy git_ops and git_repo from gm_context_t
flyingrobots Sep 27, 2025
7e8d026
refactor(benchmark): Update cache query fanout call site to use context
flyingrobots Sep 27, 2025
258c9f0
chore: merge origin/main
flyingrobots Sep 27, 2025
a8cc1bc
chore(review): seed feedback for PR #171 (258c9f05de4f483c8c9a039df4b…
Sep 27, 2025
7195186
chore(ci): remove docs-only auto-labeler
flyingrobots Sep 27, 2025
aad1e87
chore(review): seed feedback for PR #171 (7195186ca1374dcef93a846f882…
Sep 27, 2025
f495f71
fix: Refactor libgit2 calls to use object IDs and iterative tree trav…
flyingrobots Sep 27, 2025
88669fa
chore(review): address coderabbit feedback batch 3
flyingrobots Sep 27, 2025
97f31b1
chore: merge origin/chore/more-fixes
flyingrobots Sep 27, 2025
d0fec83
chore(review): seed feedback for PR #171 (97f31b140f0cd863d8b03b7feb7…
Sep 27, 2025
2b7feb4
chore(docs): remove stale coderabbit worksheet
flyingrobots Sep 27, 2025
0a19c87
Merge branch 'chore/more-fixes' of github.com:neuroglyph/git-mind int…
flyingrobots Sep 27, 2025
240ce22
test(fake-git-port): respect callback stop signal
flyingrobots Sep 27, 2025
5ccf516
chore(review): seed feedback for PR #171 (240ce229257fd93744c5adb130a…
Sep 27, 2025
b3092ae
docs: restore PR171 worksheet
flyingrobots Sep 27, 2025
9a48c5e
docs: mark PR171 worksheet decision
flyingrobots Sep 27, 2025
4b8b177
docs: mark finishing touches decision
flyingrobots Sep 27, 2025
16f0994
docs: finalize PR171 worksheet decisions
flyingrobots Sep 27, 2025
8494a8b
docs: document worksheet decisions
flyingrobots Sep 27, 2025
b45ca6a
docs: normalize worksheet decision formatting
flyingrobots Sep 27, 2025
51a6c1c
chore(review): seed feedback for PR #171 (b45ca6a03649f4076c209237823…
Sep 27, 2025
7a7e6b9
docs: document review worksheet flow
flyingrobots Sep 27, 2025
82138b1
chore(review): seed feedback for PR #171 (7a7e6b9bbf80c9685b105071f04…
Sep 27, 2025
22d3f07
docs: align titles with H1 for lint
flyingrobots Sep 27, 2025
dd6ee0f
chore(review): seed feedback for PR #171 (22d3f07ccf567820f625243996e…
Sep 27, 2025
bbfaafe
docs: exclude legacy folder from title/toc checks
flyingrobots Sep 27, 2025
ac599f8
Merge branch 'chore/more-fixes' of github.com:neuroglyph/git-mind int…
flyingrobots Sep 27, 2025
395c89f
chore(review): seed feedback for PR #171 (ac599f80c9bd31cb029e0ae5c25…
Sep 27, 2025
dcb68c9
fix(core): address post-review safety feedback
flyingrobots Sep 27, 2025
a8cdae3
chore(review): seed feedback for PR #171 (dcb68c9f3b3a68140c1f695f8f0…
Sep 27, 2025
9157271
refactor(cli): drop raw repo handle and use port disposer
flyingrobots Sep 27, 2025
dc817f5
Merge branch 'chore/more-fixes' of github.com:neuroglyph/git-mind int…
flyingrobots Sep 27, 2025
99f4a3c
chore(review): seed feedback for PR #171 (dc817f5fcba74cd582cddea533a…
Sep 27, 2025
2784651
fix(cli): balance git_libgit2_init/shutdown on failures
flyingrobots Sep 27, 2025
27a9a58
Merge branch 'chore/more-fixes' of github.com:neuroglyph/git-mind int…
flyingrobots Sep 27, 2025
4c424de
chore(review): seed feedback for PR #171 (27a9a5857017fec1a2d9314dc5e…
Sep 27, 2025
f77795e
feat(git-port): add blob disposer hook
flyingrobots Sep 27, 2025
bc31728
Merge branch 'chore/more-fixes' of github.com:neuroglyph/git-mind int…
flyingrobots Sep 27, 2025
7a7557c
chore(review): seed feedback for PR #171 (bc31728cb33add55ff420827aab…
Sep 27, 2025
91b6171
fix(cache): harden meta loading for truncated branch names
flyingrobots Sep 27, 2025
73db6ce
Merge branch 'chore/more-fixes' of github.com:neuroglyph/git-mind int…
flyingrobots Sep 27, 2025
6c8a6c3
chore(review): seed feedback for PR #171 (73db6ce6f53c6929dd0f5afc0c6…
Sep 27, 2025
d31cbe3
fix(cache): relax CLOCKS_PER_SEC assumption
flyingrobots Sep 27, 2025
1a4a7d2
Merge branch 'chore/more-fixes' of github.com:neuroglyph/git-mind int…
flyingrobots Sep 27, 2025
b776c91
chore(review): seed feedback for PR #171 (1a4a7d2bddf88e4a5cf82cb985c…
Sep 27, 2025
11c1de1
refactor: Standardize Repository Ownership, Cleanup, and OID Utility …
flyingrobots Sep 28, 2025
8107afc
Merge branch 'chore/more-fixes' of github.com:neuroglyph/git-mind int…
flyingrobots Sep 28, 2025
1336087
docs: marked up review worksheet PR171 71951
flyingrobots Sep 28, 2025
5efa2d0
docs: PR feedback worksheet
flyingrobots Sep 28, 2025
dc66533
chore(review): seed feedback for PR #171 (5efa2d0dcc6b121765a7f820a41…
Sep 28, 2025
eb6593e
feat(git): Implement safe non-fast-forward reference updates
flyingrobots Sep 28, 2025
542d2ac
Merge branch 'chore/more-fixes' of github.com:neuroglyph/git-mind int…
flyingrobots Sep 28, 2025
11cd1ca
chore(review): seed feedback for PR #171 (542d2ac051f5ae32ef5b07c44d7…
Sep 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
@@ -1,4 +0,0 @@
docs-only:
- changed-files:
- any-glob-to-any-file: ['docs/**', 'README.md', 'AGENTS.md', 'CONTRIBUTING.md']

18 changes: 0 additions & 18 deletions .github/workflows/label-docs-only.yml

This file was deleted.

30 changes: 30 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ recent_developments:
- 2025-09-19 — Cache rebuild now routes through an app-level service (`core/src/app/cache/cache_rebuild_service.c`) so `builder.c` stays a thin adapter and the One-Thing rule holds.
- 2025-09-19 — All future temp directory work must flow through a platform-aware filesystem/temp-path service (`~/.gitmind/<repo-id>/…` on every OS). Draft the port + adapters next and stop writing ad-hoc dotfiles in tests.
- 2025-09-19 — In progress: drafting git object/commit ports + fake adapters so cache rebuilds and tests stop reaching into libgit2 directly.
- 2025-09-27 — When swapping hooks over to ports, wire the adapter once per run and dispose it as soon as the loop ends; the seam stays clean and tests don't need real repos.
- 2025-09-27 — When you add a port capability, stub it in the fake immediately; tests stay fast, and tidy stops tattling.

## Project Structure & Module Organization

Expand Down Expand Up @@ -413,13 +415,41 @@ Every outbound port ships with: (a) production adapter, (b) deterministic fake u

See archives under `docs/activity/` for older logs.

### 2025-09-27
- Routed cache metadata, query, and journal flows through `gm_git_repository_port`; reader/writer now use the adapter for head detection, commit walking, message bodies, and ref updates.
- Added port surface area (head lookup, commit walk/message helpers, parent-aware commit spec) plus libgit2 adapter support to keep hex seams consistent.
- Clang-tidy cleanup delivered: renamed the short `s1`…`s5` path segments, refreshed `walk_commits` naming, and forward-declared libgit2 types so include-cleaner stops shouting.
- Fake git repository port now provides head branch lookup, per-ref commit walks, and commit-message reads via new helpers—tests can enqueue histories instead of skipping those seams.
- Edge creation (basic + attributed) now resolves blob identities through `gm_git_repository_port`; goodbye `ctx->git_repo`/`git_ops` shims. Legacy SHA arrays are populated from the returned OIDs so the transitional fields stay intact.
- Post-commit hook now builds a libgit2-backed `gm_git_repository_port` instead of poking `ctx->git_repo`; journal helpers lean solely on ports and clean up the adapter after processing.
- Hook journey note: fake git port fuels the edge tests; with augment helpers on the seam, the remaining hook work is porting merge detection and retiring the legacy `git_ops` shim.
- Augment helpers now resolve HEAD/parent blob OIDs through the repository port (new `resolve_blob_at_commit` seam), and the fake git adapter learned commit-scoped blob mapping so tests can assert hook behavior without libgit2.
- Architecture doc updated (`docs/architecture/augments-system.md`) with the post-commit ➜ repository-port wiring so future hooks keep the seam intact.
- Merge detection runs through the port too: added `commit_parent_count`, rewired the post-commit hook to create/dispose the port up front, and landed a `test_hook_augment` suite that exercises HEAD/parent blob lookups and merge detection via the fake adapter.
- `gm_context_t` is now strictly port-based—dropped the legacy `git_ops`/`git_repo` shim, handed raw libgit2 ownership to the CLI runtime, refreshed cache/journal tests, and kept the new shape green with `make ci-local`.
- Next up: finish scouring CLI + bench utilities for direct libgit2 calls, port them behind adapters, and keep clang-tidy quiet while we prep the PR.
- Lesson logged: mirroring libgit2 semantics meant storing commit sequences per ref; keeping messages alongside the OIDs made the reader decode deterministic again.
- Cap’n memo: espresso's on deck—once edge jumps ports, we raid the galley for celebratory biscotti.
- Filesystem tidy sweep: replaced the lingering raw `memset` calls in `gm_fs_path_dirname`/`gm_fs_path_basename_append` with `gm_memset_safe`, added the explicit `separator_len` so the bool math stops tripping clang-tidy, and pulled in the security header for the helper prototypes.
- POSIX temp adapter now defines `_GNU_SOURCE` up front and trusts glibc's `realpath`; the redundant extern declaration is gone, keeping include-cleaner + tidy happy.
- Immediate next move: rerun `make ci-local` once these filesystem fixes settle, then chase the remaining bool-to-int clang-tidy nits in the staged benchmark + fake git port files before we go PR hunting.
- Lesson logged: tidy catches implicit bool arithmetic fast—introducing small helper variables beats sprinkling casts and keeps the intent readable for future hex refactors.
- Message in a bottle: future Cap’n, there’s cocoa in the galley—sip it while you double-check the tidy report before raising the PR flag.
- CodeRabbit pass (batch 2): accepted output-zeroing and error-propagation notes—commit creation now clears result OIDs up front, commit message reads null-init outputs, hook blob lookups zero result buffers and distinguish NOT_FOUND from real failures, commit walks honour `GM_CALLBACK_STOP`, and the fake port header owns its std includes.
- CodeRabbit pass (batch 3): reaffirmed the wildebeest benchmark already routes queries via `gm_oid_t`, clarified detached-HEAD fallback inside `gm_hook_get_blob_sha`, and brought the PR171 review docs in line with the front-matter/TOC guidelines.

## Next Steps (handoff checklist)
- Complete on-disk cache migration to OID-only storage and naming; ensure rebuild and fallback readers handle both formats or gate with a one-time migration.
- Extend journal CBOR schema to store OIDs explicitly (not only derivable from legacy fields); update reader/writer and consumers.
- Sweep any remaining headers for guard/extern "C" issues and fix items flagged by `lint_header_guards`.
- Expand attributed edge CBOR to include OIDs alongside legacy fields; update cache/journal consumers accordingly.
- Stabilize and merge open PRs (#164, #165, #166, #167) after CI green; incorporate CodeRabbit actionable feedback and document any rejections under `docs/code-reviews/rejected-suggestions/`.
- Add focused tests: journal base64 encode/decode roundtrip, `gm_snprintf` truncation behavior, and OID-based equality/lookup paths in cache and hooks.
- Silence remaining clang-tidy warnings by fixing short parameter names, pruning redundant includes, and implementing the fake git port’s head/commit-walk seams.
- Audit other hook helpers (diff runner, env) for direct libgit2 usage and port them; parallel update CLI hook installer if needed.
- Expand docs beyond AUGMENTS (e.g., hooks install guide) to reference the repository port seam instead of raw libgit2 handles.
- Sweep for off-the-path libgit2 usage (`gm_cache_query_fanout`, benchmarks) and line up follow-up ports before cutting the PR.
- Document the port-based hook wiring in the broader docs set (install guide, CLI walkthrough) so the CLI/runtime split stays obvious.

## Vision Snapshot

Expand Down
23 changes: 14 additions & 9 deletions apps/cli/cache_rebuild.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/* SPDX-License-Identifier: LicenseRef-MIND-UCAL-1.0 */
/* © 2025 J. Kirby Ross / Neuroglyph Collective */

#include <git2.h>

#include <stdio.h>
#include <string.h>
#include <time.h>
Expand Down Expand Up @@ -33,17 +31,21 @@ static int gm_parse_cache_rebuild_args(gm_cli_ctx_t *cli, int argc, char **argv,
}

/* Get current branch name */
static int gm_get_current_branch(git_repository *repo, const char **branch,
static int gm_get_current_branch(const gm_git_repository_port_t *repo_port,
char *branch_buffer, size_t buffer_size,
const char **branch_out,
gm_output_t *output) {
git_reference *head = NULL;
int rc = git_repository_head(&head, repo);
if (rc < 0) {
gm_result_void_t result = gm_git_repository_port_head_branch(
repo_port, branch_buffer, buffer_size);
if (!result.ok) {
if (result.u.err != NULL) {
gm_error_free(result.u.err);
}
gm_output_error(output, GM_ERROR_GET_BRANCH "\n");
return GM_ERR_INVALID_ARGUMENT;
}

*branch = git_reference_shorthand(head);
git_reference_free(head);
*branch_out = branch_buffer;
return GM_OK;
}

Expand Down Expand Up @@ -121,9 +123,12 @@ int gm_cmd_cache_rebuild(gm_context_t *ctx, gm_cli_ctx_t *cli, int argc, char **
return rc;
}

char branch_storage[BUFFER_SIZE_SMALL];

/* Get current branch if not specified */
if (!branch) {
rc = gm_get_current_branch(ctx->git_repo, &branch, cli->out);
rc = gm_get_current_branch(&ctx->git_repo_port, branch_storage,
sizeof(branch_storage), &branch, cli->out);
if (rc != GM_OK) {
return rc;
}
Expand Down
52 changes: 46 additions & 6 deletions apps/cli/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "cli_runtime.h"

#include "gitmind/adapters/fs/posix_temp_adapter.h"
#include "gitmind/adapters/git/libgit2_repository_port.h"

#include "gitmind/constants_internal.h"

Expand Down Expand Up @@ -181,7 +182,21 @@ static int init_context(gm_context_t *ctx, gm_output_level_t level,

/* Set up core context */
memset(ctx, 0, sizeof(gm_context_t));
ctx->git_repo = repo;

gm_result_void_t repo_port_result =
gm_libgit2_repository_port_create(&ctx->git_repo_port, NULL,
&ctx->git_repo_port_dispose, repo);
if (!repo_port_result.ok) {
int code = GM_ERR_UNKNOWN;
if (repo_port_result.u.err != NULL) {
code = repo_port_result.u.err->code;
gm_error_free(repo_port_result.u.err);
}
git_repository_free(repo);
git_libgit2_shutdown();
return code;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
ctx->user_data = repo;

gm_result_void_t fs_port_result =
gm_posix_fs_temp_port_create(&ctx->fs_temp_port, NULL,
Expand All @@ -192,14 +207,34 @@ static int init_context(gm_context_t *ctx, gm_output_level_t level,
code = fs_port_result.u.err->code;
gm_error_free(fs_port_result.u.err);
}
git_repository_free(repo);
if (ctx->git_repo_port_dispose != NULL) {
ctx->git_repo_port_dispose(&ctx->git_repo_port);
ctx->git_repo_port_dispose = NULL;
}
if (ctx->user_data != NULL) {
git_repository_free((git_repository *)ctx->user_data);
ctx->user_data = NULL;
}
git_libgit2_shutdown();
return code;
}

/* Create CLI output context (separate from core context) */
cli->out = gm_output_create(level, format);
if (!cli->out) {
git_repository_free(repo);
if (ctx->fs_temp_port_dispose != NULL) {
ctx->fs_temp_port_dispose(&ctx->fs_temp_port);
ctx->fs_temp_port_dispose = NULL;
}
if (ctx->git_repo_port_dispose != NULL) {
ctx->git_repo_port_dispose(&ctx->git_repo_port);
ctx->git_repo_port_dispose = NULL;
}
if (ctx->user_data != NULL) {
git_repository_free((git_repository *)ctx->user_data);
ctx->user_data = NULL;
}
git_libgit2_shutdown();
return GM_ERR_OUT_OF_MEMORY;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

Expand All @@ -216,8 +251,13 @@ static void cleanup_context(gm_context_t *ctx, gm_cli_ctx_t *cli) {
ctx->fs_temp_port_dispose(&ctx->fs_temp_port);
ctx->fs_temp_port_dispose = NULL;
}
if (ctx->git_repo) {
git_repository_free((git_repository *)ctx->git_repo);
if (ctx->git_repo_port_dispose != NULL) {
ctx->git_repo_port_dispose(&ctx->git_repo_port);
ctx->git_repo_port_dispose = NULL;
}
if (ctx->user_data != NULL) {
git_repository_free((git_repository *)ctx->user_data);
ctx->user_data = NULL;
}
git_libgit2_shutdown();
}
Expand Down
11 changes: 6 additions & 5 deletions apps/cli/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,27 +96,28 @@ Future: Specific codes for different failures.
```c
git_libgit2_init(); // Global init
git_repository_open(&repo, "."); // Find .git
ctx.git_repo = repo;
gm_libgit2_repository_port_create(&ctx.git_repo_port, NULL,
&ctx.git_repo_port_dispose, repo);
ctx.log_fn = gm_log_default;
```

Clean sequence:

1. Initialize libgit2 once
2. Open repository in current dir
3. Set up function pointers
4. Pass context everywhere
3. Move the raw handle into the CLI runtime for cleanup
4. Build injectable ports and pass the pure context everywhere

### Cleanup

```c
git_repository_free(repo);
ctx.git_repo_port_dispose(&ctx.git_repo_port);
git_libgit2_shutdown();
```

RAII-style cleanup:

- Always free repository
- Always invoke the port disposer
- Shutdown libgit2 last
- No resource leaks

Expand Down
3 changes: 3 additions & 0 deletions assets/images/PRPhiedbachUndBunBun.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 21 additions & 10 deletions benchmarks/wildebeest_stampede.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "../include/gitmind.h"
#include "gitmind/adapters/fs/posix_temp_adapter.h"
#include "gitmind/adapters/git/libgit2_repository_port.h"
#include "gitmind/result.h"

#define WILDEBEEST_COUNT 100000 /* Start with 100K edges */
Expand Down Expand Up @@ -59,9 +60,9 @@ static void run_stampede(git_repository *repo, const char *branch,
printf("Creating wildebeest edges...\n");
gm_edge_t *herd = malloc(wildebeest_count * sizeof(gm_edge_t));

/* Mufasa's SHA (he's at the center of it all) */
uint8_t mufasa_sha[GM_SHA1_SIZE];
generate_wildebeest_sha(mufasa_sha);
/* Mufasa's identity (he's at the center of it all) */
gm_oid_t mufasa_oid = {0};
generate_wildebeest_sha(mufasa_oid.id);

/* Generate the herd */
for (int i = 0; i < wildebeest_count; i++) {
Expand All @@ -70,9 +71,9 @@ static void run_stampede(git_repository *repo, const char *branch,
/* Half run TO Mufasa, half run FROM */
if (i % 2 == 0) {
generate_wildebeest_sha(beast->src_sha);
memcpy(beast->tgt_sha, mufasa_sha, GM_SHA1_SIZE);
memcpy(beast->tgt_sha, mufasa_oid.id, GM_SHA1_SIZE);
} else {
memcpy(beast->src_sha, mufasa_sha, GM_SHA1_SIZE);
memcpy(beast->src_sha, mufasa_oid.id, GM_SHA1_SIZE);
generate_wildebeest_sha(beast->tgt_sha);
}

Expand All @@ -85,8 +86,15 @@ static void run_stampede(git_repository *repo, const char *branch,

/* Add to journal */
printf("Releasing the herd into the journal...\n");
gm_context_t ctx = {0};
ctx.git_repo = repo;
gm_context_t ctx = (gm_context_t){0};

gm_result_void_t repo_port_result =
gm_libgit2_repository_port_create(&ctx.git_repo_port, NULL,
&ctx.git_repo_port_dispose, repo);
if (!repo_port_result.ok) {
fprintf(stderr, "failed to create git repository port\n");
exit(EXIT_FAILURE);
}

gm_result_void_t fs_result =
gm_posix_fs_temp_port_create(&ctx.fs_temp_port, NULL,
Expand All @@ -108,7 +116,7 @@ static void run_stampede(git_repository *repo, const char *branch,
clock_t start = clock();

gm_cache_result_t scan_result = {0};
gm_cache_query_fanout(repo, branch, mufasa_sha, &scan_result);
gm_cache_query_fanout(&ctx, branch, &mufasa_oid, &scan_result);

clock_t end = clock();
result->journal_scan_ms = ((double)(end - start) / CLOCKS_PER_SEC) * 1000;
Expand All @@ -127,7 +135,7 @@ static void run_stampede(git_repository *repo, const char *branch,
start = clock();

gm_cache_result_t cache_result = {0};
gm_cache_query_fanout(repo, branch, mufasa_sha, &cache_result);
gm_cache_query_fanout(&ctx, branch, &mufasa_oid, &cache_result);

end = clock();
result->cache_query_ms = ((double)(end - start) / CLOCKS_PER_SEC) * 1000;
Expand All @@ -143,6 +151,10 @@ static void run_stampede(git_repository *repo, const char *branch,
if (ctx.fs_temp_port_dispose != NULL) {
ctx.fs_temp_port_dispose(&ctx.fs_temp_port);
}
if (ctx.git_repo_port_dispose != NULL) {
ctx.git_repo_port_dispose(&ctx.git_repo_port);
}
git_repository_free(repo);
Comment on lines +154 to +157

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Don't free the shared repo inside run_stampede.

run_stampede is invoked three times from main with the same git_repository *repo. Freeing it here invalidates the pointer after the first run, so the second iteration dereferences freed memory and the third double-frees it. Move the cleanup to the caller and free the repository once after the loop completes.

-    if (ctx.git_repo_port_dispose != NULL) {
-        ctx.git_repo_port_dispose(&ctx.git_repo_port);
-    }
-    git_repository_free(repo);
+    if (ctx.git_repo_port_dispose != NULL) {
+        ctx.git_repo_port_dispose(&ctx.git_repo_port);
+    }

Then call git_repository_free(repo); once in main after the stampede loop.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (ctx.git_repo_port_dispose != NULL) {
ctx.git_repo_port_dispose(&ctx.git_repo_port);
}
git_repository_free(repo);
if (ctx.git_repo_port_dispose != NULL) {
ctx.git_repo_port_dispose(&ctx.git_repo_port);
}
🤖 Prompt for AI Agents
In benchmarks/wildebeest_stampede.c around lines 154-157, run_stampede currently
calls git_repository_free(repo) which frees a shared git_repository used across
three invocations; remove the git_repository_free(repo) call from run_stampede
(leave any per-run ctx.git_repo_port_dispose calls if they are per-run) and
instead add a single git_repository_free(repo) in main after the loop that
invokes run_stampede three times so the repository is freed exactly once when
all runs complete.

free(herd);
}

Expand Down Expand Up @@ -208,7 +220,6 @@ int main(int argc, char **argv) {
"🦁\n\n");

/* Cleanup */
git_repository_free(repo);
git_libgit2_shutdown();

/* Remove temp dir */
Expand Down
1 change: 1 addition & 0 deletions core/include/gitmind/adapters/fs/posix_temp_adapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#define GITMIND_ADAPTERS_FS_POSIX_TEMP_ADAPTER_H

#include "gitmind/ports/fs_temp_port.h"
#include "gitmind/result.h"

#ifdef __cplusplus
extern "C" {
Expand Down
28 changes: 28 additions & 0 deletions core/include/gitmind/adapters/git/libgit2_repository_port.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: LicenseRef-MIND-UCAL-1.0 */
/* © 2025 J. Kirby Ross / Neuroglyph Collective */

#ifndef GITMIND_ADAPTERS_GIT_LIBGIT2_REPOSITORY_PORT_H
#define GITMIND_ADAPTERS_GIT_LIBGIT2_REPOSITORY_PORT_H

#include "gitmind/ports/git_repository_port.h"
#include "gitmind/result.h"

#include <git2/types.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct gm_libgit2_repository_port_state
gm_libgit2_repository_port_state_t;

gm_result_void_t gm_libgit2_repository_port_create(
gm_git_repository_port_t *out_port,
gm_libgit2_repository_port_state_t **out_state,
void (**out_dispose)(gm_git_repository_port_t *), git_repository *repo);

#ifdef __cplusplus
}
#endif

#endif /* GITMIND_ADAPTERS_GIT_LIBGIT2_REPOSITORY_PORT_H */
5 changes: 3 additions & 2 deletions core/include/gitmind/cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ typedef struct {
uint32_t version; /**< Cache format version */
char branch[GM_CACHE_BRANCH_NAME_SIZE]; /**< Branch name (may be truncated if too long) */
gm_oid_t journal_tip_oid_bin; /**< OID of last processed journal commit (preferred). Added at struct tail to preserve ABI layout. */
gm_oid_t cache_tip_oid; /**< OID of cache commit for this branch. */
} gm_cache_meta_t;

/**
Expand Down Expand Up @@ -119,8 +120,8 @@ GM_NODISCARD int gm_cache_stats(gm_context_t *ctx, const char *branch, uint64_t
*/
void gm_cache_result_free(gm_cache_result_t *result);

/* Internal functions have been moved to a private header to avoid exposing
* non-public API via umbrella headers. See core/src/cache/cache_internal.h. */
/* Internal helpers now live behind the git repository port to keep libgit2
* details out of umbrella headers. */

#ifdef __cplusplus
}
Expand Down
Loading