Skip to content

Commit ed3b10c

Browse files
authored
Merge pull request go-git#325 from tjamet/push/sha
Remote: Push, add support to push commits per hashes
2 parents 934e99b + 21d8d7e commit ed3b10c

File tree

3 files changed

+141
-2
lines changed

3 files changed

+141
-2
lines changed

Diff for: options.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,14 @@ type PushOptions struct {
198198
RemoteName string
199199
// RemoteURL overrides the remote repo address with a custom URL
200200
RemoteURL string
201-
// RefSpecs specify what destination ref to update with what source
202-
// object. A refspec with empty src can be used to delete a reference.
201+
// RefSpecs specify what destination ref to update with what source object.
202+
//
203+
// The format of a <refspec> parameter is an optional plus +, followed by
204+
// the source object <src>, followed by a colon :, followed by the destination ref <dst>.
205+
// The <src> is often the name of the branch you would want to push, but it can be a SHA-1.
206+
// The <dst> tells which ref on the remote side is updated with this push.
207+
//
208+
// A refspec with empty src can be used to delete a reference.
203209
RefSpecs []config.RefSpec
204210
// Auth credentials, if required, to use with the remote repository.
205211
Auth transport.AuthMethod

Diff for: remote.go

+41
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,10 @@ func (r *Remote) addOrUpdateReferences(
602602
if !rs.IsWildcard() {
603603
ref, ok := refsDict[rs.Src()]
604604
if !ok {
605+
commit, err := object.GetCommit(r.s, plumbing.NewHash(rs.Src()))
606+
if err == nil {
607+
return r.addCommit(rs, remoteRefs, commit.Hash, req)
608+
}
605609
return nil
606610
}
607611

@@ -656,6 +660,43 @@ func (r *Remote) deleteReferences(rs config.RefSpec,
656660
})
657661
}
658662

663+
func (r *Remote) addCommit(rs config.RefSpec,
664+
remoteRefs storer.ReferenceStorer, localCommit plumbing.Hash,
665+
req *packp.ReferenceUpdateRequest) error {
666+
667+
if rs.IsWildcard() {
668+
return errors.New("can't use wildcard together with hash refspecs")
669+
}
670+
671+
cmd := &packp.Command{
672+
Name: rs.Dst(""),
673+
Old: plumbing.ZeroHash,
674+
New: localCommit,
675+
}
676+
remoteRef, err := remoteRefs.Reference(cmd.Name)
677+
if err == nil {
678+
if remoteRef.Type() != plumbing.HashReference {
679+
//TODO: check actual git behavior here
680+
return nil
681+
}
682+
683+
cmd.Old = remoteRef.Hash()
684+
} else if err != plumbing.ErrReferenceNotFound {
685+
return err
686+
}
687+
if cmd.Old == cmd.New {
688+
return nil
689+
}
690+
if !rs.IsForceUpdate() {
691+
if err := checkFastForwardUpdate(r.s, remoteRefs, cmd); err != nil {
692+
return err
693+
}
694+
}
695+
696+
req.Commands = append(req.Commands, cmd)
697+
return nil
698+
}
699+
659700
func (r *Remote) addReferenceIfRefSpecMatches(rs config.RefSpec,
660701
remoteRefs storer.ReferenceStorer, localRef *plumbing.Reference,
661702
req *packp.ReferenceUpdateRequest) error {

Diff for: remote_test.go

+92
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@ import (
55
"context"
66
"errors"
77
"io"
8+
"io/ioutil"
9+
"os"
10+
"path/filepath"
811
"runtime"
912
"time"
1013

1114
"github.com/go-git/go-git/v5/config"
1215
"github.com/go-git/go-git/v5/plumbing"
1316
"github.com/go-git/go-git/v5/plumbing/cache"
17+
"github.com/go-git/go-git/v5/plumbing/object"
1418
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
1519
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
1620
"github.com/go-git/go-git/v5/plumbing/storer"
@@ -1206,3 +1210,91 @@ func (s *RemoteSuite) TestPushRequireRemoteRefs(c *C) {
12061210
c.Assert(err, IsNil)
12071211
c.Assert(newRef, Not(DeepEquals), oldRef)
12081212
}
1213+
1214+
func (s *RemoteSuite) TestCanPushShasToReference(c *C) {
1215+
d, err := ioutil.TempDir("", "TestCanPushShasToReference")
1216+
c.Assert(err, IsNil)
1217+
if err != nil {
1218+
return
1219+
}
1220+
defer os.RemoveAll(d)
1221+
1222+
// remote currently forces a plain path for path based remotes inside the PushContext function.
1223+
// This makes it impossible, in the current state to use memfs.
1224+
// For the sake of readability, use the same osFS everywhere and use plain git repositories on temporary files
1225+
remote, err := PlainInit(filepath.Join(d, "remote"), true)
1226+
c.Assert(err, IsNil)
1227+
c.Assert(remote, NotNil)
1228+
1229+
repo, err := PlainInit(filepath.Join(d, "repo"), false)
1230+
c.Assert(err, IsNil)
1231+
c.Assert(repo, NotNil)
1232+
1233+
fd, err := os.Create(filepath.Join(d, "repo", "README.md"))
1234+
c.Assert(err, IsNil)
1235+
if err != nil {
1236+
return
1237+
}
1238+
_, err = fd.WriteString("# test repo")
1239+
c.Assert(err, IsNil)
1240+
if err != nil {
1241+
return
1242+
}
1243+
err = fd.Close()
1244+
c.Assert(err, IsNil)
1245+
if err != nil {
1246+
return
1247+
}
1248+
1249+
wt, err := repo.Worktree()
1250+
c.Assert(err, IsNil)
1251+
if err != nil {
1252+
return
1253+
}
1254+
1255+
wt.Add("README.md")
1256+
sha, err := wt.Commit("test commit", &CommitOptions{
1257+
Author: &object.Signature{
1258+
Name: "test",
1259+
1260+
When: time.Now(),
1261+
},
1262+
Committer: &object.Signature{
1263+
Name: "test",
1264+
1265+
When: time.Now(),
1266+
},
1267+
})
1268+
c.Assert(err, IsNil)
1269+
if err != nil {
1270+
return
1271+
}
1272+
1273+
gitremote, err := repo.CreateRemote(&config.RemoteConfig{
1274+
Name: "local",
1275+
URLs: []string{filepath.Join(d, "remote")},
1276+
})
1277+
c.Assert(err, IsNil)
1278+
if err != nil {
1279+
return
1280+
}
1281+
1282+
err = gitremote.Push(&PushOptions{
1283+
RemoteName: "local",
1284+
RefSpecs: []config.RefSpec{
1285+
// TODO: check with short hashes that this is still respected
1286+
config.RefSpec(sha.String() + ":refs/heads/branch"),
1287+
},
1288+
})
1289+
c.Assert(err, IsNil)
1290+
if err != nil {
1291+
return
1292+
}
1293+
1294+
ref, err := remote.Reference(plumbing.ReferenceName("refs/heads/branch"), false)
1295+
c.Assert(err, IsNil)
1296+
if err != nil {
1297+
return
1298+
}
1299+
c.Assert(ref.Hash().String(), Equals, sha.String())
1300+
}

0 commit comments

Comments
 (0)