From 38bd9e82fd3d83fd9a82678ec89227aeda2c3b78 Mon Sep 17 00:00:00 2001 From: "Santiago M. Mola" Date: Thu, 27 Apr 2017 14:02:03 +0200 Subject: support force push (refspec with +) * add support for force push * add support for push of new references --- remote.go | 86 +++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 22 deletions(-) (limited to 'remote.go') diff --git a/remote.go b/remote.go index ae8a544..1cb7ba7 100644 --- a/remote.go +++ b/remote.go @@ -8,6 +8,7 @@ import ( "gopkg.in/src-d/go-git.v4/config" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/format/packfile" + "gopkg.in/src-d/go-git.v4/plumbing/object" "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp" "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/capability" "gopkg.in/src-d/go-git.v4/plumbing/protocol/packp/sideband" @@ -57,8 +58,7 @@ func (r *Remote) Fetch(o *FetchOptions) error { func (r *Remote) Push(o *PushOptions) (err error) { // TODO: Support deletes. // TODO: Support pushing tags. - // TODO: Check if force update is given, otherwise reject non-fast forward. - // TODO: Sideband suppor + // TODO: Sideband support if o.RemoteName == "" { o.RemoteName = r.c.Name @@ -265,37 +265,35 @@ func (r *Remote) addReferenceIfRefSpecMatches(rs config.RefSpec, return nil } - dstName := rs.Dst(localRef.Name()) - oldHash := plumbing.ZeroHash - newHash := localRef.Hash() - - iter, err := remoteRefs.IterReferences() - if err != nil { - return err + cmd := &packp.Command{ + Name: rs.Dst(localRef.Name()), + Old: plumbing.ZeroHash, + New: localRef.Hash(), } - err = iter.ForEach(func(remoteRef *plumbing.Reference) error { + remoteRef, err := remoteRefs.Reference(cmd.Name) + if err == nil { if remoteRef.Type() != plumbing.HashReference { + //TODO: check actual git behavior here return nil } - if dstName != remoteRef.Name() { - return nil - } + cmd.Old = remoteRef.Hash() + } else if err != plumbing.ErrReferenceNotFound { + return err + } - oldHash = remoteRef.Hash() + if cmd.Old == cmd.New { return nil - }) + } - if oldHash == newHash { - return nil + if !rs.IsForceUpdate() { + if err := checkFastForwardUpdate(r.s, remoteRefs, cmd); err != nil { + return err + } } - req.Commands = append(req.Commands, &packp.Command{ - Name: dstName, - Old: oldHash, - New: newHash, - }) + req.Commands = append(req.Commands, cmd) return nil } @@ -390,6 +388,50 @@ func objectExists(s storer.EncodedObjectStorer, h plumbing.Hash) (bool, error) { return true, err } +func checkFastForwardUpdate(s storer.EncodedObjectStorer, remoteRefs storer.ReferenceStorer, cmd *packp.Command) error { + if cmd.Old == plumbing.ZeroHash { + _, err := remoteRefs.Reference(cmd.Name) + if err == plumbing.ErrReferenceNotFound { + return nil + } + + if err != nil { + return err + } + + return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String()) + } + + ff, err := isFastForward(s, cmd.Old, cmd.New) + if err != nil { + return err + } + + if !ff { + return fmt.Errorf("non-fast-forward update: %s", cmd.Name.String()) + } + + return nil +} + +func isFastForward(s storer.EncodedObjectStorer, old, new plumbing.Hash) (bool, error) { + c, err := object.GetCommit(s, new) + if err != nil { + return false, err + } + + found := false + iter := object.NewCommitPreIterator(c) + return found, iter.ForEach(func(c *object.Commit) error { + if c.Hash != old { + return nil + } + + found = true + return storer.ErrStop + }) +} + func (r *Remote) newUploadPackRequest(o *FetchOptions, ar *packp.AdvRefs) (*packp.UploadPackRequest, error) { -- cgit