Skip to content
This repository has been archived by the owner on Apr 12, 2019. It is now read-only.

Add Storage interface for different implementation of git tags #92

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions refs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package git

import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
)

func readPackedRefs(repoPath string) ([]string, error) {
path := repoPath + "/packed-refs"

if _, err := os.Stat(path); os.IsNotExist(err) {
return nil, nil
}

refData, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}

re := regexp.MustCompile("v\\d+\\.\\d+\\.\\d+$")
names := []string{}

for _, ref := range bytes.Split(refData, []byte("\n")) {
if tag := re.Find(ref); tag != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would this get the tag unreleased-beta-version ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and the regexp given above doesn't find any tags at all since it's broken :)

names = append(names, string(tag))
}
}

return names, nil
}

func readRefDir(repoPath, prefix, relPath string) ([]string, error) {
dirPath := filepath.Join(repoPath, prefix, relPath)
f, err := os.Open(dirPath)
if err != nil {
return nil, err
}
defer f.Close()

fis, err := f.Readdir(0)
if err != nil {
return nil, err
}

names := make([]string, 0, len(fis))
for _, fi := range fis {
if strings.Contains(fi.Name(), ".DS_Store") {
continue
}

relFileName := filepath.Join(relPath, fi.Name())
if fi.IsDir() {
subnames, err := readRefDir(repoPath, prefix, relFileName)
if err != nil {
return nil, err
}
names = append(names, subnames...)
continue
}

names = append(names, relFileName)
}

return names, nil
}
27 changes: 1 addition & 26 deletions repo_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ package git

import (
"strings"

"github.com/mcuadros/go-version"
)

