aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--remote.go41
-rw-r--r--remote_test.go92
2 files changed, 133 insertions, 0 deletions
diff --git a/remote.go b/remote.go
index 9f2995d..b74f431 100644
--- a/remote.go
+++ b/remote.go
@@ -602,6 +602,10 @@ func (r *Remote) addOrUpdateReferences(
if !rs.IsWildcard() {
ref, ok := refsDict[rs.Src()]
if !ok {
+ commit, err := object.GetCommit(r.s, plumbing.NewHash(rs.Src()))
+ if err == nil {
+ return r.addCommit(rs, remoteRefs, commit.Hash, req)
+ }
return nil
}
@@ -656,6 +660,43 @@ func (r *Remote) deleteReferences(rs config.RefSpec,
})
}
+func (r *Remote) addCommit(rs config.RefSpec,
+ remoteRefs storer.ReferenceStorer, localCommit plumbing.Hash,
+ req *packp.ReferenceUpdateRequest) error {
+
+ if rs.IsWildcard() {
+ return errors.New("can't use wildcard together with hash refspecs")
+ }
+
+ cmd := &packp.Command{
+ Name: rs.Dst(""),
+ Old: plumbing.ZeroHash,
+ New: localCommit,
+ }
+ remoteRef, err := remoteRefs.Reference(cmd.Name)
+ if err == nil {
+ if remoteRef.Type() != plumbing.HashReference {
+ //TODO: check actual git behavior here
+ return nil
+ }
+
+ cmd.Old = remoteRef.Hash()
+ } else if err != plumbing.ErrReferenceNotFound {
+ return err
+ }
+ if cmd.Old == cmd.New {
+ return nil
+ }
+ if !rs.IsForceUpdate() {
+ if err := checkFastForwardUpdate(r.s, remoteRefs, cmd); err != nil {
+ return err
+ }
+ }
+
+ req.Commands = append(req.Commands, cmd)
+ return nil
+}
+
func (r *Remote) addReferenceIfRefSpecMatches(rs config.RefSpec,
remoteRefs storer.ReferenceStorer, localRef *plumbing.Reference,
req *packp.ReferenceUpdateRequest) error {
diff --git a/remote_test.go b/remote_test.go
index 0283e64..df07c08 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -5,12 +5,16 @@ import (
"context"
"errors"
"io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
"runtime"
"time"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/cache"
+ "github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v5/plumbing/storer"
@@ -1206,3 +1210,91 @@ func (s *RemoteSuite) TestPushRequireRemoteRefs(c *C) {
c.Assert(err, IsNil)
c.Assert(newRef, Not(DeepEquals), oldRef)
}
+
+func (s *RemoteSuite) TestCanPushShasToReference(c *C) {
+ d, err := ioutil.TempDir("", "TestCanPushShasToReference")
+ c.Assert(err, IsNil)
+ if err != nil {
+ return
+ }
+ defer os.RemoveAll(d)
+
+ // remote currently forces a plain path for path based remotes inside the PushContext function.
+ // This makes it impossible, in the current state to use memfs.
+ // For the sake of readability, use the same osFS everywhere and use plain git repositories on temporary files
+ remote, err := PlainInit(filepath.Join(d, "remote"), true)
+ c.Assert(err, IsNil)
+ c.Assert(remote, NotNil)
+
+ repo, err := PlainInit(filepath.Join(d, "repo"), false)
+ c.Assert(err, IsNil)
+ c.Assert(repo, NotNil)
+
+ fd, err := os.Create(filepath.Join(d, "repo", "README.md"))
+ c.Assert(err, IsNil)
+ if err != nil {
+ return
+ }
+ _, err = fd.WriteString("# test repo")
+ c.Assert(err, IsNil)
+ if err != nil {
+ return
+ }
+ err = fd.Close()
+ c.Assert(err, IsNil)
+ if err != nil {
+ return
+ }
+
+ wt, err := repo.Worktree()
+ c.Assert(err, IsNil)
+ if err != nil {
+ return
+ }
+
+ wt.Add("README.md")
+ sha, err := wt.Commit("test commit", &CommitOptions{
+ Author: &object.Signature{
+ Name: "test",
+ Email: "test@example.com",
+ When: time.Now(),
+ },
+ Committer: &object.Signature{
+ Name: "test",
+ Email: "test@example.com",
+ When: time.Now(),
+ },
+ })
+ c.Assert(err, IsNil)
+ if err != nil {
+ return
+ }
+
+ gitremote, err := repo.CreateRemote(&config.RemoteConfig{
+ Name: "local",
+ URLs: []string{filepath.Join(d, "remote")},
+ })
+ c.Assert(err, IsNil)
+ if err != nil {
+ return
+ }
+
+ err = gitremote.Push(&PushOptions{
+ RemoteName: "local",
+ RefSpecs: []config.RefSpec{
+ // TODO: check with short hashes that this is still respected
+ config.RefSpec(sha.String() + ":refs/heads/branch"),
+ },
+ })
+ c.Assert(err, IsNil)
+ if err != nil {
+ return
+ }
+
+ ref, err := remote.Reference(plumbing.ReferenceName("refs/heads/branch"), false)
+ c.Assert(err, IsNil)
+ if err != nil {
+ return
+ }
+ c.Assert(ref.Hash().String(), Equals, sha.String())
+}