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