aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--options.go6
-rw-r--r--remote.go28
-rw-r--r--worktree.go1
3 files changed, 33 insertions, 2 deletions
diff --git a/options.go b/options.go
index 7036bc1..d2cec4b 100644
--- a/options.go
+++ b/options.go
@@ -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.
@@ -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
}
// Validate validates the fields and sets the default values.
diff --git a/remote.go b/remote.go
index 2416152..91ae2fa 100644
--- a/remote.go
+++ b/remote.go
@@ -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")
)
const (
@@ -302,7 +303,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
}
@@ -773,8 +774,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
@@ -789,7 +793,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 {
@@ -819,6 +839,10 @@ func (r *Remote) updateLocalReferenceStorage(
updated = true
}
+ if err == nil && forceNeeded {
+ err = ErrForceNeeded
+ }
+
return
}
diff --git a/worktree.go b/worktree.go
index 8ababfa..67d7f08 100644
--- a/worktree.go
+++ b/worktree.go
@@ -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