@@ -4,11 +4,13 @@ import (
4
4
"fmt"
5
5
"github.com/pborman/getopt/v2"
6
6
"gopkg.in/src-d/go-git.v4"
7
+ "gopkg.in/src-d/go-git.v4/config"
7
8
"gopkg.in/src-d/go-git.v4/plumbing"
8
9
"gopkg.in/src-d/go-git.v4/plumbing/filemode"
9
10
"gopkg.in/src-d/go-git.v4/plumbing/object"
10
11
"log"
11
12
"os"
13
+ "path/filepath"
12
14
"sort"
13
15
"strconv"
14
16
"strings"
@@ -45,14 +47,16 @@ func (t Trac) String() string {
45
47
}
46
48
47
49
type Cache struct {
48
- repo * git.Repository
49
- tracs map [plumbing.Hash ]* Trac
50
+ repoDir string
51
+ repo * git.Repository
52
+ tracs map [plumbing.Hash ]* Trac
50
53
}
51
54
52
- func NewCache (r * git.Repository ) * Cache {
55
+ func NewCache (rdir string , r * git.Repository ) * Cache {
53
56
c := Cache {
54
- repo : r ,
55
- tracs : make (map [plumbing.Hash ]* Trac ),
57
+ repoDir : rdir ,
58
+ repo : r ,
59
+ tracs : make (map [plumbing.Hash ]* Trac ),
56
60
}
57
61
return & c
58
62
}
@@ -138,17 +142,85 @@ func commitPath(path string, sub int) string {
138
142
return fmt .Sprintf ("%s~%d" , path [:ix ], v + 1 )
139
143
}
140
144
145
+ func (c * Cache ) tryFetchFromSubmodules (path string , hash plumbing.Hash ) error {
146
+ debugf ("Searching submodules for: %.10v %v\n " , hash , path )
147
+ wt , err := c .repo .Worktree ()
148
+ if err != nil {
149
+ return fmt .Errorf ("git worktree: %v" , err )
150
+ }
151
+ subs , err := wt .Submodules ()
152
+ if err != nil {
153
+ return fmt .Errorf ("git submodules: %v" , subs )
154
+ }
155
+ for _ , sub := range subs {
156
+ subpath := sub .Config ().Path
157
+ subr , err := sub .Repository ()
158
+ if err != nil {
159
+ return fmt .Errorf ("submodule %v: %v" , subpath , err )
160
+ }
161
+ _ , err = subr .CommitObject (hash )
162
+ if err != nil {
163
+ debugf (" ...not in %v\n " , subpath )
164
+ continue
165
+ }
166
+ brname := fmt .Sprintf ("subtrac-tmp-%v" , hash )
167
+ brrefname := plumbing .NewBranchReferenceName (brname )
168
+ ref := plumbing .NewHashReference (brrefname , hash )
169
+ err = subr .Storer .SetReference (ref )
170
+ defer subr .Storer .RemoveReference (brrefname )
171
+ if err != nil {
172
+ return fmt .Errorf ("submodule %v: create %v: %v" , subpath , ref , err )
173
+ }
174
+ remotename := fmt .Sprintf ("%v/.git/modules/%v" ,
175
+ c .repoDir , sub .Config ().Name )
176
+ absremotename , err := filepath .Abs (remotename )
177
+ if err != nil {
178
+ return fmt .Errorf ("AbsPath(%v): %v" , remotename , err )
179
+ }
180
+ remote , err := c .repo .CreateRemoteAnonymous (& config.RemoteConfig {
181
+ Name : "anonymous" ,
182
+ URLs : []string {absremotename },
183
+ })
184
+ if err != nil {
185
+ return fmt .Errorf ("submodule %v: CreateRemote: %v" , absremotename , err )
186
+ }
187
+ err = remote .Fetch (& git.FetchOptions {
188
+ RemoteName : "anonymous" ,
189
+ RefSpecs : []config.RefSpec {
190
+ config .RefSpec (brrefname + ":TRAC_FETCH_HEAD" ),
191
+ },
192
+ })
193
+ if err != nil {
194
+ return fmt .Errorf ("submodule %v: fetch: %v" , absremotename , err )
195
+ }
196
+ // Fetch worked!
197
+ err = subr .Storer .RemoveReference (brrefname )
198
+ if err != nil {
199
+ return fmt .Errorf ("submodule %v: remove %v: %v" , subpath , ref , err )
200
+ }
201
+ return nil
202
+ }
203
+ return fmt .Errorf ("%v: %.10v not found." , path , hash )
204
+ }
205
+
141
206
func (c * Cache ) tracTree (path string , tree * object.Tree ) (* Trac , error ) {
142
207
trac := c .tracs [tree .Hash ]
143
208
if trac != nil {
144
209
return trac , nil
145
210
}
146
211
for _ , e := range tree .Entries {
147
212
if e .Mode == filemode .Submodule {
148
- sc , err := c .repo .CommitObject (e .Hash )
149
213
subpath := fmt .Sprintf ("%s%s@%.10v" , path , e .Name , e .Hash )
214
+ sc , err := c .repo .CommitObject (e .Hash )
215
+ if err != nil {
216
+ err = c .tryFetchFromSubmodules (subpath , e .Hash )
217
+ if err != nil {
218
+ return nil , fmt .Errorf ("%v (maybe fetch it manually?)" , err )
219
+ }
220
+ }
221
+ sc , err = c .repo .CommitObject (e .Hash )
150
222
if err != nil {
151
- return nil , fmt .Errorf ("%v: %v (maybe fetch it?) " ,
223
+ return nil , fmt .Errorf ("%v: %v" ,
152
224
subpath , err )
153
225
}
154
226
_ , err = c .tracCommit (subpath , sc )
@@ -214,7 +286,7 @@ func main() {
214
286
if len (args ) != 2 {
215
287
usagef ("command cid takes exactly 1 argument" )
216
288
}
217
- c := NewCache (r )
289
+ c := NewCache (* repodir , r )
218
290
refname := args [1 ]
219
291
_ , err = c .tracByRef (refname )
220
292
if err != nil {
0 commit comments