Fix combined partial and incremental updates.

It seems that I forgot to implement incremental update support for
partial updates entirely.
This commit is contained in:
Catherine
2026-03-25 05:08:38 +00:00
parent 310cc7d438
commit b37ca8cd14
4 changed files with 46 additions and 31 deletions

View File

@@ -15,7 +15,6 @@ import (
"strings"
"github.com/c2h5oh/datasize"
"github.com/go-git/go-git/v6/plumbing"
"github.com/klauspost/compress/zstd"
)
@@ -52,16 +51,6 @@ func ExtractZstd(
return next(ctx, boundArchiveStream(stream))
}
const BlobReferencePrefix = "/git/blobs/"
type UnresolvedRefError struct {
missing []string
}
func (err UnresolvedRefError) Error() string {
return fmt.Sprintf("%d unresolved blob references", len(err.missing))
}
func normalizeArchiveMemberName(fileName string) string {
// Strip the leading slash and any extraneous path segments.
fileName = path.Clean(fileName)
@@ -72,21 +61,6 @@ func normalizeArchiveMemberName(fileName string) string {
return fileName
}
// Returns a map of git hash to entry. If `manifest` is nil, returns an empty map.
func indexManifestByGitHash(manifest *Manifest) map[string]*Entry {
index := map[string]*Entry{}
for _, entry := range manifest.GetContents() {
if hash := entry.GetGitHash(); hash != "" {
if _, ok := plumbing.FromHex(hash); ok {
index[hash] = entry
} else {
panic(fmt.Errorf("index: malformed hash: %s", hash))
}
}
}
return index
}
func addSymlinkOrBlobReference(
manifest *Manifest, fileName string, target string,
index map[string]*Entry, missing *[]string,
@@ -110,7 +84,7 @@ func ExtractTar(ctx context.Context, reader io.Reader, oldManifest *Manifest) (*
var dataBytesRecycled int64
var dataBytesTransferred int64
index := indexManifestByGitHash(oldManifest)
index := IndexManifestByGitHash(oldManifest)
missing := []string{}
manifest := NewManifest()
for {
@@ -202,7 +176,7 @@ func ExtractZip(ctx context.Context, reader io.Reader, oldManifest *Manifest) (*
var dataBytesRecycled int64
var dataBytesTransferred int64
index := indexManifestByGitHash(oldManifest)
index := IndexManifestByGitHash(oldManifest)
missing := []string{}
manifest := NewManifest()
for _, file := range archive.File {

View File

@@ -145,6 +145,21 @@ func AddProblem(manifest *Manifest, pathName, format string, args ...any) error
return fmt.Errorf("%s: %s", pathName, cause)
}
// Returns a map of git hash to entry. If `manifest` is nil, returns an empty map.
func IndexManifestByGitHash(manifest *Manifest) map[string]*Entry {
index := map[string]*Entry{}
for _, entry := range manifest.GetContents() {
if hash := entry.GetGitHash(); hash != "" {
if _, ok := plumbing.FromHex(hash); ok {
index[hash] = entry
} else {
panic(fmt.Errorf("index: malformed hash: %s", hash))
}
}
}
return index
}
func IsEntryRegularFile(entry *Entry) bool {
return entry.GetType() == Type_InlineFile ||
entry.GetType() == Type_ExternalFile

View File

@@ -30,8 +30,12 @@ func ApplyTarPatch(manifest *Manifest, reader io.Reader, parents CreateParentsMo
children map[string]*Node
}
// Index the manifest for incremental update operations.
index := IndexManifestByGitHash(manifest)
missing := []string{}
// Extract the manifest contents (which is using a flat hash map) into a directory tree
// so that recursive delete operations have O(1) complexity. s
// so that recursive delete operations have O(1) complexity.
var root *Node
sortedNames := slices.Sorted(maps.Keys(manifest.GetContents()))
for _, name := range sortedNames {
@@ -107,8 +111,16 @@ func ApplyTarPatch(manifest *Manifest, reader io.Reader, parents CreateParentsMo
entry: NewManifestEntry(Type_InlineFile, fileData),
}
case tar.TypeSymlink:
node.children[fileName] = &Node{
entry: NewManifestEntry(Type_Symlink, []byte(header.Linkname)),
if hash, found := strings.CutPrefix(header.Linkname, BlobReferencePrefix); found {
if entry, found := index[hash]; found {
node.children[fileName] = &Node{entry: entry}
} else {
missing = append(missing, hash)
}
} else {
node.children[fileName] = &Node{
entry: NewManifestEntry(Type_Symlink, []byte(header.Linkname)),
}
}
case tar.TypeDir:
node.children[fileName] = &Node{
@@ -129,6 +141,10 @@ func ApplyTarPatch(manifest *Manifest, reader io.Reader, parents CreateParentsMo
}
}
if len(missing) > 0 {
return UnresolvedRefError{missing}
}
// Repopulate manifest contents with the updated directory tree.
var traverse func([]string, *Node)
traverse = func(segments []string, node *Node) {

View File

@@ -10,6 +10,16 @@ import (
"google.golang.org/protobuf/proto"
)
const BlobReferencePrefix = "/git/blobs/"
type UnresolvedRefError struct {
missing []string
}
func (err UnresolvedRefError) Error() string {
return fmt.Sprintf("%d unresolved blob references", len(err.missing))
}
type UpdateOutcome int
const (