Skip to content

Commit 66cd610

Browse files
committed
Actually generate the trac commits needed for tracking.
1 parent 1551bd2 commit 66cd610

File tree

1 file changed

+144
-38
lines changed

1 file changed

+144
-38
lines changed

subtrac.go

Lines changed: 144 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func fatalf(fmt string, args ...interface{}) {
2727
type Trac struct {
2828
name string
2929
hash plumbing.Hash
30+
parents []*Trac
3031
subHeads []*Trac
3132
tracCommit *object.Commit
3233
}
@@ -47,24 +48,22 @@ func (t Trac) String() string {
4748
}
4849

4950
type Cache struct {
50-
repoDir string
51-
repo *git.Repository
52-
tracs map[plumbing.Hash]*Trac
51+
repoDir string
52+
repo *git.Repository
53+
excludes map[plumbing.Hash]bool
54+
tracs map[plumbing.Hash]*Trac
5355
}
5456

5557
func NewCache(rdir string, r *git.Repository, excludes []string) *Cache {
5658
c := Cache{
57-
repoDir: rdir,
58-
repo: r,
59-
tracs: make(map[plumbing.Hash]*Trac),
59+
repoDir: rdir,
60+
repo: r,
61+
excludes: make(map[plumbing.Hash]bool),
62+
tracs: make(map[plumbing.Hash]*Trac),
6063
}
6164
for _, x := range excludes {
6265
hash := plumbing.NewHash(x)
63-
trac := &Trac{
64-
name: "[excluded]",
65-
hash: hash,
66-
}
67-
c.add(trac)
66+
c.excludes[hash] = true
6867
}
6968
return &c
7069
}
@@ -102,10 +101,8 @@ func (c *Cache) tracByRef(refname string) (*Trac, error) {
102101
// any cycles when traversing the commit+submodule hierarchy, although the
103102
// same sub-objects may occur many times at different points in the tree.
104103
func (c *Cache) tracCommit(path string, commit *object.Commit) (*Trac, error) {
105-
// debugf("commit %.10v %v\n", commit.Hash, path)
106104
trac := c.tracs[commit.Hash]
107105
if trac != nil {
108-
// debugf(" found: %v\n", trac)
109106
return trac, nil
110107
}
111108
trac = &Trac{
@@ -116,25 +113,125 @@ func (c *Cache) tracCommit(path string, commit *object.Commit) (*Trac, error) {
116113
if err != nil {
117114
return nil, fmt.Errorf("%v:%.10v: %v", path, commit.Hash, err)
118115
}
119-
_, err = c.tracTree(path+"/", tree)
116+
ttrac, err := c.tracTree(path+"/", tree)
120117
if err != nil {
121118
return nil, err
122119
}
120+
121+
// The list of submodules owned by the tree is the same as the list
122+
// owned by the commit.
123+
trac.subHeads = ttrac.subHeads
124+
123125
for i, parent := range commit.ParentHashes {
124126
pc, err := c.repo.CommitObject(parent)
125127
if err != nil {
126128
return nil, fmt.Errorf("%v:%.10v: %v", path, pc.Hash, err)
127129
}
128130
np := commitPath(path, i+1)
129-
_, err = c.tracCommit(np, pc)
131+
ptrac, err := c.tracCommit(np, pc)
130132
if err != nil {
131133
return nil, err
132134
}
135+
trac.parents = append(trac.parents, ptrac)
136+
}
137+
138+
seenHeads := make(map[plumbing.Hash]bool)
139+
seenTracs := make(map[plumbing.Hash]bool)
140+
var heads []*Trac
141+
var tracs []*object.Commit
142+
143+
for _, h := range trac.subHeads {
144+
if !seenHeads[h.hash] {
145+
seenHeads[h.hash] = true
146+
heads = append(heads, h)
147+
}
133148
}
149+
for _, p := range trac.parents {
150+
if p.tracCommit != nil {
151+
if !seenTracs[p.tracCommit.Hash] {
152+
seenTracs[p.tracCommit.Hash] = true
153+
tracs = append(tracs, p.tracCommit)
154+
}
155+
}
156+
}
157+
158+
if len(trac.parents) == 1 && equalSubs(trac.subHeads, trac.parents[0].subHeads) {
159+
// Nothing has changed since our parent, no new commit needed.
160+
trac.tracCommit = trac.parents[0].tracCommit
161+
} else {
162+
// Generate a new commit that includes our parent(s) and all
163+
// our submodules.
164+
trac.tracCommit, err = c.newTracCommit(commit, tracs, heads)
165+
if err != nil {
166+
return nil, err
167+
}
168+
}
169+
134170
c.add(trac)
135171
return trac, nil
136172
}
137173

174+
func equalSubs(a, b []*Trac) bool {
175+
if len(a) != len(b) {
176+
return false
177+
}
178+
for i := range a {
179+
if a[i].hash != b[i].hash {
180+
return false
181+
}
182+
}
183+
return true
184+
}
185+
186+
func (c *Cache) newTracCommit(commit *object.Commit, tracs []*object.Commit, heads []*Trac) (*object.Commit, error) {
187+
var parents []plumbing.Hash
188+
189+
// Inherit from our parent tracCommits
190+
for _, c := range tracs {
191+
parents = append(parents, c.Hash)
192+
}
193+
// *Also* inherit from the actual submodule heads included in the
194+
// current commit
195+
for _, h := range heads {
196+
parents = append(parents, h.hash)
197+
}
198+
199+
sig := object.Signature{
200+
Name: "git-subtrac",
201+
Email: "git-subtrac@",
202+
When: commit.Committer.When,
203+
}
204+
emptyTree := object.Tree{}
205+
nec := c.repo.Storer.NewEncodedObject()
206+
err := emptyTree.Encode(nec)
207+
if err != nil {
208+
return nil, fmt.Errorf("emptyTree.Encode: %v", err)
209+
}
210+
emptyTreeHash, err := c.repo.Storer.SetEncodedObject(nec)
211+
if err != nil {
212+
return nil, fmt.Errorf("emptyTree.Store: %v", err)
213+
}
214+
215+
tc := &object.Commit{
216+
Author: sig,
217+
Committer: sig,
218+
TreeHash: emptyTreeHash,
219+
ParentHashes: parents,
220+
Message: "[git-subtrac merge]",
221+
}
222+
nec = c.repo.Storer.NewEncodedObject()
223+
err = tc.Encode(nec)
224+
if err != nil {
225+
return nil, fmt.Errorf("commit.Encode: %v", err)
226+
}
227+
tch, err := c.repo.Storer.SetEncodedObject(nec)
228+
if err != nil {
229+
return nil, fmt.Errorf("commit.Store: %v", err)
230+
}
231+
tc.Hash = tch
232+
return tc, nil
233+
}
234+
138235
func commitPath(path string, sub int) string {
139236
if sub != 1 {
140237
return fmt.Sprintf("%s^%d", path, sub)
@@ -216,45 +313,54 @@ func (c *Cache) tracTree(path string, tree *object.Tree) (*Trac, error) {
216313
if trac != nil {
217314
return trac, nil
218315
}
316+
trac = &Trac{
317+
name: path,
318+
hash: tree.Hash,
319+
}
219320
for _, e := range tree.Entries {
220321
if e.Mode == filemode.Submodule {
221-
if c.tracs[e.Hash] != nil {
222-
// already handled
322+
if c.excludes[e.Hash] {
323+
// Pretend it doesn't exist; don't link to it.
223324
continue
224325
}
225-
subpath := fmt.Sprintf("%s%s@%.10v", path, e.Name, e.Hash)
226-
sc, err := c.repo.CommitObject(e.Hash)
227-
if err != nil {
228-
err = c.tryFetchFromSubmodules(subpath, e.Hash)
326+
subtrac := c.tracs[e.Hash]
327+
if subtrac == nil {
328+
subpath := fmt.Sprintf("%s%s@%.10v", path, e.Name, e.Hash)
329+
sc, err := c.repo.CommitObject(e.Hash)
229330
if err != nil {
230-
return nil, fmt.Errorf("%v (maybe fetch it manually?)", err)
331+
err = c.tryFetchFromSubmodules(subpath, e.Hash)
332+
if err != nil {
333+
return nil, fmt.Errorf("%v (maybe fetch it manually?)", err)
334+
}
335+
}
336+
sc, err = c.repo.CommitObject(e.Hash)
337+
if err != nil {
338+
return nil, fmt.Errorf("%v: %v",
339+
subpath, err)
340+
}
341+
subtrac, err = c.tracCommit(subpath, sc)
342+
if err != nil {
343+
return nil, err
231344
}
232345
}
233-
sc, err = c.repo.CommitObject(e.Hash)
234-
if err != nil {
235-
return nil, fmt.Errorf("%v: %v",
236-
subpath, err)
237-
}
238-
_, err = c.tracCommit(subpath, sc)
239-
if err != nil {
240-
return nil, err
241-
}
346+
// Add exactly one submodule.
347+
// subtrac.tracCommit includes any submodules which
348+
// that submodule itself depends on.
349+
trac.subHeads = append(trac.subHeads, subtrac)
242350
} else if e.Mode == filemode.Dir {
243351
t, err := c.repo.TreeObject(e.Hash)
244352
if err != nil {
245353
return nil, fmt.Errorf("%v:%.10v: %v",
246354
path+e.Name, e.Hash, err)
247355
}
248-
_, err = c.tracTree(path+e.Name+"/", t)
356+
subtrac, err := c.tracTree(path+e.Name+"/", t)
249357
if err != nil {
250358
return nil, err
251359
}
360+
// Collect the list of submodules all the way down the tree.
361+
trac.subHeads = append(trac.subHeads, subtrac.subHeads...)
252362
}
253363
}
254-
trac = &Trac{
255-
name: path,
256-
hash: tree.Hash,
257-
}
258364
c.add(trac)
259365
return trac, nil
260366
}
@@ -301,11 +407,11 @@ func main() {
301407
}
302408
c := NewCache(*repodir, r, *excludes)
303409
refname := args[1]
304-
_, err = c.tracByRef(refname)
410+
trac, err := c.tracByRef(refname)
305411
if err != nil {
306412
fatalf("%v\n", err)
307413
}
308-
fmt.Printf("%v\n", c)
414+
fmt.Printf("%v\n", trac.tracCommit.Hash)
309415
default:
310416
usagef("unknown command %v", args[0])
311417
}

0 commit comments

Comments
 (0)