diff options
| author | 2025-11-11 00:00:00 +0000 | |
|---|---|---|
| committer | 2025-11-13 00:00:00 +0000 | |
| commit | 15855e3249754ab7dc07183c9383f8a8e8c26af2 (patch) | |
| tree | 83b32bdd63f7e672152f07d89268e9b268d1f3f5 /refs.go | |
| signature | ||
Initial commit
Diffstat (limited to 'refs.go')
| -rw-r--r-- | refs.go | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/refs.go b/refs.go new file mode 100644 index 00000000..df3a8303 --- /dev/null +++ b/refs.go @@ -0,0 +1,94 @@ +package furgit + +import ( + "bufio" + "bytes" + "errors" + "os" + "strings" +) + +// ResolveRef resolves a fully qualified ref name to its object ID. +func (repo *Repository) ResolveRef(refname string) (Hash, error) { + id, err := repo.resolveLooseRef(refname) + if err == nil { + return id, nil + } else if !errors.Is(err, ErrNotFound) { + return Hash{}, err + } + + return repo.resolvePackedRef(refname) +} + +func (repo *Repository) resolveLooseRef(refname string) (Hash, error) { + data, err := os.ReadFile(repo.repoPath(refname)) + if err != nil { + if os.IsNotExist(err) { + return Hash{}, ErrNotFound + } + return Hash{}, err + } + line := strings.TrimSpace(string(data)) + id, err := ParseHash(line) + if err != nil { + return Hash{}, err + } + return id, nil +} + +func (repo *Repository) resolvePackedRef(refname string) (Hash, error) { + path := repo.repoPath("packed-refs") + f, err := os.Open(path) + if err != nil { + if os.IsNotExist(err) { + return Hash{}, ErrInvalidObject + } + return Hash{}, err + } + defer func() { _ = f.Close() }() + + want := []byte(refname) + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Bytes() + if len(line) == 0 || line[0] == '#' || line[0] == '^' { + continue + } + sp := bytes.IndexByte(line, ' ') + if sp != HashSize*2 { + continue + } + name := line[sp+1:] + if bytes.Equal(name, want) { + hex := string(line[:sp]) + id, err := ParseHash(hex) + if err != nil { + return Hash{}, err + } + return id, nil + } + } + scanErr := scanner.Err() + if scanErr != nil { + return Hash{}, scanErr + } + return Hash{}, ErrInvalidObject +} + +// ResolveHEAD reads HEAD and returns the ref that HEAD points to. +func (repo *Repository) ResolveHEAD() (string, error) { + data, err := os.ReadFile(repo.repoPath("HEAD")) + if err != nil { + return "", err + } + line := strings.TrimSpace(string(data)) + const prefix = "ref: " + if strings.HasPrefix(line, prefix) { + ref := strings.TrimSpace(line[len(prefix):]) + if ref == "" { + return "", ErrInvalidRef + } + return ref, nil + } + return "", ErrInvalidRef +} |
