@@ -271,26 +271,35 @@ func (r *backupRun) backupRepo(ctx context.Context, repo source.Repo, authHeader
271271 if err != nil {
272272 return fail (fmt .Errorf ("check refs: %w" , err ))
273273 }
274+
275+ date := r .now ().UTC ().Format ("2006-01-02" )
276+ prefix := path .Join (repo .Host , repo .Owner , repo .Name , date )
277+
278+ // No commits, so no bundle — but the metadata is still stored below. A repository with no
279+ // code can still carry issues, labels and milestones, and dropping those because nobody
280+ // pushed a commit would be a silent loss of exactly the kind this tool exists to prevent.
274281 if ! hasRefs {
275- r .log .Info ("repo skipped ( no commits) " , "repo" , repo .Slug ())
282+ r .log .Info ("repo has no commits; storing metadata only " , "repo" , repo .Slug ())
276283 entry .Status = StatusSkipped
277284 entry .Reason = ReasonEmpty
278- return entry
279- }
280-
281- if err := r .git .BundleAll (ctx , mirror , bundlePath ); err != nil {
282- return fail (err )
285+ } else {
286+ if err := r .git .BundleAll (ctx , mirror , bundlePath ); err != nil {
287+ return fail (err )
288+ }
283289 }
284290
285- date := r .now ().UTC ().Format ("2006-01-02" )
286- prefix := path .Join (repo .Host , repo .Owner , repo .Name , date )
287-
288291 // bundle (git data); the stored SHA covers the on-disk object (ciphertext if encrypted).
289- bres , bundleSHA , err := r .putFile (ctx , path .Join (prefix , repo .Name + ".bundle" ), bundlePath , ret )
290- if err != nil {
291- return fail (err )
292+ // Skipped entirely for a repository with no commits: there is no bundle, and so no sha256
293+ // sidecar either, since that file describes the bundle.
294+ var bundleSHA string
295+ if hasRefs {
296+ bres , sha , err := r .putFile (ctx , path .Join (prefix , repo .Name + ".bundle" ), bundlePath , ret )
297+ if err != nil {
298+ return fail (err )
299+ }
300+ bundleSHA = sha
301+ entry .Artifacts = append (entry .Artifacts , artifact ("bundle" , bres , bundleSHA ))
292302 }
293- entry .Artifacts = append (entry .Artifacts , artifact ("bundle" , bres , bundleSHA ))
294303
295304 // per-resource metadata
296305 meta , err := r .src .FetchMetadata (ctx , repo )
@@ -304,15 +313,18 @@ func (r *backupRun) backupRepo(ctx context.Context, repo source.Repo, authHeader
304313 entry .Artifacts = append (entry .Artifacts , artifact ("meta" , mres , metaSHA ))
305314
306315 // sha256 sidecar (sha256sum format) over the stored bundle object
307- shaLine := fmt .Sprintf ("%s %s\n " , bundleSHA , repo .Name + ".bundle" )
308- sres , shaSHA , err := r .putBytes (ctx , path .Join (prefix , repo .Name + ".sha256" ), []byte (shaLine ), ret )
309- if err != nil {
310- return fail (err )
316+ if hasRefs {
317+ shaLine := fmt .Sprintf ("%s %s\n " , bundleSHA , repo .Name + ".bundle" )
318+ sres , shaSHA , err := r .putBytes (ctx , path .Join (prefix , repo .Name + ".sha256" ), []byte (shaLine ), ret )
319+ if err != nil {
320+ return fail (err )
321+ }
322+ entry .Artifacts = append (entry .Artifacts , artifact ("sha256" , sres , shaSHA ))
311323 }
312- entry .Artifacts = append (entry .Artifacts , artifact ("sha256" , sres , shaSHA ))
313324
314325 // LFS objects (optional): fetch and store as a separate immutable tar artifact.
315- if r .cfg .Backup .LFS && gitexec .LFSAvailable () {
326+ // Nothing to fetch without refs: LFS objects are pointed at by commits.
327+ if hasRefs && r .cfg .Backup .LFS && gitexec .LFSAvailable () {
316328 if err := r .git .LFSFetchAll (ctx , mirror , cloneURL , gitexec.Options {AuthHeader : authHeader }); err != nil {
317329 return fail (fmt .Errorf ("lfs fetch: %w" , err ))
318330 }
0 commit comments