tenseleyflow/shithub / d8b70e1

Browse files

S27: RepoFS.CloneBareShared + SetPreciousObjects helpers

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
d8b70e1cfd5d8fe4ec5c01e714f61996dced384f
Parents
2b36e97
Tree
3eafe35

1 changed file

StatusFile+-
M internal/infra/storage/reposfs.go 53 0
internal/infra/storage/reposfs.gomodified
@@ -174,6 +174,59 @@ func (r *RepoFS) InitBare(ctx context.Context, path string) error {
174174
 	return nil
175175
 }
176176
 
177
+// CloneBareShared clones src → dst as a bare repo with object
178
+// alternates pointing back at src. Disk usage of the result is
179
+// essentially refs + a small overhead; objects live in src's
180
+// `objects/` until the fork is detached (S16 hard-delete cascade
181
+// repacks each fork before removing the source).
182
+//
183
+// Both paths must be contained in r.root and on the same volume —
184
+// the same-volume requirement is what makes alternates safe (S04).
185
+//
186
+// On success the dst directory exists with `git init --bare` shape
187
+// plus an `objects/info/alternates` file pointing at src/objects.
188
+// On failure the dst directory is removed so a retry sees a clean
189
+// slate.
190
+func (r *RepoFS) CloneBareShared(ctx context.Context, src, dst string) error {
191
+	if err := r.containedInRoot(src); err != nil {
192
+		return err
193
+	}
194
+	if err := r.containedInRoot(dst); err != nil {
195
+		return err
196
+	}
197
+	if entries, err := os.ReadDir(dst); err == nil && len(entries) > 0 {
198
+		return fmt.Errorf("%w: %s", ErrAlreadyExists, dst)
199
+	}
200
+	if err := os.MkdirAll(filepath.Dir(dst), 0o750); err != nil {
201
+		return fmt.Errorf("storage: repofs: mkdir parent: %w", err)
202
+	}
203
+	// G204: src/dst are RepoPath-derived, both verified under r.root.
204
+	cmd := exec.CommandContext(ctx, "git", "clone", "--bare", "--shared", src, dst) //nolint:gosec
205
+	out, err := cmd.CombinedOutput()
206
+	if err != nil {
207
+		// Best-effort cleanup; if removal fails too, surface the
208
+		// original clone error since that's the actionable signal.
209
+		_ = os.RemoveAll(dst)
210
+		return fmt.Errorf("storage: repofs: git clone --bare --shared: %w (output: %s)", err, strings.TrimSpace(string(out)))
211
+	}
212
+	return nil
213
+}
214
+
215
+// SetPreciousObjects marks a bare repo's objects as not-prunable. The
216
+// canonical foot-gun for forks is source-repo `git gc` removing
217
+// objects that forks reach via alternates; setting this on the source
218
+// after a fork is created prevents that. Idempotent.
219
+func (r *RepoFS) SetPreciousObjects(ctx context.Context, path string) error {
220
+	if err := r.containedInRoot(path); err != nil {
221
+		return err
222
+	}
223
+	cmd := exec.CommandContext(ctx, "git", "-C", path, "config", "extensions.preciousObjects", "true") //nolint:gosec
224
+	if out, err := cmd.CombinedOutput(); err != nil {
225
+		return fmt.Errorf("storage: repofs: set preciousObjects: %w (output: %s)", err, strings.TrimSpace(string(out)))
226
+	}
227
+	return nil
228
+}
229
+
177230
 // Move atomically renames oldPath to newPath. Both must be under root.
178231
 // If newPath already exists, returns ErrAlreadyExists rather than
179232
 // overwriting (avoids silent corruption on concurrent moves).