aboutsummaryrefslogtreecommitdiffstats
path: root/storage/filesystem/internal/dotgit/refs.go
diff options
context:
space:
mode:
Diffstat (limited to 'storage/filesystem/internal/dotgit/refs.go')
-rw-r--r--storage/filesystem/internal/dotgit/refs.go144
1 files changed, 144 insertions, 0 deletions
diff --git a/storage/filesystem/internal/dotgit/refs.go b/storage/filesystem/internal/dotgit/refs.go
new file mode 100644
index 0000000..894732f
--- /dev/null
+++ b/storage/filesystem/internal/dotgit/refs.go
@@ -0,0 +1,144 @@
+package dotgit
+
+import (
+ "bufio"
+ "errors"
+ "io/ioutil"
+ "os"
+ "strings"
+
+ "gopkg.in/src-d/go-git.v4/core"
+)
+
+var (
+ // ErrPackedRefsDuplicatedRef is returned when a duplicated
+ // reference is found in the packed-ref file. This is usually the
+ // case for corrupted git repositories.
+ ErrPackedRefsDuplicatedRef = errors.New("duplicated ref found in packed-ref file")
+ // ErrPackedRefsBadFormat is returned when the packed-ref file
+ // corrupt.
+ ErrPackedRefsBadFormat = errors.New("malformed packed-ref")
+ // ErrSymRefTargetNotFound is returned when a symbolic reference is
+ // targeting a non-existing object. This usually means the
+ // repository is corrupt.
+ ErrSymRefTargetNotFound = errors.New("symbolic reference target not found")
+)
+
+const (
+ refsPath = "refs"
+)
+
+func (d *DotGit) addRefsFromPackedRefs(refs *[]*core.Reference) (err error) {
+ path := d.fs.Join(d.path, packedRefsPath)
+ f, err := d.fs.Open(path)
+ if err != nil {
+ if err == os.ErrNotExist {
+ return nil
+ }
+
+ return err
+ }
+
+ defer func() {
+ if errClose := f.Close(); err == nil {
+ err = errClose
+ }
+ }()
+
+ s := bufio.NewScanner(f)
+ for s.Scan() {
+ ref, err := d.processLine(s.Text())
+ if err != nil {
+ return err
+ }
+
+ if ref != nil {
+ *refs = append(*refs, ref)
+ }
+ }
+
+ return s.Err()
+}
+
+// process lines from a packed-refs file
+func (d *DotGit) processLine(line string) (*core.Reference, error) {
+ switch line[0] {
+ case '#': // comment - ignore
+ return nil, nil
+ case '^': // annotated tag commit of the previous line - ignore
+ return nil, nil
+ default:
+ ws := strings.Split(line, " ") // hash then ref
+ if len(ws) != 2 {
+ return nil, ErrPackedRefsBadFormat
+ }
+
+ return core.NewReferenceFromStrings(ws[1], ws[0]), nil
+ }
+}
+
+func (d *DotGit) addRefsFromRefDir(refs *[]*core.Reference) error {
+ return d.walkReferencesTree(refs, refsPath)
+}
+
+func (d *DotGit) walkReferencesTree(refs *[]*core.Reference, relPath string) error {
+ files, err := d.fs.ReadDir(d.fs.Join(d.path, relPath))
+ if err != nil {
+ return err
+ }
+
+ for _, f := range files {
+ newRelPath := d.fs.Join(relPath, f.Name())
+ if f.IsDir() {
+ if err = d.walkReferencesTree(refs, newRelPath); err != nil {
+ return err
+ }
+
+ continue
+ }
+
+ ref, err := d.readReferenceFile(d.path, newRelPath)
+ if err != nil {
+ return err
+ }
+
+ if ref != nil {
+ *refs = append(*refs, ref)
+ }
+ }
+
+ return nil
+}
+
+func (d *DotGit) addRefFromHEAD(refs *[]*core.Reference) error {
+ ref, err := d.readReferenceFile(d.path, "HEAD")
+ if err != nil {
+ return err
+ }
+
+ *refs = append(*refs, ref)
+ return nil
+}
+
+func (d *DotGit) readReferenceFile(refsPath, refFile string) (ref *core.Reference, err error) {
+ path := d.fs.Join(refsPath, refFile)
+
+ f, err := d.fs.Open(path)
+ if err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ if errClose := f.Close(); err == nil {
+ err = errClose
+ }
+ }()
+
+ b, err := ioutil.ReadAll(f)
+ if err != nil {
+ return nil, err
+ }
+
+ line := strings.TrimSpace(string(b))
+ return core.NewReferenceFromStrings(refFile, line), nil
+}