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