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