Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ type PullOptions struct {
// stored, if nil nothing is stored and the capability (if supported)
// no-progress, is sent to the server to avoid send this information.
Progress sideband.Progress
// Force allows the pull to update a local branch even when the remote
// branch does not descend from it.
Force bool
}

// Validate validates the fields and sets the default values.
Expand Down Expand Up @@ -142,6 +145,9 @@ type FetchOptions struct {
// Tags describe how the tags will be fetched from the remote repository,
// by default is TagFollowing.
Tags TagMode
// Force allows the fetch to update a local branch even when the remote
// branch does not descend from it.
Force bool
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is from someone else's PR, but I don't think this is strictly needed because the RefSpecs above already have an IsForce() method that says whether that particular ref should be forced or not. (For PullOptions, the default RefSpec is defined in the RemoteConfig.) Could you please update it to use the IsForce method instead?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now when actually doing this, are you sure we want this? CheckoutOptions and PullOptions have the Force bool in addition to FetchOptions. Removing it from one but not the others would feel inconsistent.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can keep the Force bool if you want, but in that case please also add support for the IsForce() method. The git remote protocol supports both force and non-force fetches in the same batch, so we'll need to be able to specify the force on individual refs.

}

// Validate validates the fields and sets the default values.
Expand Down
28 changes: 26 additions & 2 deletions remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
var (
NoErrAlreadyUpToDate = errors.New("already up-to-date")
ErrDeleteRefNotSupported = errors.New("server does not support delete-refs")
ErrForceNeeded = errors.New("some refs were not updated")
)

// Remote represents a connection to a remote repository.
Expand Down Expand Up @@ -294,7 +295,7 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (storer.ReferenceSt
}
}

updated, err := r.updateLocalReferenceStorage(o.RefSpecs, refs, remoteRefs, o.Tags)
updated, err := r.updateLocalReferenceStorage(o.RefSpecs, refs, remoteRefs, o.Tags, o.Force)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -682,8 +683,11 @@ func (r *Remote) updateLocalReferenceStorage(
specs []config.RefSpec,
fetchedRefs, remoteRefs memory.ReferenceStorage,
tagMode TagMode,
force bool,
) (updated bool, err error) {
isWildcard := true
forceNeeded := false

for _, spec := range specs {
if !spec.IsWildcard() {
isWildcard = false
Expand All @@ -698,7 +702,23 @@ func (r *Remote) updateLocalReferenceStorage(
continue
}

new := plumbing.NewHashReference(spec.Dst(ref.Name()), ref.Hash())
localName := spec.Dst(ref.Name())
old, _ := storer.ResolveReference(r.s, localName)
new := plumbing.NewHashReference(localName, ref.Hash())

// If the ref exists locally as a branch and force is not specified,
// only update if the new ref is an ancestor of the old
if old != nil && old.Name().IsBranch() && !force {
ff, err := isFastForward(r.s, old.Hash(), new.Hash())
if err != nil {
return updated, err
}

if !ff {
forceNeeded = true
continue
}
}

refUpdated, err := updateReferenceStorerIfNeeded(r.s, new)
if err != nil {
Expand Down Expand Up @@ -728,6 +748,10 @@ func (r *Remote) updateLocalReferenceStorage(
updated = true
}

if err == nil && forceNeeded {
err = ErrForceNeeded
}

return
}

Expand Down
1 change: 1 addition & 0 deletions worktree.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
Depth: o.Depth,
Auth: o.Auth,
Progress: o.Progress,
Force: o.Force,
})

updated := true
Expand Down