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 package org.eclipse.jgit.gitrepo;
44
45 import java.io.File;
46 import java.io.FileInputStream;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.text.MessageFormat;
50 import java.util.ArrayList;
51 import java.util.List;
52 import java.util.Map;
53
54 import org.eclipse.jgit.api.Git;
55 import org.eclipse.jgit.api.GitCommand;
56 import org.eclipse.jgit.api.SubmoduleAddCommand;
57 import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
58 import org.eclipse.jgit.api.errors.GitAPIException;
59 import org.eclipse.jgit.api.errors.JGitInternalException;
60 import org.eclipse.jgit.dircache.DirCache;
61 import org.eclipse.jgit.dircache.DirCacheBuilder;
62 import org.eclipse.jgit.dircache.DirCacheEntry;
63 import org.eclipse.jgit.gitrepo.ManifestParser.IncludedFileReader;
64 import org.eclipse.jgit.gitrepo.RepoProject.CopyFile;
65 import org.eclipse.jgit.gitrepo.internal.RepoText;
66 import org.eclipse.jgit.internal.JGitText;
67 import org.eclipse.jgit.lib.CommitBuilder;
68 import org.eclipse.jgit.lib.Config;
69 import org.eclipse.jgit.lib.Constants;
70 import org.eclipse.jgit.lib.FileMode;
71 import org.eclipse.jgit.lib.ObjectId;
72 import org.eclipse.jgit.lib.ObjectInserter;
73 import org.eclipse.jgit.lib.ObjectReader;
74 import org.eclipse.jgit.lib.PersonIdent;
75 import org.eclipse.jgit.lib.ProgressMonitor;
76 import org.eclipse.jgit.lib.Ref;
77 import org.eclipse.jgit.lib.RefDatabase;
78 import org.eclipse.jgit.lib.RefUpdate;
79 import org.eclipse.jgit.lib.RefUpdate.Result;
80 import org.eclipse.jgit.lib.Repository;
81 import org.eclipse.jgit.revwalk.RevCommit;
82 import org.eclipse.jgit.revwalk.RevWalk;
83 import org.eclipse.jgit.util.FileUtils;
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 public class RepoCommand extends GitCommand<RevCommit> {
103
104 private String path;
105 private String uri;
106 private String groups;
107 private String branch;
108 private PersonIdent author;
109 private RemoteReader callback;
110 private InputStream inputStream;
111 private IncludedFileReader includedReader;
112
113 private List<RepoProject> bareProjects;
114 private Git git;
115 private ProgressMonitor monitor;
116
117
118
119
120
121
122
123
124
125
126
127 public interface RemoteReader {
128
129
130
131
132
133
134
135
136
137
138 public ObjectId sha1(String uri, String ref) throws GitAPIException;
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154 public byte[] readFile(String uri, String ref, String path)
155 throws GitAPIException, IOException;
156 }
157
158
159 public static class DefaultRemoteReader implements RemoteReader {
160 public ObjectId sha1(String uri, String ref) throws GitAPIException {
161 Map<String, Ref> map = Git
162 .lsRemoteRepository()
163 .setRemote(uri)
164 .callAsMap();
165 Ref r = RefDatabase.findRef(map, ref);
166 return r != null ? r.getObjectId() : null;
167 }
168
169 public byte[] readFile(String uri, String ref, String path)
170 throws GitAPIException, IOException {
171 File dir = FileUtils.createTempDir("jgit_", ".git", null);
172 Repository repo = Git
173 .cloneRepository()
174 .setBare(true)
175 .setDirectory(dir)
176 .setURI(uri)
177 .call()
178 .getRepository();
179 try {
180 return readFileFromRepo(repo, ref, path);
181 } finally {
182 repo.close();
183 FileUtils.delete(dir, FileUtils.RECURSIVE);
184 }
185 }
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201 protected byte[] readFileFromRepo(Repository repo,
202 String ref, String path) throws GitAPIException, IOException {
203 try (ObjectReader reader = repo.newObjectReader()) {
204 ObjectId oid = repo.resolve(ref + ":" + path);
205 return reader.open(oid).getBytes(Integer.MAX_VALUE);
206 }
207 }
208 }
209
210 @SuppressWarnings("serial")
211 private static class ManifestErrorException extends GitAPIException {
212 ManifestErrorException(Throwable cause) {
213 super(RepoText.get().invalidManifest, cause);
214 }
215 }
216
217 @SuppressWarnings("serial")
218 private static class RemoteUnavailableException extends GitAPIException {
219 RemoteUnavailableException(String uri) {
220 super(MessageFormat.format(RepoText.get().errorRemoteUnavailable, uri));
221 }
222 }
223
224
225
226
227 public RepoCommand(final Repository repo) {
228 super(repo);
229 }
230
231
232
233
234
235
236
237
238
239
240 public RepoCommand setPath(final String path) {
241 this.path = path;
242 return this;
243 }
244
245
246
247
248
249
250
251
252
253
254
255 public RepoCommand setInputStream(final InputStream inputStream) {
256 this.inputStream = inputStream;
257 return this;
258 }
259
260
261
262
263
264
265
266 public RepoCommand setURI(final String uri) {
267 this.uri = uri;
268 return this;
269 }
270
271
272
273
274
275
276
277 public RepoCommand setGroups(final String groups) {
278 this.groups = groups;
279 return this;
280 }
281
282
283
284
285
286
287
288
289
290
291
292 public RepoCommand setBranch(final String branch) {
293 this.branch = branch;
294 return this;
295 }
296
297
298
299
300
301
302
303
304
305 public RepoCommand setProgressMonitor(final ProgressMonitor monitor) {
306 this.monitor = monitor;
307 return this;
308 }
309
310
311
312
313
314
315
316
317
318
319 public RepoCommand setAuthor(final PersonIdent author) {
320 this.author = author;
321 return this;
322 }
323
324
325
326
327
328
329
330
331
332 public RepoCommand setRemoteReader(final RemoteReader callback) {
333 this.callback = callback;
334 return this;
335 }
336
337
338
339
340
341
342
343
344 public RepoCommand setIncludedFileReader(IncludedFileReader reader) {
345 this.includedReader = reader;
346 return this;
347 }
348
349 @Override
350 public RevCommit call() throws GitAPIException {
351 try {
352 checkCallable();
353 if (uri == null || uri.length() == 0)
354 throw new IllegalArgumentException(
355 JGitText.get().uriNotConfigured);
356 if (inputStream == null) {
357 if (path == null || path.length() == 0)
358 throw new IllegalArgumentException(
359 JGitText.get().pathNotConfigured);
360 try {
361 inputStream = new FileInputStream(path);
362 } catch (IOException e) {
363 throw new IllegalArgumentException(
364 JGitText.get().pathNotConfigured);
365 }
366 }
367
368 if (repo.isBare()) {
369 bareProjects = new ArrayList<RepoProject>();
370 if (author == null)
371 author = new PersonIdent(repo);
372 if (callback == null)
373 callback = new DefaultRemoteReader();
374 } else
375 git = new Git(repo);
376
377 ManifestParser parser = new ManifestParser(
378 includedReader, path, branch, uri, groups, repo);
379 try {
380 parser.read(inputStream);
381 for (RepoProject proj : parser.getFilteredProjects()) {
382 addSubmodule(proj.getUrl(),
383 proj.getPath(),
384 proj.getRevision(),
385 proj.getCopyFiles());
386 }
387 } catch (GitAPIException | IOException e) {
388 throw new ManifestErrorException(e);
389 }
390 } finally {
391 try {
392 if (inputStream != null)
393 inputStream.close();
394 } catch (IOException e) {
395
396 }
397 }
398
399 if (repo.isBare()) {
400 DirCache index = DirCache.newInCore();
401 DirCacheBuilder builder = index.builder();
402 ObjectInserter inserter = repo.newObjectInserter();
403 try (RevWalk rw = new RevWalk(repo)) {
404 Config cfg = new Config();
405 for (RepoProject proj : bareProjects) {
406 String name = proj.getPath();
407 String nameUri = proj.getName();
408 cfg.setString("submodule", name, "path", name);
409 cfg.setString("submodule", name, "url", nameUri);
410
411 DirCacheEntry dcEntry = new DirCacheEntry(name);
412 ObjectId objectId;
413 if (ObjectId.isId(proj.getRevision()))
414 objectId = ObjectId.fromString(proj.getRevision());
415 else {
416 objectId = callback.sha1(nameUri, proj.getRevision());
417 }
418 if (objectId == null)
419 throw new RemoteUnavailableException(nameUri);
420 dcEntry.setObjectId(objectId);
421 dcEntry.setFileMode(FileMode.GITLINK);
422 builder.add(dcEntry);
423
424 for (CopyFile copyfile : proj.getCopyFiles()) {
425 byte[] src = callback.readFile(
426 nameUri, proj.getRevision(), copyfile.src);
427 objectId = inserter.insert(Constants.OBJ_BLOB, src);
428 dcEntry = new DirCacheEntry(copyfile.dest);
429 dcEntry.setObjectId(objectId);
430 dcEntry.setFileMode(FileMode.REGULAR_FILE);
431 builder.add(dcEntry);
432 }
433 }
434 String content = cfg.toText();
435
436
437 final DirCacheEntry dcEntry = new DirCacheEntry(Constants.DOT_GIT_MODULES);
438 ObjectId objectId = inserter.insert(Constants.OBJ_BLOB,
439 content.getBytes(Constants.CHARACTER_ENCODING));
440 dcEntry.setObjectId(objectId);
441 dcEntry.setFileMode(FileMode.REGULAR_FILE);
442 builder.add(dcEntry);
443
444 builder.finish();
445 ObjectId treeId = index.writeTree(inserter);
446
447
448 ObjectId headId = repo.resolve(Constants.HEAD + "^{commit}");
449 CommitBuilder commit = new CommitBuilder();
450 commit.setTreeId(treeId);
451 if (headId != null)
452 commit.setParentIds(headId);
453 commit.setAuthor(author);
454 commit.setCommitter(author);
455 commit.setMessage(RepoText.get().repoCommitMessage);
456
457 ObjectId commitId = inserter.insert(commit);
458 inserter.flush();
459
460 RefUpdate ru = repo.updateRef(Constants.HEAD);
461 ru.setNewObjectId(commitId);
462 ru.setExpectedOldObjectId(headId != null ? headId : ObjectId.zeroId());
463 Result rc = ru.update(rw);
464
465 switch (rc) {
466 case NEW:
467 case FORCED:
468 case FAST_FORWARD:
469
470 break;
471 case REJECTED:
472 case LOCK_FAILURE:
473 throw new ConcurrentRefUpdateException(
474 JGitText.get().couldNotLockHEAD, ru.getRef(),
475 rc);
476 default:
477 throw new JGitInternalException(MessageFormat.format(
478 JGitText.get().updatingRefFailed,
479 Constants.HEAD, commitId.name(), rc));
480 }
481
482 return rw.parseCommit(commitId);
483 } catch (IOException e) {
484 throw new ManifestErrorException(e);
485 }
486 } else {
487 return git
488 .commit()
489 .setMessage(RepoText.get().repoCommitMessage)
490 .call();
491 }
492 }
493
494 private void addSubmodule(String url, String name, String revision,
495 List<CopyFile> copyfiles) throws GitAPIException, IOException {
496 if (repo.isBare()) {
497 RepoProject proj = new RepoProject(url, name, revision, null, null);
498 proj.addCopyFiles(copyfiles);
499 bareProjects.add(proj);
500 } else {
501 SubmoduleAddCommand add = git
502 .submoduleAdd()
503 .setPath(name)
504 .setURI(url);
505 if (monitor != null)
506 add.setProgressMonitor(monitor);
507
508 Repository subRepo = add.call();
509 if (revision != null) {
510 try (Git sub = new Git(subRepo)) {
511 sub.checkout().setName(findRef(revision, subRepo))
512 .call();
513 }
514 subRepo.close();
515 git.add().addFilepattern(name).call();
516 }
517 for (CopyFile copyfile : copyfiles) {
518 copyfile.copy();
519 git.add().addFilepattern(copyfile.dest).call();
520 }
521 }
522 }
523
524 private static String findRef(String ref, Repository repo)
525 throws IOException {
526 if (!ObjectId.isId(ref)) {
527 Ref r = repo.getRef(Constants.DEFAULT_REMOTE_NAME + "/" + ref);
528 if (r != null)
529 return r.getName();
530 }
531 return ref;
532 }
533 }