// TagPrefix tags prefix path on the repository
Expand Down Expand Up @@ -130,28 +128,5 @@ func (repo *Repository) GetTagInfos() ([]*Tag, error) {

// GetTags returns all tags of the repository.
func (repo *Repository) GetTags() ([]string, error) {
cmd := NewCommand("tag", "-l")
if version.Compare(gitVersion, "2.0.0", ">=") {
cmd.AddArguments("--sort=-v:refname")
}

stdout, err := cmd.RunInDir(repo.Path)
if err != nil {
return nil, err
}

tags := strings.Split(stdout, "\n")
tags = tags[:len(tags)-1]

if version.Compare(gitVersion, "2.0.0", "<") {
version.Sort(tags)

// Reverse order
for i := 0; i < len(tags)/2; i++ {
j := len(tags) - i - 1
tags[i], tags[j] = tags[j], tags[i]
}
}

return tags, nil
return DefaultStorage.GetTags(repo.Path)
}
33 changes: 33 additions & 0 deletions repo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package git

import "testing"

func TestGetRefs(t *testing.T) {
expected := []string{"v1.0.0", "v1.2.1", "v2.0.0", "v3.0.0", "v3.1.0", "v3.10.1"}

for _, p := range []string{
"testdata/test_loose_refs.git",
"testdata/test_packed_refs.git",
"testdata/test_mixed_refs.git",
} {
r, err := OpenRepository(p)
if err != nil {
t.Fatal(err)
}
tags, err := r.GetTags()
if err != nil {
t.Fatal(err)
}

if len(expected) != len(tags) {
t.Fatalf("[%s] wrong number of tags returned - expected [%d] got [%d]", p, len(expected), len(tags))
}

for i, v := range tags {
if expected[i] != v {
t.Fatalf("[%s] incorrect tag - expected [%s] got [%s]", p, expected[i], v)
}
}
}

}
87 changes: 87 additions & 0 deletions storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package git

import (
"path/filepath"
"strings"

"github.com/Unknwon/com"
version "github.com/mcuadros/go-version"
)

// Storage is an interface which describes how to fetch git objects from the git data storage placement.
type Storage interface {
GetTags(string) ([]string, error)
}

// shellStorage is an implementation of Storage which use git shell to retrieve git objects from file system.
type shellStorage struct {
}

// GetTags return the tag names according repoPath
func (shellStorage) GetTags(repoPath string) ([]string, error) {
cmd := NewCommand("tag", "-l")
if version.Compare(gitVersion, "2.0.0", ">=") {
cmd.AddArguments("--sort=-v:refname")
}

stdout, err := cmd.RunInDir(repoPath)
if err != nil {
return nil, err
}

tags := strings.Split(stdout, "\n")
tags = tags[:len(tags)-1]

if version.Compare(gitVersion, "2.0.0", "<") {
version.Sort(tags)

// Reverse order
for i := 0; i < len(tags)/2; i++ {
j := len(tags) - i - 1
tags[i], tags[j] = tags[j], tags[i]
}
}

return tags, nil
}

type localStorage struct {
}

// GetTags return the tag names according repoPath
func (localStorage) GetTags(repoPath string) ([]string, error) {
packed, err := readPackedRefs(repoPath)
if err != nil {
return nil, err
}

if !com.IsExist(filepath.Join(repoPath, "refs/tags")) {
return packed, nil
}

// Attempt loose files first as the /refs/tags folder should always
// exist whether it has files or not.
loose, err := readRefDir(repoPath, "refs/tags", "")
if err != nil {
return nil, err
}

// If both loose refs and packed refs exist then it's highly
// likely that the loose refs are more recent than packed (created
// on top of packed older refs). Therefore we can append each
// together taking the packed refs first.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not return duplicate refs. Especially not stale refs :(

return append(packed, loose...), nil
}

var (
// ShellStorage provides methods to retrieve git objects via git shell
ShellStorage = new(shellStorage)
// LocalStorage provides methods to retrieve git objects via pure go
LocalStorage = new(localStorage)
// DefaultStorage is the default implementation of git data storage
DefaultStorage Storage = ShellStorage
)
1 change: 1 addition & 0 deletions testdata/test_loose_refs.git/refs/tags/v1.0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f821d04fc4f8ba72f88cf1880b69860c88a0a58b
1 change: 1 addition & 0 deletions testdata/test_loose_refs.git/refs/tags/v1.2.1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f821d04fc4f8ba72f88cf1880b69860c88a0a58b
1 change: 1 addition & 0 deletions testdata/test_loose_refs.git/refs/tags/v2.0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f821d04fc4f8ba72f88cf1880b69860c88a0a58b
1 change: 1 addition & 0 deletions testdata/test_loose_refs.git/refs/tags/v3.0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f821d04fc4f8ba72f88cf1880b69860c88a0a58b
1 change: 1 addition & 0 deletions testdata/test_loose_refs.git/refs/tags/v3.1.0
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f821d04fc4f8ba72f88cf1880b69860c88a0a58b
1 change: 1 addition & 0 deletions testdata/test_loose_refs.git/refs/tags/v3.10.1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f821d04fc4f8ba72f88cf1880b69860c88a0a58b
6 changes: 6 additions & 0 deletions testdata/test_mixed_refs.git/packed-refs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# pack-refs with: peeled fully-peeled
f821d04fc4f8ba72f88cf1880b69860c88a0a58b refs/heads/master
f821d04fc4f8ba72f88cf1880b69860c88a0a58b refs/remotes/origin/master
f821d04fc4f8ba72f88cf1880b69860c88a0a58b refs/tags/v1.0.0
f821d04fc4f8ba72f88cf1880b69860c88a0a58b refs/tags/v1.2.1
f821d04fc4f8ba72f88cf1880b69860c88a0a58b refs/tags/v2.0.0
1 change: 1 addition & 0 deletions testdata/test_mixed_refs.git/refs/tags/v3.0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f821d04fc4f8ba72f88cf1880b69860c88a0a58b
1 change: 1 addition & 0 deletions testdata/test_mixed_refs.git/refs/tags/v3.1.0
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f821d04fc4f8ba72f88cf1880b69860c88a0a58b
1 change: 1 addition & 0 deletions testdata/test_mixed_refs.git/refs/tags/v3.10.1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f821d04fc4f8ba72f88cf1880b69860c88a0a58b
9 changes: 9 additions & 0 deletions testdata/test_packed_refs.git/packed-refs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# pack-refs with: peeled fully-peeled
f821d04fc4f8ba72f88cf1880b69860c88a0a58b refs/heads/master
f821d04fc4f8ba72f88cf1880b69860c88a0a58b refs/remotes/origin/master
f821d04fc4f8ba72f88cf1880b69860c88a0a58b refs/tags/v1.0.0
f821d04fc4f8ba72f88cf1880b69860c88a0a58b refs/tags/v1.2.1
f821d04fc4f8ba72f88cf1880b69860c88a0a58b refs/tags/v2.0.0
f821d04fc4f8ba72f88cf1880b69860c88a0a58b refs/tags/v3.0.0
f821d04fc4f8ba72f88cf1880b69860c88a0a58b refs/tags/v3.1.0
f821d04fc4f8ba72f88cf1880b69860c88a0a58b refs/tags/v3.10.1