View Javadoc
1   /*
2    * Copyright (C) 2014, Google Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.gitrepo;
11  
12  import static java.nio.charset.StandardCharsets.UTF_8;
13  import static org.junit.Assert.assertEquals;
14  import static org.junit.Assert.assertFalse;
15  import static org.junit.Assert.assertNull;
16  import static org.junit.Assert.assertTrue;
17  import static org.junit.Assert.fail;
18  
19  import java.io.BufferedReader;
20  import java.io.ByteArrayInputStream;
21  import java.io.File;
22  import java.io.IOException;
23  import java.net.URI;
24  import java.nio.file.Files;
25  import java.nio.file.Path;
26  import java.text.MessageFormat;
27  import java.util.HashMap;
28  import java.util.Map;
29  
30  import org.eclipse.jgit.api.Git;
31  import org.eclipse.jgit.api.errors.GitAPIException;
32  import org.eclipse.jgit.api.errors.InvalidRefNameException;
33  import org.eclipse.jgit.api.errors.InvalidRemoteException;
34  import org.eclipse.jgit.gitrepo.RepoCommand.RemoteFile;
35  import org.eclipse.jgit.internal.JGitText;
36  import org.eclipse.jgit.junit.JGitTestUtil;
37  import org.eclipse.jgit.junit.RepositoryTestCase;
38  import org.eclipse.jgit.lib.BlobBasedConfig;
39  import org.eclipse.jgit.lib.Config;
40  import org.eclipse.jgit.lib.Constants;
41  import org.eclipse.jgit.lib.ObjectId;
42  import org.eclipse.jgit.lib.ObjectReader;
43  import org.eclipse.jgit.lib.Ref;
44  import org.eclipse.jgit.lib.Repository;
45  import org.eclipse.jgit.revwalk.RevCommit;
46  import org.eclipse.jgit.storage.file.FileBasedConfig;
47  import org.eclipse.jgit.treewalk.TreeWalk;
48  import org.eclipse.jgit.util.FS;
49  import org.eclipse.jgit.util.IO;
50  import org.eclipse.jgit.util.RawParseUtils;
51  import org.junit.Test;
52  
53  public class RepoCommandTest extends RepositoryTestCase {
54  
55  	private static final String BRANCH = "branch";
56  	private static final String TAG = "release";
57  
58  	private Repository defaultDb;
59  	private Repository notDefaultDb;
60  	private Repository groupADb;
61  	private Repository groupBDb;
62  
63  	private String rootUri;
64  	private String defaultUri;
65  	private String notDefaultUri;
66  	private String groupAUri;
67  	private String groupBUri;
68  
69  	private ObjectId oldCommitId;
70  
71  	@Override
72  	public void setUp() throws Exception {
73  		super.setUp();
74  
75  		defaultDb = createWorkRepository();
76  		try (Git git = new Git(defaultDb)) {
77  			JGitTestUtil.writeTrashFile(defaultDb, "hello.txt", "branch world");
78  			git.add().addFilepattern("hello.txt").call();
79  			oldCommitId = git.commit().setMessage("Initial commit").call().getId();
80  			git.checkout().setName(BRANCH).setCreateBranch(true).call();
81  			git.checkout().setName("master").call();
82  			git.tag().setName(TAG).call();
83  			JGitTestUtil.writeTrashFile(defaultDb, "hello.txt", "master world");
84  			git.add().addFilepattern("hello.txt").call();
85  			git.commit().setMessage("Second commit").call();
86  			addRepoToClose(defaultDb);
87  		}
88  
89  		notDefaultDb = createWorkRepository();
90  		try (Git git = new Git(notDefaultDb)) {
91  			JGitTestUtil.writeTrashFile(notDefaultDb, "world.txt", "hello");
92  			git.add().addFilepattern("world.txt").call();
93  			git.commit().setMessage("Initial commit").call();
94  			addRepoToClose(notDefaultDb);
95  		}
96  
97  		groupADb = createWorkRepository();
98  		try (Git git = new Git(groupADb)) {
99  			JGitTestUtil.writeTrashFile(groupADb, "a.txt", "world");
100 			git.add().addFilepattern("a.txt").call();
101 			git.commit().setMessage("Initial commit").call();
102 			addRepoToClose(groupADb);
103 		}
104 
105 		groupBDb = createWorkRepository();
106 		try (Git git = new Git(groupBDb)) {
107 			JGitTestUtil.writeTrashFile(groupBDb, "b.txt", "world");
108 			git.add().addFilepattern("b.txt").call();
109 			git.commit().setMessage("Initial commit").call();
110 			addRepoToClose(groupBDb);
111 		}
112 
113 		resolveRelativeUris();
114 	}
115 
116 	static class IndexedRepos implements RepoCommand.RemoteReader {
117 		Map<String, Repository> uriRepoMap;
118 
119 		IndexedRepos() {
120 			uriRepoMap = new HashMap<>();
121 		}
122 
123 		void put(String u, Repository r) {
124 			uriRepoMap.put(u, r);
125 		}
126 
127 		@Override
128 		public ObjectId sha1(String uri, String refname) throws GitAPIException {
129 			if (!uriRepoMap.containsKey(uri)) {
130 				return null;
131 			}
132 
133 			Repository r = uriRepoMap.get(uri);
134 			try {
135 				Ref ref = r.findRef(refname);
136 				if (ref == null) return null;
137 
138 				ref = r.getRefDatabase().peel(ref);
139 				ObjectId id = ref.getObjectId();
140 				return id;
141 			} catch (IOException e) {
142 				throw new InvalidRemoteException("", e);
143 			}
144 		}
145 
146 		@Override
147 		public RemoteFile readFileWithMode(String uri, String ref, String path)
148 				throws GitAPIException, IOException {
149 			Repository repo = uriRepoMap.get(uri);
150 			ObjectId refCommitId = sha1(uri, ref);
151 			if (refCommitId == null) {
152 				throw new InvalidRefNameException(MessageFormat
153 						.format(JGitText.get().refNotResolved, ref));
154 			}
155 			RevCommit commit = repo.parseCommit(refCommitId);
156 			TreeWalk tw = TreeWalk.forPath(repo, path, commit.getTree());
157 
158 			// TODO(ifrade): Cope better with big files (e.g. using InputStream
159 			// instead of byte[])
160 			return new RemoteFile(tw.getObjectReader().open(tw.getObjectId(0))
161 					.getCachedBytes(Integer.MAX_VALUE), tw.getFileMode(0));
162 		}
163 	}
164 
165 	private Repository cloneRepository(Repository repo, boolean bare)
166 			throws Exception {
167 		Repository r = Git.cloneRepository()
168 				.setURI(repo.getDirectory().toURI().toString())
169 				.setDirectory(createUniqueTestGitDir(true)).setBare(bare).call()
170 				.getRepository();
171 		if (bare) {
172 			assertTrue(r.isBare());
173 		} else {
174 			assertFalse(r.isBare());
175 		}
176 		return r;
177 	}
178 
179 	private static void assertContents(Path path, String expected)
180 			throws IOException {
181 		try (BufferedReader reader = Files.newBufferedReader(path, UTF_8)) {
182 			String content = reader.readLine();
183 			assertEquals("Unexpected content in " + path.getFileName(),
184 					expected, content);
185 		}
186 	}
187 
188 	@Test
189 	public void runTwiceIsNOP() throws Exception {
190 		try (Repository child = cloneRepository(groupADb, true);
191 				Repository dest = cloneRepository(db, true)) {
192 			StringBuilder xmlContent = new StringBuilder();
193 			xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
194 					.append("<manifest>")
195 					.append("<remote name=\"remote1\" fetch=\"..\" />")
196 					.append("<default revision=\"master\" remote=\"remote1\" />")
197 					.append("<project path=\"base\" name=\"platform/base\" />")
198 					.append("</manifest>");
199 			RepoCommand cmd = new RepoCommand(dest);
200 
201 			IndexedRepos repos = new IndexedRepos();
202 			repos.put("platform/base", child);
203 
204 			RevCommit commit = cmd
205 					.setInputStream(new ByteArrayInputStream(
206 							xmlContent.toString().getBytes(UTF_8)))
207 					.setRemoteReader(repos).setURI("platform/")
208 					.setTargetURI("platform/superproject")
209 					.setRecordRemoteBranch(true).setRecordSubmoduleLabels(true)
210 					.call();
211 
212 			String firstIdStr = commit.getId().name() + ":" + ".gitmodules";
213 			commit = new RepoCommand(dest)
214 					.setInputStream(new ByteArrayInputStream(
215 							xmlContent.toString().getBytes(UTF_8)))
216 					.setRemoteReader(repos).setURI("platform/")
217 					.setTargetURI("platform/superproject")
218 					.setRecordRemoteBranch(true).setRecordSubmoduleLabels(true)
219 					.call();
220 			String idStr = commit.getId().name() + ":" + ".gitmodules";
221 			assertEquals(firstIdStr, idStr);
222 		}
223 	}
224 
225 	@Test
226 	public void androidSetup() throws Exception {
227 		try (Repository child = cloneRepository(groupADb, true);
228 				Repository dest = cloneRepository(db, true)) {
229 			StringBuilder xmlContent = new StringBuilder();
230 			xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
231 					.append("<manifest>")
232 					.append("<remote name=\"remote1\" fetch=\"..\" />")
233 					.append("<default revision=\"master\" remote=\"remote1\" />")
234 					.append("<project path=\"base\" name=\"platform/base\" />")
235 					.append("</manifest>");
236 			RepoCommand cmd = new RepoCommand(dest);
237 
238 			IndexedRepos repos = new IndexedRepos();
239 			repos.put("platform/base", child);
240 
241 			RevCommit commit = cmd
242 					.setInputStream(new ByteArrayInputStream(
243 							xmlContent.toString().getBytes(UTF_8)))
244 					.setRemoteReader(repos).setURI("platform/")
245 					.setTargetURI("platform/superproject")
246 					.setRecordRemoteBranch(true).setRecordSubmoduleLabels(true)
247 					.call();
248 
249 			String idStr = commit.getId().name() + ":" + ".gitmodules";
250 			ObjectId modId = dest.resolve(idStr);
251 
252 			try (ObjectReader reader = dest.newObjectReader()) {
253 				byte[] bytes = reader.open(modId)
254 						.getCachedBytes(Integer.MAX_VALUE);
255 				Config base = new Config();
256 				BlobBasedConfig cfg = new BlobBasedConfig(base, bytes);
257 				String subUrl = cfg.getString("submodule", "platform/base",
258 						"url");
259 				assertEquals(subUrl, "../base");
260 			}
261 		}
262 	}
263 
264 	@Test
265 	public void recordUnreachableRemotes() throws Exception {
266 		StringBuilder xmlContent = new StringBuilder();
267 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
268 			.append("<manifest>")
269 			.append("<remote name=\"remote1\" fetch=\"https://host.com/\" />")
270 			.append("<default revision=\"master\" remote=\"remote1\" />")
271 			.append("<project path=\"base\" name=\"platform/base\" />")
272 			.append("</manifest>");
273 
274 		try (Repository dest = cloneRepository(db, true)) {
275 			RevCommit commit = new RepoCommand(dest)
276 					.setInputStream(new ByteArrayInputStream(
277 							xmlContent.toString().getBytes(UTF_8)))
278 					.setRemoteReader(new IndexedRepos()).setURI("platform/")
279 					.setTargetURI("platform/superproject")
280 					.setRecordRemoteBranch(true).setIgnoreRemoteFailures(true)
281 					.setRecordSubmoduleLabels(true).call();
282 
283 			String idStr = commit.getId().name() + ":" + ".gitmodules";
284 			ObjectId modId = dest.resolve(idStr);
285 
286 			try (ObjectReader reader = dest.newObjectReader()) {
287 				byte[] bytes = reader.open(modId)
288 						.getCachedBytes(Integer.MAX_VALUE);
289 				Config base = new Config();
290 				BlobBasedConfig cfg = new BlobBasedConfig(base, bytes);
291 				String subUrl = cfg.getString("submodule", "platform/base",
292 						"url");
293 				assertEquals(subUrl, "https://host.com/platform/base");
294 			}
295 		}
296 	}
297 
298 	@Test
299 	public void gerritSetup() throws Exception {
300 		try (Repository child = cloneRepository(groupADb, true);
301 				Repository dest = cloneRepository(db, true)) {
302 			StringBuilder xmlContent = new StringBuilder();
303 			xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
304 					.append("<manifest>")
305 					.append("<remote name=\"remote1\" fetch=\".\" />")
306 					.append("<default revision=\"master\" remote=\"remote1\" />")
307 					.append("<project path=\"plugins/cookbook\" name=\"plugins/cookbook\" />")
308 					.append("</manifest>");
309 			RepoCommand cmd = new RepoCommand(dest);
310 
311 			IndexedRepos repos = new IndexedRepos();
312 			repos.put("plugins/cookbook", child);
313 
314 			RevCommit commit = cmd
315 					.setInputStream(new ByteArrayInputStream(
316 							xmlContent.toString().getBytes(UTF_8)))
317 					.setRemoteReader(repos).setURI("").setTargetURI("gerrit")
318 					.setRecordRemoteBranch(true).setRecordSubmoduleLabels(true)
319 					.call();
320 
321 			String idStr = commit.getId().name() + ":" + ".gitmodules";
322 			ObjectId modId = dest.resolve(idStr);
323 
324 			try (ObjectReader reader = dest.newObjectReader()) {
325 				byte[] bytes = reader.open(modId)
326 						.getCachedBytes(Integer.MAX_VALUE);
327 				Config base = new Config();
328 				BlobBasedConfig cfg = new BlobBasedConfig(base, bytes);
329 				String subUrl = cfg.getString("submodule", "plugins/cookbook",
330 						"url");
331 				assertEquals(subUrl, "../plugins/cookbook");
332 			}
333 		}
334 	}
335 
336 	@Test
337 	public void absoluteRemoteURL() throws Exception {
338 		try (Repository child = cloneRepository(groupADb, true);
339 				Repository dest = cloneRepository(db, true)) {
340 			String abs = "https://chromium.googlesource.com";
341 			String repoUrl = "https://chromium.googlesource.com/chromium/src";
342 			boolean fetchSlash = false;
343 			boolean baseSlash = false;
344 			do {
345 				do {
346 					String fetchUrl = fetchSlash ? abs + "/" : abs;
347 					String baseUrl = baseSlash ? abs + "/" : abs;
348 
349 					StringBuilder xmlContent = new StringBuilder();
350 					xmlContent.append(
351 							"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
352 							.append("<manifest>")
353 							.append("<remote name=\"origin\" fetch=\""
354 									+ fetchUrl + "\" />")
355 							.append("<default revision=\"master\" remote=\"origin\" />")
356 							.append("<project path=\"src\" name=\"chromium/src\" />")
357 							.append("</manifest>");
358 					RepoCommand cmd = new RepoCommand(dest);
359 
360 					IndexedRepos repos = new IndexedRepos();
361 					repos.put(repoUrl, child);
362 
363 					RevCommit commit = cmd
364 							.setInputStream(new ByteArrayInputStream(
365 									xmlContent.toString().getBytes(UTF_8)))
366 							.setRemoteReader(repos).setURI(baseUrl)
367 							.setTargetURI("gerrit").setRecordRemoteBranch(true)
368 							.setRecordSubmoduleLabels(true).call();
369 
370 					String idStr = commit.getId().name() + ":" + ".gitmodules";
371 					ObjectId modId = dest.resolve(idStr);
372 
373 					try (ObjectReader reader = dest.newObjectReader()) {
374 						byte[] bytes = reader.open(modId)
375 								.getCachedBytes(Integer.MAX_VALUE);
376 						Config base = new Config();
377 						BlobBasedConfig cfg = new BlobBasedConfig(base, bytes);
378 						String subUrl = cfg.getString("submodule",
379 								"chromium/src", "url");
380 						assertEquals(
381 								"https://chromium.googlesource.com/chromium/src",
382 								subUrl);
383 					}
384 					fetchSlash = !fetchSlash;
385 				} while (fetchSlash);
386 				baseSlash = !baseSlash;
387 			} while (baseSlash);
388 		}
389 	}
390 
391 	@Test
392 	public void absoluteRemoteURLAbsoluteTargetURL() throws Exception {
393 		try (Repository child = cloneRepository(groupADb, true);
394 				Repository dest = cloneRepository(db, true)) {
395 			String abs = "https://chromium.googlesource.com";
396 			String repoUrl = "https://chromium.googlesource.com/chromium/src";
397 			boolean fetchSlash = false;
398 			boolean baseSlash = false;
399 			do {
400 				do {
401 					String fetchUrl = fetchSlash ? abs + "/" : abs;
402 					String baseUrl = baseSlash ? abs + "/" : abs;
403 
404 					StringBuilder xmlContent = new StringBuilder();
405 					xmlContent.append(
406 							"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
407 							.append("<manifest>")
408 							.append("<remote name=\"origin\" fetch=\""
409 									+ fetchUrl + "\" />")
410 							.append("<default revision=\"master\" remote=\"origin\" />")
411 							.append("<project path=\"src\" name=\"chromium/src\" />")
412 							.append("</manifest>");
413 					RepoCommand cmd = new RepoCommand(dest);
414 
415 					IndexedRepos repos = new IndexedRepos();
416 					repos.put(repoUrl, child);
417 
418 					RevCommit commit = cmd
419 							.setInputStream(new ByteArrayInputStream(
420 									xmlContent.toString().getBytes(UTF_8)))
421 							.setRemoteReader(repos).setURI(baseUrl)
422 							.setTargetURI(abs + "/superproject")
423 							.setRecordRemoteBranch(true)
424 							.setRecordSubmoduleLabels(true).call();
425 
426 					String idStr = commit.getId().name() + ":" + ".gitmodules";
427 					ObjectId modId = dest.resolve(idStr);
428 
429 					try (ObjectReader reader = dest.newObjectReader()) {
430 						byte[] bytes = reader.open(modId)
431 								.getCachedBytes(Integer.MAX_VALUE);
432 						Config base = new Config();
433 						BlobBasedConfig cfg = new BlobBasedConfig(base, bytes);
434 						String subUrl = cfg.getString("submodule",
435 								"chromium/src", "url");
436 						assertEquals("../chromium/src", subUrl);
437 					}
438 					fetchSlash = !fetchSlash;
439 				} while (fetchSlash);
440 				baseSlash = !baseSlash;
441 			} while (baseSlash);
442 		}
443 	}
444 
445 	@Test
446 	public void testAddRepoManifest() throws Exception {
447 		StringBuilder xmlContent = new StringBuilder();
448 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
449 			.append("<manifest>")
450 			.append("<remote name=\"remote1\" fetch=\".\" />")
451 			.append("<default revision=\"master\" remote=\"remote1\" />")
452 			.append("<project path=\"foo\" name=\"")
453 			.append(defaultUri)
454 			.append("\" />")
455 			.append("</manifest>");
456 		writeTrashFile("manifest.xml", xmlContent.toString());
457 		RepoCommand command = new RepoCommand(db);
458 		command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml")
459 			.setURI(rootUri)
460 			.call();
461 		File hello = new File(db.getWorkTree(), "foo/hello.txt");
462 		assertTrue("submodule should be checked out", hello.exists());
463 		assertContents(hello.toPath(), "master world");
464 	}
465 
466 	@Test
467 	public void testRepoManifestGroups() throws Exception {
468 		StringBuilder xmlContent = new StringBuilder();
469 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
470 			.append("<manifest>")
471 			.append("<remote name=\"remote1\" fetch=\".\" />")
472 			.append("<default revision=\"master\" remote=\"remote1\" />")
473 			.append("<project path=\"foo\" name=\"")
474 			.append(defaultUri)
475 			.append("\" groups=\"a,test\" />")
476 			.append("<project path=\"bar\" name=\"")
477 			.append(notDefaultUri)
478 			.append("\" groups=\"notdefault\" />")
479 			.append("<project path=\"a\" name=\"")
480 			.append(groupAUri)
481 			.append("\" groups=\"a\" />")
482 			.append("<project path=\"b\" name=\"")
483 			.append(groupBUri)
484 			.append("\" groups=\"b\" />")
485 			.append("</manifest>");
486 
487 		// default should have foo, a & b
488 		Repository localDb = createWorkRepository();
489 		JGitTestUtil.writeTrashFile(
490 				localDb, "manifest.xml", xmlContent.toString());
491 		RepoCommand command = new RepoCommand(localDb);
492 		command
493 			.setPath(localDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
494 			.setURI(rootUri)
495 			.call();
496 		File file = new File(localDb.getWorkTree(), "foo/hello.txt");
497 		assertTrue("default should have foo", file.exists());
498 		file = new File(localDb.getWorkTree(), "bar/world.txt");
499 		assertFalse("default shouldn't have bar", file.exists());
500 		file = new File(localDb.getWorkTree(), "a/a.txt");
501 		assertTrue("default should have a", file.exists());
502 		file = new File(localDb.getWorkTree(), "b/b.txt");
503 		assertTrue("default should have b", file.exists());
504 
505 		// all,-a should have bar & b
506 		localDb = createWorkRepository();
507 		JGitTestUtil.writeTrashFile(
508 				localDb, "manifest.xml", xmlContent.toString());
509 		command = new RepoCommand(localDb);
510 		command
511 			.setPath(localDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
512 			.setURI(rootUri)
513 			.setGroups("all,-a")
514 			.call();
515 		file = new File(localDb.getWorkTree(), "foo/hello.txt");
516 		assertFalse("\"all,-a\" shouldn't have foo", file.exists());
517 		file = new File(localDb.getWorkTree(), "bar/world.txt");
518 		assertTrue("\"all,-a\" should have bar", file.exists());
519 		file = new File(localDb.getWorkTree(), "a/a.txt");
520 		assertFalse("\"all,-a\" shuoldn't have a", file.exists());
521 		file = new File(localDb.getWorkTree(), "b/b.txt");
522 		assertTrue("\"all,-a\" should have b", file.exists());
523 	}
524 
525 	@Test
526 	public void testRepoManifestCopyFile() throws Exception {
527 		Repository localDb = createWorkRepository();
528 		StringBuilder xmlContent = new StringBuilder();
529 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
530 			.append("<manifest>")
531 			.append("<remote name=\"remote1\" fetch=\".\" />")
532 			.append("<default revision=\"master\" remote=\"remote1\" />")
533 			.append("<project path=\"foo\" name=\"")
534 			.append(defaultUri)
535 			.append("\">")
536 			.append("<copyfile src=\"hello.txt\" dest=\"Hello\" />")
537 			.append("</project>")
538 			.append("</manifest>");
539 		JGitTestUtil.writeTrashFile(
540 				localDb, "manifest.xml", xmlContent.toString());
541 		RepoCommand command = new RepoCommand(localDb);
542 		command
543 			.setPath(localDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
544 			.setURI(rootUri)
545 			.call();
546 		// The original file should exist
547 		File hello = new File(localDb.getWorkTree(), "foo/hello.txt");
548 		assertTrue("The original file should exist", hello.exists());
549 		assertFalse("The original file should not be executable",
550 				hello.canExecute());
551 		assertContents(hello.toPath(), "master world");
552 		// The dest file should also exist
553 		hello = new File(localDb.getWorkTree(), "Hello");
554 		assertTrue("The destination file should exist", hello.exists());
555 		assertFalse("The destination file should not be executable",
556 				hello.canExecute());
557 		assertContents(hello.toPath(), "master world");
558 	}
559 
560 	@Test
561 	public void testRepoManifestCopyFile_executable() throws Exception {
562 		try (Git git = new Git(defaultDb)) {
563 			git.checkout().setName("master").call();
564 			File f = JGitTestUtil.writeTrashFile(defaultDb, "hello.sh",
565 					"content of the executable file");
566 			f.setExecutable(true);
567 			git.add().addFilepattern("hello.sh").call();
568 			git.commit().setMessage("Add binary file").call();
569 		}
570 
571 		Repository localDb = createWorkRepository();
572 		StringBuilder xmlContent = new StringBuilder();
573 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
574 				.append("<manifest>")
575 				.append("<remote name=\"remote1\" fetch=\".\" />")
576 				.append("<default revision=\"master\" remote=\"remote1\" />")
577 				.append("<project path=\"foo\" name=\"").append(defaultUri)
578 				.append("\">")
579 				.append("<copyfile src=\"hello.sh\" dest=\"copy-hello.sh\" />")
580 				.append("</project>").append("</manifest>");
581 		JGitTestUtil.writeTrashFile(localDb, "manifest.xml",
582 				xmlContent.toString());
583 		RepoCommand command = new RepoCommand(localDb);
584 		command.setPath(
585 				localDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
586 				.setURI(rootUri).call();
587 
588 		// The original file should exist and be an executable
589 		File hello = new File(localDb.getWorkTree(), "foo/hello.sh");
590 		assertTrue("The original file should exist", hello.exists());
591 		assertTrue("The original file must be executable", hello.canExecute());
592 		try (BufferedReader reader = Files.newBufferedReader(hello.toPath(),
593 				UTF_8)) {
594 			String content = reader.readLine();
595 			assertEquals("The original file should have expected content",
596 					"content of the executable file", content);
597 		}
598 
599 		// The destination file should also exist and be an executable
600 		hello = new File(localDb.getWorkTree(), "copy-hello.sh");
601 		assertTrue("The destination file should exist", hello.exists());
602 		assertTrue("The destination file must be executable",
603 				hello.canExecute());
604 		try (BufferedReader reader = Files.newBufferedReader(hello.toPath(),
605 				UTF_8)) {
606 			String content = reader.readLine();
607 			assertEquals("The destination file should have expected content",
608 					"content of the executable file", content);
609 		}
610 	}
611 
612 	@Test
613 	public void testBareRepo() throws Exception {
614 		Repository remoteDb = createBareRepository();
615 		Repository tempDb = createWorkRepository();
616 
617 		StringBuilder xmlContent = new StringBuilder();
618 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
619 				.append("<manifest>")
620 				.append("<remote name=\"remote1\" fetch=\".\" />")
621 				.append("<default revision=\"master\" remote=\"remote1\" />")
622 				.append("<project path=\"foo\" name=\"").append(defaultUri)
623 				.append("\" />").append("</manifest>");
624 		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
625 				xmlContent.toString());
626 		RepoCommand command = new RepoCommand(remoteDb);
627 		command.setPath(
628 				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
629 				.setURI(rootUri).call();
630 		// Clone it
631 		File directory = createTempDirectory("testBareRepo");
632 		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
633 				.setURI(remoteDb.getDirectory().toURI().toString()).call()
634 				.getRepository()) {
635 			// The .gitmodules file should exist
636 			File gitmodules = new File(localDb.getWorkTree(), ".gitmodules");
637 			assertTrue("The .gitmodules file should exist",
638 					gitmodules.exists());
639 			// The first line of .gitmodules file should be expected
640 			try (BufferedReader reader = Files
641 					.newBufferedReader(gitmodules.toPath(), UTF_8)) {
642 				String content = reader.readLine();
643 				assertEquals(
644 						"The first line of .gitmodules file should be as expected",
645 						"[submodule \"" + defaultUri + "\"]", content);
646 			}
647 			// The gitlink should be the same as remote head sha1
648 			String gitlink = localDb.resolve(Constants.HEAD + ":foo").name();
649 			String remote = defaultDb.resolve(Constants.HEAD).name();
650 			assertEquals("The gitlink should be the same as remote head",
651 					remote, gitlink);
652 		}
653 	}
654 
655 	@Test
656 	public void testRevision() throws Exception {
657 		StringBuilder xmlContent = new StringBuilder();
658 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
659 			.append("<manifest>")
660 			.append("<remote name=\"remote1\" fetch=\".\" />")
661 			.append("<default revision=\"master\" remote=\"remote1\" />")
662 			.append("<project path=\"foo\" name=\"")
663 			.append(defaultUri)
664 			.append("\" revision=\"")
665 			.append(oldCommitId.name())
666 			.append("\" />")
667 			.append("</manifest>");
668 		writeTrashFile("manifest.xml", xmlContent.toString());
669 		RepoCommand command = new RepoCommand(db);
670 		command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml")
671 			.setURI(rootUri)
672 			.call();
673 		File hello = new File(db.getWorkTree(), "foo/hello.txt");
674 		try (BufferedReader reader = Files.newBufferedReader(hello.toPath(),
675 				UTF_8)) {
676 			String content = reader.readLine();
677 			assertEquals("submodule content should be as expected",
678 					"branch world", content);
679 		}
680 	}
681 
682 	@Test
683 	public void testRevisionBranch() throws Exception {
684 		StringBuilder xmlContent = new StringBuilder();
685 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
686 			.append("<manifest>")
687 			.append("<remote name=\"remote1\" fetch=\".\" />")
688 			.append("<default revision=\"")
689 			.append(BRANCH)
690 			.append("\" remote=\"remote1\" />")
691 			.append("<project path=\"foo\" name=\"")
692 			.append(defaultUri)
693 			.append("\" />")
694 			.append("</manifest>");
695 		writeTrashFile("manifest.xml", xmlContent.toString());
696 		RepoCommand command = new RepoCommand(db);
697 		command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml")
698 			.setURI(rootUri)
699 			.call();
700 		File hello = new File(db.getWorkTree(), "foo/hello.txt");
701 		assertContents(hello.toPath(), "branch world");
702 	}
703 
704 	@Test
705 	public void testRevisionTag() throws Exception {
706 		StringBuilder xmlContent = new StringBuilder();
707 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
708 			.append("<manifest>")
709 			.append("<remote name=\"remote1\" fetch=\".\" />")
710 			.append("<default revision=\"master\" remote=\"remote1\" />")
711 			.append("<project path=\"foo\" name=\"")
712 			.append(defaultUri)
713 			.append("\" revision=\"")
714 			.append(TAG)
715 			.append("\" />")
716 			.append("</manifest>");
717 		writeTrashFile("manifest.xml", xmlContent.toString());
718 		RepoCommand command = new RepoCommand(db);
719 		command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml")
720 			.setURI(rootUri)
721 			.call();
722 		File hello = new File(db.getWorkTree(), "foo/hello.txt");
723 		assertContents(hello.toPath(), "branch world");
724 	}
725 
726 	@Test
727 	public void testRevisionBare() throws Exception {
728 		Repository remoteDb = createBareRepository();
729 		Repository tempDb = createWorkRepository();
730 
731 		StringBuilder xmlContent = new StringBuilder();
732 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
733 				.append("<manifest>")
734 				.append("<remote name=\"remote1\" fetch=\".\" />")
735 				.append("<default revision=\"").append(BRANCH)
736 				.append("\" remote=\"remote1\" />")
737 				.append("<project path=\"foo\" name=\"").append(defaultUri)
738 				.append("\" />").append("</manifest>");
739 		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
740 				xmlContent.toString());
741 		RepoCommand command = new RepoCommand(remoteDb);
742 		command.setPath(
743 				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
744 				.setURI(rootUri).call();
745 		// Clone it
746 		File directory = createTempDirectory("testRevisionBare");
747 		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
748 				.setURI(remoteDb.getDirectory().toURI().toString()).call()
749 				.getRepository()) {
750 			// The gitlink should be the same as oldCommitId
751 			String gitlink = localDb.resolve(Constants.HEAD + ":foo").name();
752 			assertEquals("The gitlink is same as remote head",
753 					oldCommitId.name(), gitlink);
754 
755 			File dotmodules = new File(localDb.getWorkTree(),
756 					Constants.DOT_GIT_MODULES);
757 			assertTrue(dotmodules.exists());
758 			// The .gitmodules file should have "branch" lines
759 			String gitModulesContents = RawParseUtils
760 					.decode(IO.readFully(dotmodules));
761 			assertTrue(gitModulesContents.contains("branch = branch"));
762 		}
763 	}
764 
765 	@Test
766 	public void testRevisionBare_ignoreTags() throws Exception {
767 		Repository remoteDb = createBareRepository();
768 		Repository tempDb = createWorkRepository();
769 
770 		StringBuilder xmlContent = new StringBuilder();
771 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
772 				.append("<manifest>")
773 				.append("<remote name=\"remote1\" fetch=\".\" />")
774 				.append("<default revision=\"").append("refs/tags/" + TAG)
775 				.append("\" remote=\"remote1\" />")
776 				.append("<project path=\"foo\" name=\"")
777 				.append(defaultUri)
778 				.append("\" />").append("</manifest>");
779 		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
780 				xmlContent.toString());
781 		RepoCommand command = new RepoCommand(remoteDb);
782 		command.setPath(
783 				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
784 				.setURI(rootUri).call();
785 		// Clone it
786 		File directory = createTempDirectory("testReplaceManifestBare");
787 		File dotmodules;
788 		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
789 				.setURI(remoteDb.getDirectory().toURI().toString()).call()
790 				.getRepository()) {
791 			dotmodules = new File(localDb.getWorkTree(),
792 					Constants.DOT_GIT_MODULES);
793 			assertTrue(dotmodules.exists());
794 		}
795 
796 		// The .gitmodules file should not have "branch" lines
797 		String gitModulesContents = RawParseUtils
798 				.decode(IO.readFully(dotmodules));
799 		assertFalse(gitModulesContents.contains("branch"));
800 		assertTrue(gitModulesContents.contains("ref = refs/tags/" + TAG));
801 	}
802 
803 	@Test
804 	public void testCopyFileBare() throws Exception {
805 		Repository remoteDb = createBareRepository();
806 		Repository tempDb = createWorkRepository();
807 
808 		StringBuilder xmlContent = new StringBuilder();
809 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
810 				.append("<manifest>")
811 				.append("<remote name=\"remote1\" fetch=\".\" />")
812 				.append("<default revision=\"master\" remote=\"remote1\" />")
813 				.append("<project path=\"foo\" name=\"").append(defaultUri)
814 				.append("\" revision=\"").append(BRANCH).append("\" >")
815 				.append("<copyfile src=\"hello.txt\" dest=\"Hello\" />")
816 				.append("<copyfile src=\"hello.txt\" dest=\"foo/Hello\" />")
817 				.append("</project>").append("</manifest>");
818 		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
819 				xmlContent.toString());
820 		RepoCommand command = new RepoCommand(remoteDb);
821 		command.setPath(
822 				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
823 				.setURI(rootUri).call();
824 		// Clone it
825 		File directory = createTempDirectory("testCopyFileBare");
826 		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
827 				.setURI(remoteDb.getDirectory().toURI().toString()).call()
828 				.getRepository()) {
829 			// The Hello file should exist
830 			File hello = new File(localDb.getWorkTree(), "Hello");
831 			assertTrue("The Hello file should exist", hello.exists());
832 			// The foo/Hello file should be skipped.
833 			File foohello = new File(localDb.getWorkTree(), "foo/Hello");
834 			assertFalse("The foo/Hello file should be skipped",
835 					foohello.exists());
836 			// The content of Hello file should be expected
837 			assertContents(hello.toPath(), "branch world");
838 		}
839 	}
840 
841 	@Test
842 	public void testCopyFileBare_executable() throws Exception {
843 		try (Git git = new Git(defaultDb)) {
844 			git.checkout().setName(BRANCH).call();
845 			File f = JGitTestUtil.writeTrashFile(defaultDb, "hello.sh",
846 					"content of the executable file");
847 			f.setExecutable(true);
848 			git.add().addFilepattern("hello.sh").call();
849 			git.commit().setMessage("Add binary file").call();
850 		}
851 
852 		Repository remoteDb = createBareRepository();
853 		Repository tempDb = createWorkRepository();
854 
855 		StringBuilder xmlContent = new StringBuilder();
856 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
857 				.append("<manifest>")
858 				.append("<remote name=\"remote1\" fetch=\".\" />")
859 				.append("<default revision=\"master\" remote=\"remote1\" />")
860 				.append("<project path=\"foo\" name=\"").append(defaultUri)
861 				.append("\" revision=\"").append(BRANCH)
862 				.append("\" >")
863 				.append("<copyfile src=\"hello.txt\" dest=\"Hello\" />")
864 				.append("<copyfile src=\"hello.txt\" dest=\"foo/Hello\" />")
865 				.append("<copyfile src=\"hello.sh\" dest=\"copy-hello.sh\" />")
866 				.append("</project>").append("</manifest>");
867 		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
868 				xmlContent.toString());
869 		RepoCommand command = new RepoCommand(remoteDb);
870 		command.setPath(
871 				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
872 				.setURI(rootUri).call();
873 		// Clone it
874 		File directory = createTempDirectory("testCopyFileBare");
875 		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
876 				.setURI(remoteDb.getDirectory().toURI().toString()).call()
877 				.getRepository()) {
878 			// The Hello file should exist
879 			File hello = new File(localDb.getWorkTree(), "Hello");
880 			assertTrue("The Hello file should exist", hello.exists());
881 			// The foo/Hello file should be skipped.
882 			File foohello = new File(localDb.getWorkTree(), "foo/Hello");
883 			assertFalse("The foo/Hello file should be skipped",
884 					foohello.exists());
885 			// The content of Hello file should be expected
886 			try (BufferedReader reader = Files.newBufferedReader(hello.toPath(),
887 					UTF_8)) {
888 				String content = reader.readLine();
889 				assertEquals("The Hello file should have expected content",
890 						"branch world", content);
891 			}
892 
893 			// The executable file must be there and preserve the executable bit
894 			File helloSh = new File(localDb.getWorkTree(), "copy-hello.sh");
895 			assertTrue("Destination file should exist", helloSh.exists());
896 			assertContents(helloSh.toPath(), "content of the executable file");
897 			assertTrue("Destination file should be executable",
898 					helloSh.canExecute());
899 
900 		}
901 	}
902 
903 	@Test
904 	public void testReplaceManifestBare() throws Exception {
905 		Repository remoteDb = createBareRepository();
906 		Repository tempDb = createWorkRepository();
907 
908 		StringBuilder xmlContent = new StringBuilder();
909 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
910 				.append("<manifest>")
911 				.append("<remote name=\"remote1\" fetch=\".\" />")
912 				.append("<default revision=\"master\" remote=\"remote1\" />")
913 				.append("<project path=\"foo\" name=\"").append(defaultUri)
914 				.append("\" revision=\"").append(BRANCH).append("\" >")
915 				.append("<copyfile src=\"hello.txt\" dest=\"Hello\" />")
916 				.append("</project>").append("</manifest>");
917 		JGitTestUtil.writeTrashFile(tempDb, "old.xml", xmlContent.toString());
918 		RepoCommand command = new RepoCommand(remoteDb);
919 		command.setPath(tempDb.getWorkTree().getAbsolutePath() + "/old.xml")
920 				.setURI(rootUri).call();
921 		xmlContent = new StringBuilder();
922 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
923 				.append("<manifest>")
924 				.append("<remote name=\"remote1\" fetch=\".\" />")
925 				.append("<default revision=\"master\" remote=\"remote1\" />")
926 				.append("<project path=\"bar\" name=\"").append(notDefaultUri)
927 				.append("\" >")
928 				.append("<copyfile src=\"world.txt\" dest=\"World.txt\" />")
929 				.append("</project>").append("</manifest>");
930 		JGitTestUtil.writeTrashFile(tempDb, "new.xml", xmlContent.toString());
931 		command = new RepoCommand(remoteDb);
932 		command.setPath(tempDb.getWorkTree().getAbsolutePath() + "/new.xml")
933 				.setURI(rootUri).call();
934 		// Clone it
935 		File directory = createTempDirectory("testReplaceManifestBare");
936 		File dotmodules;
937 		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
938 				.setURI(remoteDb.getDirectory().toURI().toString()).call()
939 				.getRepository()) {
940 			// The Hello file should not exist
941 			File hello = new File(localDb.getWorkTree(), "Hello");
942 			assertFalse("The Hello file shouldn't exist", hello.exists());
943 			// The Hello.txt file should exist
944 			File hellotxt = new File(localDb.getWorkTree(), "World.txt");
945 			assertTrue("The World.txt file should exist", hellotxt.exists());
946 			dotmodules = new File(localDb.getWorkTree(),
947 					Constants.DOT_GIT_MODULES);
948 		}
949 		// The .gitmodules file should have 'submodule "bar"' and shouldn't
950 		// have
951 		// 'submodule "foo"' lines.
952 		try (BufferedReader reader = Files
953 				.newBufferedReader(dotmodules.toPath(), UTF_8)) {
954 			boolean foo = false;
955 			boolean bar = false;
956 			while (true) {
957 				String line = reader.readLine();
958 				if (line == null)
959 					break;
960 				if (line.contains("submodule \"" + defaultUri + "\""))
961 					foo = true;
962 				if (line.contains("submodule \"" + notDefaultUri + "\""))
963 					bar = true;
964 			}
965 			assertTrue("The bar submodule should exist", bar);
966 			assertFalse("The foo submodule shouldn't exist", foo);
967 		}
968 	}
969 
970 	@Test
971 	public void testRemoveOverlappingBare() throws Exception {
972 		Repository remoteDb = createBareRepository();
973 		Repository tempDb = createWorkRepository();
974 
975 		StringBuilder xmlContent = new StringBuilder();
976 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
977 				.append("<manifest>")
978 				.append("<remote name=\"remote1\" fetch=\".\" />")
979 				.append("<default revision=\"master\" remote=\"remote1\" />")
980 				.append("<project path=\"foo/bar\" name=\"").append(groupBUri)
981 				.append("\" />").append("<project path=\"a\" name=\"")
982 				.append(groupAUri).append("\" />")
983 				.append("<project path=\"foo\" name=\"").append(defaultUri)
984 				.append("\" />").append("</manifest>");
985 		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
986 				xmlContent.toString());
987 		RepoCommand command = new RepoCommand(remoteDb);
988 		command.setPath(
989 				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
990 				.setURI(rootUri).call();
991 		// Clone it
992 		File directory = createTempDirectory("testRemoveOverlappingBare");
993 		File dotmodules;
994 		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
995 				.setURI(remoteDb.getDirectory().toURI().toString()).call()
996 				.getRepository()) {
997 			dotmodules = new File(localDb.getWorkTree(),
998 				Constants.DOT_GIT_MODULES);
999 		}
1000 
1001 		// Check .gitmodules file
1002 		try (BufferedReader reader = Files
1003 				.newBufferedReader(dotmodules.toPath(), UTF_8)) {
1004 			boolean foo = false;
1005 			boolean foobar = false;
1006 			boolean a = false;
1007 			while (true) {
1008 				String line = reader.readLine();
1009 				if (line == null)
1010 					break;
1011 				if (line.contains("submodule \"" + defaultUri + "\""))
1012 					foo = true;
1013 				if (line.contains("submodule \"" + groupBUri + "\""))
1014 					foobar = true;
1015 				if (line.contains("submodule \"" + groupAUri + "\""))
1016 					a = true;
1017 			}
1018 			assertTrue("The " + defaultUri + " submodule should exist", foo);
1019 			assertFalse("The " + groupBUri + " submodule shouldn't exist",
1020 					foobar);
1021 			assertTrue("The " + groupAUri + " submodule should exist", a);
1022 		}
1023 	}
1024 
1025 	@Test
1026 	public void testIncludeTag() throws Exception {
1027 		Repository localDb = createWorkRepository();
1028 		Repository tempDb = createWorkRepository();
1029 
1030 		StringBuilder xmlContent = new StringBuilder();
1031 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
1032 			.append("<manifest>")
1033 			.append("<include name=\"_include.xml\" />")
1034 			.append("<default revision=\"master\" remote=\"remote1\" />")
1035 			.append("</manifest>");
1036 		JGitTestUtil.writeTrashFile(
1037 				tempDb, "manifest.xml", xmlContent.toString());
1038 
1039 		xmlContent = new StringBuilder();
1040 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
1041 			.append("<manifest>")
1042 			.append("<remote name=\"remote1\" fetch=\".\" />")
1043 			.append("<default revision=\"master\" remote=\"remote1\" />")
1044 			.append("<project path=\"foo\" name=\"")
1045 			.append(defaultUri)
1046 			.append("\" />")
1047 			.append("</manifest>");
1048 		JGitTestUtil.writeTrashFile(
1049 				tempDb, "_include.xml", xmlContent.toString());
1050 
1051 		RepoCommand command = new RepoCommand(localDb);
1052 		command
1053 			.setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
1054 			.setURI(rootUri)
1055 			.call();
1056 		File hello = new File(localDb.getWorkTree(), "foo/hello.txt");
1057 		assertTrue("submodule should be checked out", hello.exists());
1058 		try (BufferedReader reader = Files.newBufferedReader(hello.toPath(),
1059 				UTF_8)) {
1060 			String content = reader.readLine();
1061 			assertEquals("submodule content should be as expected",
1062 					"master world", content);
1063 		}
1064 	}
1065 	@Test
1066 	public void testRemoteAlias() throws Exception {
1067 		StringBuilder xmlContent = new StringBuilder();
1068 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
1069 			.append("<manifest>")
1070 			.append("<remote name=\"remote1\" fetch=\".\" alias=\"remote2\" />")
1071 			.append("<default revision=\"master\" remote=\"remote2\" />")
1072 			.append("<project path=\"foo\" name=\"")
1073 			.append(defaultUri)
1074 			.append("\" />")
1075 			.append("</manifest>");
1076 
1077 		Repository localDb = createWorkRepository();
1078 		JGitTestUtil.writeTrashFile(
1079 				localDb, "manifest.xml", xmlContent.toString());
1080 		RepoCommand command = new RepoCommand(localDb);
1081 		command
1082 			.setPath(localDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
1083 			.setURI(rootUri)
1084 			.call();
1085 		File file = new File(localDb.getWorkTree(), "foo/hello.txt");
1086 		assertTrue("We should have foo", file.exists());
1087 	}
1088 
1089 	@Test
1090 	public void testTargetBranch() throws Exception {
1091 		Repository remoteDb1 = createBareRepository();
1092 		Repository remoteDb2 = createBareRepository();
1093 		Repository tempDb = createWorkRepository();
1094 
1095 		StringBuilder xmlContent = new StringBuilder();
1096 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
1097 				.append("<manifest>")
1098 				.append("<remote name=\"remote1\" fetch=\".\" />")
1099 				.append("<default revision=\"master\" remote=\"remote1\" />")
1100 				.append("<project path=\"foo\" name=\"").append(defaultUri)
1101 				.append("\" />").append("</manifest>");
1102 		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
1103 				xmlContent.toString());
1104 		RepoCommand command = new RepoCommand(remoteDb1);
1105 		command.setPath(
1106 				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
1107 				.setURI(rootUri).setTargetBranch("test").call();
1108 		ObjectId branchId = remoteDb1
1109 				.resolve(Constants.R_HEADS + "test^{tree}");
1110 		command = new RepoCommand(remoteDb2);
1111 		command.setPath(
1112 				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
1113 				.setURI(rootUri).call();
1114 		ObjectId defaultId = remoteDb2.resolve(Constants.HEAD + "^{tree}");
1115 		assertEquals(
1116 				"The tree id of branch db and default db should be the same",
1117 				branchId, defaultId);
1118 	}
1119 
1120 	@Test
1121 	public void testRecordRemoteBranch() throws Exception {
1122 		Repository remoteDb = createBareRepository();
1123 		Repository tempDb = createWorkRepository();
1124 
1125 		StringBuilder xmlContent = new StringBuilder();
1126 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
1127 				.append("<manifest>")
1128 				.append("<remote name=\"remote1\" fetch=\".\" />")
1129 				.append("<default revision=\"master\" remote=\"remote1\" />")
1130 				.append("<project path=\"with-branch\" ")
1131 				.append("revision=\"master\" ").append("name=\"")
1132 				.append(notDefaultUri).append("\" />")
1133 				.append("<project path=\"with-long-branch\" ")
1134 				.append("revision=\"refs/heads/master\" ").append("name=\"")
1135 				.append(defaultUri).append("\" />").append("</manifest>");
1136 		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
1137 				xmlContent.toString());
1138 
1139 		RepoCommand command = new RepoCommand(remoteDb);
1140 		command.setPath(
1141 				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
1142 				.setURI(rootUri).setRecordRemoteBranch(true).call();
1143 		// Clone it
1144 		File directory = createTempDirectory("testBareRepo");
1145 		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
1146 				.setURI(remoteDb.getDirectory().toURI().toString()).call()
1147 				.getRepository();) {
1148 			// The .gitmodules file should exist
1149 			File gitmodules = new File(localDb.getWorkTree(), ".gitmodules");
1150 			assertTrue("The .gitmodules file should exist",
1151 					gitmodules.exists());
1152 			FileBasedConfig c = new FileBasedConfig(gitmodules, FS.DETECTED);
1153 			c.load();
1154 			assertEquals(
1155 					"Recording remote branches should work for short branch descriptions",
1156 					"master",
1157 					c.getString("submodule", notDefaultUri, "branch"));
1158 			assertEquals(
1159 					"Recording remote branches should work for full ref specs",
1160 					"refs/heads/master",
1161 					c.getString("submodule", defaultUri, "branch"));
1162 		}
1163 	}
1164 
1165 
1166 	@Test
1167 	public void testRecordSubmoduleLabels() throws Exception {
1168 		Repository remoteDb = createBareRepository();
1169 		Repository tempDb = createWorkRepository();
1170 
1171 		StringBuilder xmlContent = new StringBuilder();
1172 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
1173 				.append("<manifest>")
1174 				.append("<remote name=\"remote1\" fetch=\".\" />")
1175 				.append("<default revision=\"master\" remote=\"remote1\" />")
1176 				.append("<project path=\"test\" ")
1177 				.append("revision=\"master\" ").append("name=\"")
1178 				.append(notDefaultUri).append("\" ")
1179 				.append("groups=\"a1,a2\" />").append("</manifest>");
1180 		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
1181 				xmlContent.toString());
1182 
1183 		RepoCommand command = new RepoCommand(remoteDb);
1184 		command.setPath(
1185 				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
1186 				.setURI(rootUri).setRecordSubmoduleLabels(true).call();
1187 		// Clone it
1188 		File directory = createTempDirectory("testBareRepo");
1189 		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
1190 				.setURI(remoteDb.getDirectory().toURI().toString()).call()
1191 				.getRepository();) {
1192 			// The .gitattributes file should exist
1193 			File gitattributes = new File(localDb.getWorkTree(),
1194 					".gitattributes");
1195 			assertTrue("The .gitattributes file should exist",
1196 					gitattributes.exists());
1197 			try (BufferedReader reader = Files
1198 					.newBufferedReader(gitattributes.toPath(),
1199 					UTF_8)) {
1200 				String content = reader.readLine();
1201 				assertEquals(".gitattributes content should be as expected",
1202 						"/test a1 a2", content);
1203 			}
1204 		}
1205 	}
1206 
1207 	@Test
1208 	public void testRecordShallowRecommendation() throws Exception {
1209 		Repository remoteDb = createBareRepository();
1210 		Repository tempDb = createWorkRepository();
1211 
1212 		StringBuilder xmlContent = new StringBuilder();
1213 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
1214 				.append("<manifest>")
1215 				.append("<remote name=\"remote1\" fetch=\".\" />")
1216 				.append("<default revision=\"master\" remote=\"remote1\" />")
1217 				.append("<project path=\"shallow-please\" ").append("name=\"")
1218 				.append(defaultUri).append("\" ").append("clone-depth=\"1\" />")
1219 				.append("<project path=\"non-shallow\" ").append("name=\"")
1220 				.append(notDefaultUri).append("\" />").append("</manifest>");
1221 		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
1222 				xmlContent.toString());
1223 
1224 		RepoCommand command = new RepoCommand(remoteDb);
1225 		command.setPath(
1226 				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
1227 				.setURI(rootUri).setRecommendShallow(true).call();
1228 		// Clone it
1229 		File directory = createTempDirectory("testBareRepo");
1230 		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
1231 				.setURI(remoteDb.getDirectory().toURI().toString()).call()
1232 				.getRepository();) {
1233 			// The .gitmodules file should exist
1234 			File gitmodules = new File(localDb.getWorkTree(), ".gitmodules");
1235 			assertTrue("The .gitmodules file should exist",
1236 					gitmodules.exists());
1237 			FileBasedConfig c = new FileBasedConfig(gitmodules, FS.DETECTED);
1238 			c.load();
1239 			assertEquals("Recording shallow configuration should work", "true",
1240 					c.getString("submodule", defaultUri, "shallow"));
1241 			assertNull("Recording non shallow configuration should work",
1242 					c.getString("submodule", notDefaultUri, "shallow"));
1243 		}
1244 	}
1245 
1246 	@Test
1247 	public void testRemoteRevision() throws Exception {
1248 		StringBuilder xmlContent = new StringBuilder();
1249 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
1250 			.append("<manifest>")
1251 			.append("<remote name=\"remote1\" fetch=\".\" />")
1252 			.append("<remote name=\"remote2\" fetch=\".\" revision=\"")
1253 			.append(BRANCH)
1254 			.append("\" />")
1255 			.append("<default remote=\"remote1\" revision=\"master\" />")
1256 			.append("<project path=\"foo\" remote=\"remote2\" name=\"")
1257 			.append(defaultUri)
1258 			.append("\" />")
1259 			.append("</manifest>");
1260 		writeTrashFile("manifest.xml", xmlContent.toString());
1261 		RepoCommand command = new RepoCommand(db);
1262 		command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml")
1263 			.setURI(rootUri)
1264 			.call();
1265 		File hello = new File(db.getWorkTree(), "foo/hello.txt");
1266 		assertContents(hello.toPath(), "branch world");
1267 	}
1268 
1269 	@Test
1270 	public void testDefaultRemoteRevision() throws Exception {
1271 		StringBuilder xmlContent = new StringBuilder();
1272 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
1273 			.append("<manifest>")
1274 			.append("<remote name=\"remote1\" fetch=\".\" revision=\"")
1275 			.append(BRANCH)
1276 			.append("\" />")
1277 			.append("<default remote=\"remote1\" />")
1278 			.append("<project path=\"foo\" name=\"")
1279 			.append(defaultUri)
1280 			.append("\" />")
1281 			.append("</manifest>");
1282 		writeTrashFile("manifest.xml", xmlContent.toString());
1283 		RepoCommand command = new RepoCommand(db);
1284 		command.setPath(db.getWorkTree().getAbsolutePath() + "/manifest.xml")
1285 			.setURI(rootUri)
1286 			.call();
1287 		File hello = new File(db.getWorkTree(), "foo/hello.txt");
1288 		try (BufferedReader reader = Files.newBufferedReader(hello.toPath(),
1289 				UTF_8)) {
1290 			String content = reader.readLine();
1291 			assertEquals("submodule content should be as expected",
1292 					"branch world", content);
1293 		}
1294 	}
1295 
1296 	@Test
1297 	public void testTwoPathUseTheSameName() throws Exception {
1298 		Repository remoteDb = createBareRepository();
1299 		Repository tempDb = createWorkRepository();
1300 
1301 		StringBuilder xmlContent = new StringBuilder();
1302 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
1303 				.append("<manifest>")
1304 				.append("<remote name=\"remote1\" fetch=\".\" />")
1305 				.append("<default revision=\"master\" remote=\"remote1\" />")
1306 				.append("<project path=\"path1\" ").append("name=\"")
1307 				.append(defaultUri).append("\" />")
1308 				.append("<project path=\"path2\" ").append("name=\"")
1309 				.append(defaultUri).append("\" />").append("</manifest>");
1310 		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
1311 				xmlContent.toString());
1312 
1313 		RepoCommand command = new RepoCommand(remoteDb);
1314 		command.setPath(
1315 				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
1316 				.setURI(rootUri).setRecommendShallow(true).call();
1317 		File directory = createTempDirectory("testBareRepo");
1318 		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
1319 				.setURI(remoteDb.getDirectory().toURI().toString()).call()
1320 				.getRepository();) {
1321 			File gitmodules = new File(localDb.getWorkTree(), ".gitmodules");
1322 			assertTrue("The .gitmodules file should exist",
1323 					gitmodules.exists());
1324 			FileBasedConfig c = new FileBasedConfig(gitmodules, FS.DETECTED);
1325 			c.load();
1326 			assertEquals("A module should exist for path1", "path1",
1327 					c.getString("submodule", defaultUri + "/path1", "path"));
1328 			assertEquals("A module should exist for path2", "path2",
1329 					c.getString("submodule", defaultUri + "/path2", "path"));
1330 		}
1331 	}
1332 
1333 	private void resolveRelativeUris() {
1334 		// Find the longest common prefix ends with "/" as rootUri.
1335 		defaultUri = defaultDb.getDirectory().toURI().toString();
1336 		notDefaultUri = notDefaultDb.getDirectory().toURI().toString();
1337 		groupAUri = groupADb.getDirectory().toURI().toString();
1338 		groupBUri = groupBDb.getDirectory().toURI().toString();
1339 		int start = 0;
1340 		while (start <= defaultUri.length()) {
1341 			int newStart = defaultUri.indexOf('/', start + 1);
1342 			String prefix = defaultUri.substring(0, newStart);
1343 			if (!notDefaultUri.startsWith(prefix) ||
1344 					!groupAUri.startsWith(prefix) ||
1345 					!groupBUri.startsWith(prefix)) {
1346 				start++;
1347 				rootUri = defaultUri.substring(0, start) + "manifest";
1348 				defaultUri = defaultUri.substring(start);
1349 				notDefaultUri = notDefaultUri.substring(start);
1350 				groupAUri = groupAUri.substring(start);
1351 				groupBUri = groupBUri.substring(start);
1352 				return;
1353 			}
1354 			start = newStart;
1355 		}
1356 	}
1357 
1358 	void testRelative(String a, String b, String want) {
1359 		String got = RepoCommand.relativize(URI.create(a), URI.create(b)).toString();
1360 
1361 		if (!got.equals(want)) {
1362 			fail(String.format("relative('%s', '%s') = '%s', want '%s'", a, b, got, want));
1363 		}
1364 	}
1365 
1366 	@Test
1367 	public void relative() {
1368 		testRelative("a/b/", "a/", "../");
1369 		// Normalization:
1370 		testRelative("a/p/..//b/", "a/", "../");
1371 		testRelative("a/b", "a/", "");
1372 		testRelative("a/", "a/b/", "b/");
1373 		testRelative("a/", "a/b", "b");
1374 		testRelative("/a/b/c", "/b/c", "../../b/c");
1375 		testRelative("/abc", "bcd", "bcd");
1376 		testRelative("abc", "def", "def");
1377 		testRelative("abc", "/bcd", "/bcd");
1378 		testRelative("http://a", "a/b", "a/b");
1379 		testRelative("http://base.com/a/", "http://child.com/a/b", "http://child.com/a/b");
1380 		testRelative("http://base.com/a/", "http://base.com/a/b", "b");
1381 	}
1382 }