Skip to content

Commit

Permalink
git: enable fetch with unqualified references
Browse files Browse the repository at this point in the history
Signed-off-by: Arieh Schneier <15041913+AriehSchneier@users.noreply.github.com>
  • Loading branch information
AriehSchneier committed May 7, 2023
1 parent 1dbd729 commit 7789b49
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 45 deletions.
7 changes: 4 additions & 3 deletions plumbing/reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ const (
symrefPrefix = "ref: "
)

// RefRevParseRules are a set of rules to parse references into short names.
// These are the same rules as used by git in shorten_unambiguous_ref.
// RefRevParseRules are a set of rules to parse references into short names, or expand into a full reference.
// These are the same rules as used by git in shorten_unambiguous_ref and expand_ref.
// See: https://github.com/git/git/blob/e0aaa1b6532cfce93d87af9bc813fb2e7a7ce9d7/refs.c#L417
var RefRevParseRules = []string{
"%s",
"refs/%s",
"refs/tags/%s",
"refs/heads/%s",
Expand Down Expand Up @@ -113,7 +114,7 @@ func (r ReferenceName) String() string {
func (r ReferenceName) Short() string {
s := string(r)
res := s
for _, format := range RefRevParseRules {
for _, format := range RefRevParseRules[1:] {
_, err := fmt.Sscanf(s, format, &res)
if err == nil {
continue
Expand Down
60 changes: 39 additions & 21 deletions remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,9 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen
return nil, err
}

refs, err := calculateRefs(o.RefSpecs, remoteRefs, o.Tags)
modifiableRefSpecs := make([]config.RefSpec, len(o.RefSpecs))
copy(modifiableRefSpecs, o.RefSpecs)
refs, err := calculateRefs(modifiableRefSpecs, remoteRefs, o.Tags)
if err != nil {
return nil, err
}
Expand All @@ -469,7 +471,7 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen
}
}

updated, err := r.updateLocalReferenceStorage(o.RefSpecs, refs, remoteRefs, o.Tags, o.Force)
updated, err := r.updateLocalReferenceStorage(modifiableRefSpecs, refs, remoteRefs, o.Tags, o.Force)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -925,8 +927,8 @@ func calculateRefs(
}

refs := make(memory.ReferenceStorage)
for _, s := range spec {
if err := doCalculateRefs(s, remoteRefs, refs); err != nil {
for i := range spec {
if err := doCalculateRefs(&spec[i], remoteRefs, refs); err != nil {
return nil, err
}
}
Expand All @@ -935,26 +937,17 @@ func calculateRefs(
}

func doCalculateRefs(
s config.RefSpec,
s *config.RefSpec,
remoteRefs storer.ReferenceStorer,
refs memory.ReferenceStorage,
) error {
iter, err := remoteRefs.IterReferences()
if err != nil {
return err
}

if s.IsExactSHA1() {
ref := plumbing.NewHashReference(s.Dst(""), plumbing.NewHash(s.Src()))
return refs.SetReference(ref)
}

var matched bool
err = iter.ForEach(func(ref *plumbing.Reference) error {
if !s.Match(ref.Name()) {
return nil
}

onMatched := func(ref *plumbing.Reference) error {
if ref.Type() == plumbing.SymbolicReference {
target, err := storer.ResolveReference(remoteRefs, ref.Name())
if err != nil {
Expand All @@ -973,18 +966,39 @@ func doCalculateRefs(
return err
}

if !s.IsWildcard() {
return storer.ErrStop
return nil
}

var ret error
if s.IsWildcard() {
iter, err := remoteRefs.IterReferences()
if err != nil {
return err
}
ret = iter.ForEach(func(ref *plumbing.Reference) error {
if !s.Match(ref.Name()) {
return nil
}

return nil
})
return onMatched(ref)
})
} else {
var resolvedRef *plumbing.Reference
src := s.Src()
resolvedRef, ret = expand_ref(remoteRefs, plumbing.ReferenceName(src))
if ret == nil {
ret = onMatched(resolvedRef)
if src != "HEAD" && src != "FETCH_HEAD" {
*s = config.RefSpec(strings.Replace(string(*s), src, resolvedRef.Name().String(), 1))
}
}
}

if !matched && !s.IsWildcard() {
return NoMatchingRefSpecError{refSpec: s}
return NoMatchingRefSpecError{refSpec: *s}
}

return err
return ret
}

func getWants(localStorer storage.Storer, refs memory.ReferenceStorage) ([]plumbing.Hash, error) {
Expand Down Expand Up @@ -1165,6 +1179,10 @@ func (r *Remote) updateLocalReferenceStorage(
}

localName := spec.Dst(ref.Name())
// If localName doesn't start with "refs/" then treat as a branch
if !strings.HasPrefix(localName.String(), "refs/") {
localName = plumbing.ReferenceName("refs/heads/" + localName.String())
}
old, _ := storer.ResolveReference(r.s, localName)
new := plumbing.NewHashReference(localName, ref.Hash())

Expand Down
26 changes: 26 additions & 0 deletions remote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,32 @@ func (s *RemoteSuite) TestFetch(c *C) {
})
}

func (s *RemoteSuite) TestFetchToNewBranch(c *C) {
r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
})

s.testFetch(c, r, &FetchOptions{
RefSpecs: []config.RefSpec{
// qualified branch to unqualified branch
"+refs/heads/master:foo",
// unqualified branch to unqualified branch
"+master:bar",
// unqualified tag to unqualified branch
config.RefSpec("+tree-tag:tree-tag"),
// unqualified tag to qualified tag
config.RefSpec("+commit-tag:refs/tags/renamed-tag"),
},
}, []*plumbing.Reference{
plumbing.NewReferenceFromStrings("refs/heads/foo", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"),
plumbing.NewReferenceFromStrings("refs/heads/bar", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"),
plumbing.NewReferenceFromStrings("refs/heads/tree-tag", "152175bf7e5580299fa1f0ba41ef6474cc043b70"),
plumbing.NewReferenceFromStrings("refs/tags/tree-tag", "152175bf7e5580299fa1f0ba41ef6474cc043b70"),
plumbing.NewReferenceFromStrings("refs/tags/renamed-tag", "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"),
plumbing.NewReferenceFromStrings("refs/tags/commit-tag", "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"),
})
}

func (s *RemoteSuite) TestFetchNonExistantReference(c *C) {
r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
Expand Down
42 changes: 21 additions & 21 deletions repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -1013,21 +1013,9 @@ func (r *Repository) fetchAndUpdateReferences(
return nil, err
}

var resolvedRef *plumbing.Reference
// return error from checking the raw ref passed in
var rawRefError error
for _, rule := range append([]string{"%s"}, plumbing.RefRevParseRules...) {
resolvedRef, err = storer.ResolveReference(remoteRefs, plumbing.ReferenceName(fmt.Sprintf(rule, ref)))

if err == nil {
break
} else if rawRefError == nil {
rawRefError = err
}
}

resolvedRef, err := expand_ref(remoteRefs, ref)
if err != nil {
return nil, rawRefError
return nil, err
}

refsUpdated, err := r.updateReferences(remote.c.Fetch, resolvedRef)
Expand Down Expand Up @@ -1473,6 +1461,22 @@ func (r *Repository) Worktree() (*Worktree, error) {
return &Worktree{r: r, Filesystem: r.wt}, nil
}

func expand_ref(s storer.ReferenceStorer, ref plumbing.ReferenceName) (*plumbing.Reference, error) {
// return the error from the first rule
var ret error
for _, rule := range plumbing.RefRevParseRules {
resolvedRef, err := storer.ResolveReference(s, plumbing.ReferenceName(fmt.Sprintf(rule, ref)))

if err == nil {
return resolvedRef, nil
} else if ret == nil {
ret = err
}
}

return nil, ret
}

// ResolveRevision resolves revision to corresponding hash. It will always
// resolve to a commit hash, not a tree or annotated tag.
//
Expand Down Expand Up @@ -1502,13 +1506,9 @@ func (r *Repository) ResolveRevision(in plumbing.Revision) (*plumbing.Hash, erro

tryHashes = append(tryHashes, r.resolveHashPrefix(string(revisionRef))...)

for _, rule := range append([]string{"%s"}, plumbing.RefRevParseRules...) {
ref, err := storer.ResolveReference(r.Storer, plumbing.ReferenceName(fmt.Sprintf(rule, revisionRef)))

if err == nil {
tryHashes = append(tryHashes, ref.Hash())
break
}
ref, err := expand_ref(r.Storer, plumbing.ReferenceName(revisionRef))
if err == nil {
tryHashes = append(tryHashes, ref.Hash())
}

// in ambiguous cases, `git rev-parse` will emit a warning, but
Expand Down

0 comments on commit 7789b49

Please sign in to comment.