diff --git a/buck2-test-authority.json b/buck2-test-authority.json index cc10ad8cf4..8bae2f506a 100644 --- a/buck2-test-authority.json +++ b/buck2-test-authority.json @@ -840,6 +840,7 @@ "src/cli/store.integration.test.ts", "src/cli/sync.integration.test.ts", "src/core/git-memory.integration.test.ts", + "src/core/git-reflog.integration.test.ts", "src/core/git-streaming-parsers.integration.test.ts", "src/core/git-timeout.integration.test.ts", "src/core/nix-lock/mod.integration.test.ts", @@ -887,6 +888,7 @@ "src/cli/worktree-lease.integration.test.ts", "src/core/config.unit.test.ts", "src/core/git-memory.integration.test.ts", + "src/core/git-reflog.integration.test.ts", "src/core/git-streaming-parsers.integration.test.ts", "src/core/git-timeout.integration.test.ts", "src/core/git.unit.test.ts", @@ -963,6 +965,7 @@ "src/cli/worktree-lease.integration.test.ts", "src/core/config.unit.test.ts", "src/core/git-memory.integration.test.ts", + "src/core/git-reflog.integration.test.ts", "src/core/git-streaming-parsers.integration.test.ts", "src/core/git-timeout.integration.test.ts", "src/core/git.unit.test.ts", @@ -1029,6 +1032,7 @@ "src/cli/store.integration.test.ts", "src/cli/sync.integration.test.ts", "src/core/git-memory.integration.test.ts", + "src/core/git-reflog.integration.test.ts", "src/core/git-streaming-parsers.integration.test.ts", "src/core/git-timeout.integration.test.ts", "src/core/nix-lock/mod.integration.test.ts", diff --git a/packages/@overeng/megarepo/BUCK b/packages/@overeng/megarepo/BUCK index 9e8d651ef0..bdfd6a6638 100644 --- a/packages/@overeng/megarepo/BUCK +++ b/packages/@overeng/megarepo/BUCK @@ -7,7 +7,7 @@ load("//buck2/products:defs.bzl", "javascript_product") # Projection source: packages/@overeng/megarepo/BUCK.genie.ts # Projection schema version: 13 # Projection generator: effect-utils/genie/buck2-typescript-package-projection -# Semantic fingerprint: sha256:467bfb4a75d4f8ac21b6c3a623362f1ec8c7f974b9f11a87f86cfc838408ae10 +# Semantic fingerprint: sha256:95381df02a78d0cec2e092ec731bc6e403601754d9e15c051d16431cb568d5ab # Regenerate: devenv tasks run genie:run load("//buck2:materialization.bzl", "export_materialization_inputs", "package_view") @@ -193,6 +193,7 @@ export_materialization_inputs([ "src/core/config.ts", "src/core/config.unit.test.ts", "src/core/git-memory.integration.test.ts", + "src/core/git-reflog.integration.test.ts", "src/core/git-streaming-parsers.integration.test.ts", "src/core/git-timeout.integration.test.ts", "src/core/git.ts", @@ -519,6 +520,7 @@ package_view( "src/core/config.ts": "src/core/config.ts", "src/core/config.unit.test.ts": "src/core/config.unit.test.ts", "src/core/git-memory.integration.test.ts": "src/core/git-memory.integration.test.ts", + "src/core/git-reflog.integration.test.ts": "src/core/git-reflog.integration.test.ts", "src/core/git-streaming-parsers.integration.test.ts": "src/core/git-streaming-parsers.integration.test.ts", "src/core/git-timeout.integration.test.ts": "src/core/git-timeout.integration.test.ts", "src/core/git.ts": "src/core/git.ts", @@ -847,6 +849,7 @@ package_view( "src/core/config.ts": "src/core/config.ts", "src/core/config.unit.test.ts": "src/core/config.unit.test.ts", "src/core/git-memory.integration.test.ts": "src/core/git-memory.integration.test.ts", + "src/core/git-reflog.integration.test.ts": "src/core/git-reflog.integration.test.ts", "src/core/git-streaming-parsers.integration.test.ts": "src/core/git-streaming-parsers.integration.test.ts", "src/core/git-timeout.integration.test.ts": "src/core/git-timeout.integration.test.ts", "src/core/git.ts": "src/core/git.ts", @@ -1170,6 +1173,7 @@ package_view( "src/core/config.ts": "src/core/config.ts", "src/core/config.unit.test.ts": "src/core/config.unit.test.ts", "src/core/git-memory.integration.test.ts": "src/core/git-memory.integration.test.ts", + "src/core/git-reflog.integration.test.ts": "src/core/git-reflog.integration.test.ts", "src/core/git-streaming-parsers.integration.test.ts": "src/core/git-streaming-parsers.integration.test.ts", "src/core/git-timeout.integration.test.ts": "src/core/git-timeout.integration.test.ts", "src/core/git.ts": "src/core/git.ts", @@ -1385,6 +1389,7 @@ vitest_test( "src/cli/store.integration.test.ts", "src/cli/sync.integration.test.ts", "src/core/git-memory.integration.test.ts", + "src/core/git-reflog.integration.test.ts", "src/core/git-streaming-parsers.integration.test.ts", "src/core/git-timeout.integration.test.ts", "src/core/nix-lock/mod.integration.test.ts", @@ -1433,6 +1438,7 @@ vitest_collect( "src/cli/store.integration.test.ts", "src/cli/sync.integration.test.ts", "src/core/git-memory.integration.test.ts", + "src/core/git-reflog.integration.test.ts", "src/core/git-streaming-parsers.integration.test.ts", "src/core/git-timeout.integration.test.ts", "src/core/nix-lock/mod.integration.test.ts", diff --git a/packages/@overeng/megarepo/BUCK.genie.ts b/packages/@overeng/megarepo/BUCK.genie.ts index 6ebab8acff..a1c711b777 100644 --- a/packages/@overeng/megarepo/BUCK.genie.ts +++ b/packages/@overeng/megarepo/BUCK.genie.ts @@ -76,6 +76,7 @@ export const buck2TypeScriptAdmission = { 'src/cli/store.integration.test.ts', 'src/cli/sync.integration.test.ts', 'src/core/git-memory.integration.test.ts', + 'src/core/git-reflog.integration.test.ts', 'src/core/git-streaming-parsers.integration.test.ts', 'src/core/git-timeout.integration.test.ts', 'src/core/nix-lock/mod.integration.test.ts', diff --git a/packages/@overeng/megarepo/docs/spec.md b/packages/@overeng/megarepo/docs/spec.md index 8a7b61d59d..7c7a72979e 100644 --- a/packages/@overeng/megarepo/docs/spec.md +++ b/packages/@overeng/megarepo/docs/spec.md @@ -37,6 +37,10 @@ Global repository cache at `~/.megarepo/` (configurable via `MEGAREPO_STORE`). The store uses **bare repos with worktrees per ref**: +New bare stores set local `core.logAllRefUpdates=true` so branch updates leave +reflogs for last-touch and recovery evidence. Fetching an existing store enables +this setting before updating refs, preserving an existing `always` value. + ``` ~/.megarepo/ github.com/ diff --git a/packages/@overeng/megarepo/src/core/git-reflog.integration.test.ts b/packages/@overeng/megarepo/src/core/git-reflog.integration.test.ts new file mode 100644 index 0000000000..d4d68822aa --- /dev/null +++ b/packages/@overeng/megarepo/src/core/git-reflog.integration.test.ts @@ -0,0 +1,93 @@ +import { NodeServices } from '@effect/platform-node' +import { describe, it } from '@effect/vitest' +import { Effect } from 'effect' +import * as FileSystem from 'effect/FileSystem' +import { expect } from 'vitest' + +import * as Git from './git.ts' + +process.env.GIT_CONFIG_GLOBAL = '/dev/null' +process.env.GIT_CONFIG_SYSTEM = '/dev/null' + +const makeFixture = Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem + const root = yield* fs.makeTempDirectoryScoped() + const source = `${root}/source` + const bare = `${root}/store.git` + yield* fs.makeDirectory(source) + yield* Git.runCommand({ cwd: source, args: ['init', '-b', 'main'] }) + yield* Git.runCommand({ + cwd: source, + args: [ + '-c', + 'user.name=Test User', + '-c', + 'user.email=test@example.com', + '-c', + 'commit.gpgsign=false', + 'commit', + '--allow-empty', + '-m', + 'initial', + ], + }) + yield* Git.cloneBare({ url: source, targetPath: bare }) + return { source, bare } +}) + +const readLogging = (repoPath: string) => + Git.runCommand({ + cwd: repoPath, + args: ['config', '--local', '--get', 'core.logAllRefUpdates'], + }) + +const expectFetchedReflog = (repoPath: string) => + Effect.gen(function* () { + const commit = yield* Git.runCommand({ cwd: repoPath, args: ['rev-parse', 'main'] }) + const reflog = yield* Git.runCommand({ + cwd: repoPath, + args: ['reflog', 'show', '--format=%H', 'refs/remotes/origin/main'], + }) + expect(reflog.split('\n')).toContain(commit) + }) + +describe('bare store reflogs', () => { + it.effect('enables reflogs on clone and records a later branch update', () => + Effect.gen(function* () { + const { bare } = yield* makeFixture + expect(yield* readLogging(bare)).toBe('true') + yield* Git.fetchBare({ repoPath: bare }) + yield* expectFetchedReflog(bare) + }).pipe(Effect.provide(NodeServices.layer), Effect.scoped), + ) + + it.effect('enables missing local reflog configuration before fetching existing stores', () => + Effect.gen(function* () { + const { bare } = yield* makeFixture + yield* Git.runCommand({ + cwd: bare, + args: ['config', '--local', '--unset-all', 'core.logAllRefUpdates'], + }) + yield* Git.fetchBare({ repoPath: bare }) + expect(yield* readLogging(bare)).toBe('true') + yield* expectFetchedReflog(bare) + }).pipe(Effect.provide(NodeServices.layer), Effect.scoped), + ) + + for (const value of ['false', 'always', 'ALWAYS']) { + it.effect( + `fetch ${value === 'false' ? 'enables disabled reflogs' : `preserves ${value}`}`, + () => + Effect.gen(function* () { + const { bare } = yield* makeFixture + yield* Git.runCommand({ + cwd: bare, + args: ['config', '--local', 'core.logAllRefUpdates', value], + }) + yield* Git.fetchBare({ repoPath: bare }) + expect(yield* readLogging(bare)).toBe(value === 'false' ? 'true' : value) + yield* expectFetchedReflog(bare) + }).pipe(Effect.provide(NodeServices.layer), Effect.scoped), + ) + } +}) diff --git a/packages/@overeng/megarepo/src/core/git.ts b/packages/@overeng/megarepo/src/core/git.ts index 8bb6195661..56792a425e 100644 --- a/packages/@overeng/megarepo/src/core/git.ts +++ b/packages/@overeng/megarepo/src/core/git.ts @@ -891,6 +891,28 @@ export const repairBareOriginFetchRefspec = ({ repoPath }: { repoPath: string }) }), ) +/** Enable store reflogs without weakening an existing `always` policy. Caller holds the writer lock. */ +export const ensureBareRefLogging = Effect.fn('git/ensure-bare-ref-logging')(function* ({ + repoPath, +}: { + repoPath: string +}) { + const value = yield* runGitCommand({ + args: ['config', '--local', '--get', 'core.logAllRefUpdates'], + cwd: repoPath, + }).pipe( + Effect.catchTag('GitCommandError', (error) => + error.exitCode === 1 && error.stderr === '' ? Effect.succeed('') : Effect.fail(error), + ), + ) + const normalized = value.toLowerCase() + if (normalized === 'true' || normalized === 'always') return + yield* runGitCommand({ + args: ['config', '--local', 'core.logAllRefUpdates', 'true'], + cwd: repoPath, + }) +}) + /** * Clone a repository as a bare repo. * Configures fetch refspec so remote tracking refs are created on fetch. @@ -929,6 +951,10 @@ export const cloneBare = (args: { url: string; targetPath: string }) => { args: ['config', 'remote.origin.fetch', bareOriginFetchRefspec], cwd: args.targetPath, }) + yield* runGitCommand({ + args: ['config', '--local', 'core.logAllRefUpdates', 'true'], + cwd: args.targetPath, + }) return exists === false }).pipe( withGitWriterLock({ path: args.targetPath, create: true }), @@ -959,6 +985,7 @@ export const fetchBare = (args: { refs?: ReadonlyArray }) => Effect.gen(function* () { + yield* ensureBareRefLogging({ repoPath: args.repoPath }) const remote = args.remote ?? 'origin' yield* runGitCommandWithRetry({ args: ['fetch', '--no-tags', '--prune', remote, bareOriginFetchRefspec], diff --git a/packages/@overeng/tui-stories/BUCK b/packages/@overeng/tui-stories/BUCK index f15e028d26..21229aa146 100644 --- a/packages/@overeng/tui-stories/BUCK +++ b/packages/@overeng/tui-stories/BUCK @@ -7,7 +7,7 @@ load("//buck2/products:defs.bzl", "javascript_product") # Projection source: packages/@overeng/tui-stories/BUCK.genie.ts # Projection schema version: 13 # Projection generator: effect-utils/genie/buck2-typescript-package-projection -# Semantic fingerprint: sha256:b93d0006b8d5a54a23a32e8c4de47701ff523f71c088bc9b0e0552f48e8f04c7 +# Semantic fingerprint: sha256:ce22cb54526b18bc11014f2c33324601fcee76b158c2d84c92e5d79f8fbe9bbb # Regenerate: devenv tasks run genie:run load("//buck2:materialization.bzl", "export_materialization_inputs", "package_view") @@ -315,6 +315,7 @@ package_view( "node_modules/@overeng/megarepo/src/core/config.ts": "//packages/@overeng/megarepo:src/core/config.ts", "node_modules/@overeng/megarepo/src/core/config.unit.test.ts": "//packages/@overeng/megarepo:src/core/config.unit.test.ts", "node_modules/@overeng/megarepo/src/core/git-memory.integration.test.ts": "//packages/@overeng/megarepo:src/core/git-memory.integration.test.ts", + "node_modules/@overeng/megarepo/src/core/git-reflog.integration.test.ts": "//packages/@overeng/megarepo:src/core/git-reflog.integration.test.ts", "node_modules/@overeng/megarepo/src/core/git-streaming-parsers.integration.test.ts": "//packages/@overeng/megarepo:src/core/git-streaming-parsers.integration.test.ts", "node_modules/@overeng/megarepo/src/core/git-timeout.integration.test.ts": "//packages/@overeng/megarepo:src/core/git-timeout.integration.test.ts", "node_modules/@overeng/megarepo/src/core/git.ts": "//packages/@overeng/megarepo:src/core/git.ts", @@ -716,6 +717,7 @@ package_view( "node_modules/@overeng/megarepo/src/core/config.ts": "//packages/@overeng/megarepo:src/core/config.ts", "node_modules/@overeng/megarepo/src/core/config.unit.test.ts": "//packages/@overeng/megarepo:src/core/config.unit.test.ts", "node_modules/@overeng/megarepo/src/core/git-memory.integration.test.ts": "//packages/@overeng/megarepo:src/core/git-memory.integration.test.ts", + "node_modules/@overeng/megarepo/src/core/git-reflog.integration.test.ts": "//packages/@overeng/megarepo:src/core/git-reflog.integration.test.ts", "node_modules/@overeng/megarepo/src/core/git-streaming-parsers.integration.test.ts": "//packages/@overeng/megarepo:src/core/git-streaming-parsers.integration.test.ts", "node_modules/@overeng/megarepo/src/core/git-timeout.integration.test.ts": "//packages/@overeng/megarepo:src/core/git-timeout.integration.test.ts", "node_modules/@overeng/megarepo/src/core/git.ts": "//packages/@overeng/megarepo:src/core/git.ts", @@ -1117,6 +1119,7 @@ package_view( "node_modules/@overeng/megarepo/src/core/config.ts": "//packages/@overeng/megarepo:src/core/config.ts", "node_modules/@overeng/megarepo/src/core/config.unit.test.ts": "//packages/@overeng/megarepo:src/core/config.unit.test.ts", "node_modules/@overeng/megarepo/src/core/git-memory.integration.test.ts": "//packages/@overeng/megarepo:src/core/git-memory.integration.test.ts", + "node_modules/@overeng/megarepo/src/core/git-reflog.integration.test.ts": "//packages/@overeng/megarepo:src/core/git-reflog.integration.test.ts", "node_modules/@overeng/megarepo/src/core/git-streaming-parsers.integration.test.ts": "//packages/@overeng/megarepo:src/core/git-streaming-parsers.integration.test.ts", "node_modules/@overeng/megarepo/src/core/git-timeout.integration.test.ts": "//packages/@overeng/megarepo:src/core/git-timeout.integration.test.ts", "node_modules/@overeng/megarepo/src/core/git.ts": "//packages/@overeng/megarepo:src/core/git.ts",