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