diff options
Diffstat (limited to 'format/packfile/ingest/finalize.go')
| -rw-r--r-- | format/packfile/ingest/finalize.go | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/format/packfile/ingest/finalize.go b/format/packfile/ingest/finalize.go new file mode 100644 index 00000000..6fe4edb2 --- /dev/null +++ b/format/packfile/ingest/finalize.go @@ -0,0 +1,94 @@ +package ingest + +import ( + "errors" + "fmt" + "io/fs" + "strings" + + "codeberg.org/lindenii/furgit/internal/intconv" +) + +// finalizeArtifacts links temporary files to final names and returns Result. +func finalizeArtifacts(state *ingestState) (Result, error) { + base := "pack-" + state.packHash.String() + packFinal := base + ".pack" + idxFinal := base + ".idx" + + revFinal := "" + if state.opts.WriteRev { + revFinal = base + ".rev" + } + + err := linkTempToFinal(state, state.packTmpName, packFinal) + if err != nil { + return Result{}, err + } + + err = linkTempToFinal(state, state.idxTmpName, idxFinal) + if err != nil { + return Result{}, err + } + + if state.opts.WriteRev { + err := linkTempToFinal(state, state.revTmpName, revFinal) + if err != nil { + return Result{}, err + } + } + + objectCount, err := intconv.IntToUint32(len(state.records)) + if err != nil { + return Result{}, err + } + + return Result{ + PackName: packFinal, + IdxName: idxFinal, + RevName: revFinal, + PackHash: state.packHash, + ObjectCount: objectCount, + ThinFixed: state.thinFixed, + }, nil +} + +// rollbackTemporaryArtifacts removes temporary files after failure. +func rollbackTemporaryArtifacts(state *ingestState) { + if state.packTmpName != "" { + _ = state.destination.Remove(state.packTmpName) + } + + if state.idxTmpName != "" { + _ = state.destination.Remove(state.idxTmpName) + } + + if state.revTmpName != "" { + _ = state.destination.Remove(state.revTmpName) + } +} + +// linkTempToFinal hard-links tmp to final, tolerating existing final paths. +func linkTempToFinal(state *ingestState, tmp, final string) error { + if tmp == "" || final == "" { + return fmt.Errorf("packfile/ingest: invalid finalize names tmp=%q final=%q", tmp, final) + } + + if strings.Contains(final, "/") { + return fmt.Errorf("packfile/ingest: final name must be leaf: %q", final) + } + + err := state.destination.Link(tmp, final) + if err == nil { + _ = state.destination.Remove(tmp) + + return nil + } + + if errors.Is(err, fs.ErrExist) { + _ = state.destination.Remove(tmp) + + return nil + } + + return err +} |
