Skip to content

Commit 4cf2394

Browse files
committed
fix(git): complete ref update after fetch for both bare and normal repositories
The Fetch method was not persisting fetched references to refs/heads after pulling from the remote. When using depth=1 (shallow fetches), the shallow boundary wasn't maintained and refs/heads/ were never created. For normal repos with full history, updatedRefs were collected but never written to storage. - For shallow repositories: Create refs/heads/ and maintain shallow boundary - For normal repositories: Create refs/heads/ from updatedRefs - Ensure both code paths properly persist fetched references to local storage Signed-off-by: Roman Dmytrenko <[email protected]>
1 parent 6126a45 commit 4cf2394

File tree

1 file changed

+73
-4
lines changed

1 file changed

+73
-4
lines changed

internal/storage/git/repository.go

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -397,24 +397,32 @@ func (r *Repository) Fetch(ctx context.Context, specific ...string) (err error)
397397
refSpecs = append(refSpecs, refSpec)
398398
}
399399

400-
if err := r.FetchContext(ctx, &git.FetchOptions{
400+
opts := &git.FetchOptions{
401401
RemoteName: r.remote.Name,
402402
Auth: r.auth,
403403
CABundle: r.caBundle,
404404
InsecureSkipTLS: r.insecureSkipTLS,
405405
RefSpecs: refSpecs,
406406
Prune: true,
407407
Tags: plumbing.NoTags,
408-
Depth: 1,
409-
}); err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
408+
}
409+
410+
// For bare repositories (i.e., repositories without a working tree),
411+
// limit the fetch to a depth of 1 so that only the latest commit is
412+
// retrieved. Since no checkout will occur, a full history is unnecessary
413+
// and would only increase network transfer and storage usage.
414+
if !r.isNormalRepo {
415+
opts.Depth = 1
416+
}
417+
418+
if err := r.FetchContext(ctx, opts); err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
410419
return err
411420
}
412421

413422
allRefs, err := r.References()
414423
if err != nil {
415424
return err
416425
}
417-
418426
if err := allRefs.ForEach(func(ref *plumbing.Reference) error {
419427
// we're only interested in updates to remotes
420428
if !ref.Name().IsRemote() {
@@ -433,6 +441,67 @@ func (r *Repository) Fetch(ctx context.Context, specific ...string) (err error)
433441
return err
434442
}
435443

444+
// Get all existing local branch refs
445+
allLocalRefs, err := r.Storer.IterReferences()
446+
if err != nil {
447+
return fmt.Errorf("failed to get local refs: %w", err)
448+
}
449+
450+
existingHeads := make(map[string]struct{})
451+
if err := allLocalRefs.ForEach(func(ref *plumbing.Reference) error {
452+
if ref.Name().IsBranch() {
453+
branchName := strings.TrimPrefix(ref.Name().String(), "refs/heads/")
454+
existingHeads[branchName] = struct{}{}
455+
}
456+
return nil
457+
}); err != nil {
458+
return fmt.Errorf("failed to iterate local refs: %w", err)
459+
}
460+
461+
// Update refs for branches that exist on remote
462+
for name, hash := range updatedRefs {
463+
ref := plumbing.NewHashReference(plumbing.NewBranchReferenceName(name), hash)
464+
if err := r.Storer.SetReference(ref); err != nil {
465+
return fmt.Errorf("failed to set reference %s: %w", name, err)
466+
}
467+
delete(existingHeads, name)
468+
}
469+
470+
// Remove local refs for branches that no longer exist on remote
471+
for branchName := range existingHeads {
472+
refName := plumbing.NewBranchReferenceName(branchName)
473+
// Check if this branch is one we're tracking
474+
isTracked := false
475+
for _, head := range heads {
476+
if refMatch(branchName, head) {
477+
isTracked = true
478+
break
479+
}
480+
}
481+
if isTracked {
482+
if err := r.Storer.RemoveReference(refName); err != nil {
483+
return fmt.Errorf("failed to remove reference %s: %w", branchName, err)
484+
}
485+
}
486+
}
487+
488+
if opts.Depth == 1 {
489+
// When performing a depth=1 fetch, maintain the shallow boundary in the object store.
490+
// For shallow clones, we track which commits are shallow to prevent operations that
491+
// require complete commit history from proceeding.
492+
shallows, err := r.Storer.Shallow()
493+
if err != nil {
494+
return fmt.Errorf("failed to get shallows: %w", err)
495+
}
496+
shallows = slices.AppendSeq(shallows, maps.Values(updatedRefs))
497+
slices.SortFunc(shallows, func(a plumbing.Hash, b plumbing.Hash) int {
498+
return a.Compare(b.Bytes())
499+
})
500+
if err := r.Storer.SetShallow(slices.Compact(shallows)); err != nil {
501+
return fmt.Errorf("failed to set shallows: %w", err)
502+
}
503+
}
504+
436505
return nil
437506
}
438507

0 commit comments

Comments
 (0)