-
Notifications
You must be signed in to change notification settings - Fork 0
/
db.go
127 lines (116 loc) · 2.67 KB
/
db.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright 2014 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
package ggit
import (
"bytes"
"crypto/sha1"
"os"
"sort"
"strings"
"syscall"
)
// Object database. Strategy for finding an object based on name:
// 1.) normalize name to hash (TODO: implement)
// 2.) look in .git/packed-refs (TODO: figure out what this is)
// 3.) if pack files not yet parsed:
// 3.a) open .git/objects/pack/
// 3.b) go through pack files there, parse indices
// 4.) search pack files for hash
// 5.) if not found, open .git/objects/ha/sh.....
type pack struct {
p *packFile
idx packIndexFile
baseFileName string
pFile, idxFile *os.File
}
func (p pack) Close() {
if p.p != nil {
_ = syscall.Munmap(p.p.data)
_ = p.pFile.Close()
}
_ = syscall.Munmap(p.idx.data)
_ = p.idxFile.Close()
}
func (p *pack) parsePackFile() error {
data, err := mmapFile(".git/objects/pack/" + p.baseFileName + ".pack")
if err != nil {
return err
}
pp, err := parsePackFile(data)
if err != nil {
return err
}
p.p = &pp
return nil
}
func (p *pack) findHash(hash []byte) *Object {
if len(hash) > sha1.Size {
hash = hash[:sha1.Size]
}
lo := 0
if hash[0] > 0 {
lo = p.idx.fanOut[int(hash[0])-1]
}
hi := p.idx.fanOut[hash[0]]
idx := sort.Search(hi-lo, func(i int) bool {
return bytes.Compare(hash, p.idx.hash(i + lo)[:len(hash)]) <= 0
}) + lo
if idx == hi {
return nil
}
cmp := bytes.Compare(hash, p.idx.hash(idx)[:len(hash)])
if cmp != 0 {
return nil
}
if p.p == nil {
err := p.parsePackFile()
if err != nil {
panic(err)
}
}
o, err := p.p.extractObject(p.idx.offset(idx))
if err != nil {
panic(err)
}
return &o
}
var parsedPackFiles = []*pack(nil) // nil means not yet checked, empty means no pack files
func findHash(hash []byte) (*Object, error) {
if parsedPackFiles == nil {
f, err := os.Open(".git/objects/pack")
if err != nil {
return nil, err
}
names, err := f.Readdirnames(0)
if err != nil {
return nil, err
}
for _, n := range names {
if !strings.HasPrefix(n, "pack-") {
continue
}
if strings.HasSuffix(n, ".idx") {
data, err := mmapFile(".git/objects/pack/" + n)
idx, err := parsePackIndexFile(data)
if err != nil {
return nil, err
}
parsedPackFiles = append(parsedPackFiles,
&pack{p: nil,
pFile: nil,
baseFileName: n[:len(n)-len(".idx")],
idx: idx,
idxFile: f})
}
}
}
for _, p := range parsedPackFiles {
o := p.findHash(hash)
if o != nil {
return o, nil
}
}
return nil, nil
}