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