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