View Javadoc
1   /*
2    * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3    * Copyright (C) 2007-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
4    * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
5    * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com>
6    * and other copyright owners as documented in the project's IP log.
7    *
8    * This program and the accompanying materials are made available
9    * under the terms of the Eclipse Distribution License v1.0 which
10   * accompanies this distribution, is reproduced below, and is
11   * available at http://www.eclipse.org/org/documents/edl-v10.php
12   *
13   * All rights reserved.
14   *
15   * Redistribution and use in source and binary forms, with or
16   * without modification, are permitted provided that the following
17   * conditions are met:
18   *
19   * - Redistributions of source code must retain the above copyright
20   *   notice, this list of conditions and the following disclaimer.
21   *
22   * - Redistributions in binary form must reproduce the above
23   *   copyright notice, this list of conditions and the following
24   *   disclaimer in the documentation and/or other materials provided
25   *   with the distribution.
26   *
27   * - Neither the name of the Eclipse Foundation, Inc. nor the
28   *   names of its contributors may be used to endorse or promote
29   *   products derived from this software without specific prior
30   *   written permission.
31   *
32   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
33   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
34   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
37   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
39   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
40   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
41   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
42   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
43   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
44   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45   */
46  
47  package org.eclipse.jgit.internal.storage.file;
48  
49  import static org.junit.Assert.assertEquals;
50  import static org.junit.Assert.assertFalse;
51  import static org.junit.Assert.assertNotNull;
52  import static org.junit.Assert.assertNotSame;
53  import static org.junit.Assert.assertTrue;
54  import static org.junit.Assert.fail;
55  
56  import java.io.File;
57  import java.io.FileInputStream;
58  import java.io.FileReader;
59  import java.io.IOException;
60  import java.io.UnsupportedEncodingException;
61  
62  import org.eclipse.jgit.errors.ConfigInvalidException;
63  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
64  import org.eclipse.jgit.errors.MissingObjectException;
65  import org.eclipse.jgit.internal.JGitText;
66  import org.eclipse.jgit.lib.AnyObjectId;
67  import org.eclipse.jgit.lib.CommitBuilder;
68  import org.eclipse.jgit.lib.Constants;
69  import org.eclipse.jgit.lib.FileMode;
70  import org.eclipse.jgit.lib.ObjectDatabase;
71  import org.eclipse.jgit.lib.ObjectId;
72  import org.eclipse.jgit.lib.ObjectInserter;
73  import org.eclipse.jgit.lib.PersonIdent;
74  import org.eclipse.jgit.lib.RefUpdate;
75  import org.eclipse.jgit.lib.Repository;
76  import org.eclipse.jgit.lib.TagBuilder;
77  import org.eclipse.jgit.lib.TreeFormatter;
78  import org.eclipse.jgit.revwalk.RevCommit;
79  import org.eclipse.jgit.revwalk.RevTag;
80  import org.eclipse.jgit.revwalk.RevWalk;
81  import org.eclipse.jgit.storage.file.FileBasedConfig;
82  import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
83  import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
84  import org.eclipse.jgit.util.FileUtils;
85  import org.junit.Test;
86  
87  public class T0003_BasicTest extends SampleDataRepositoryTestCase {
88  
89  	@Test
90  	public void test001_Initalize() {
91  		final File gitdir = new File(trash, Constants.DOT_GIT);
92  		final File hooks = new File(gitdir, "hooks");
93  		final File objects = new File(gitdir, "objects");
94  		final File objects_pack = new File(objects, "pack");
95  		final File objects_info = new File(objects, "info");
96  		final File refs = new File(gitdir, "refs");
97  		final File refs_heads = new File(refs, "heads");
98  		final File refs_tags = new File(refs, "tags");
99  		final File HEAD = new File(gitdir, "HEAD");
100 
101 		assertTrue("Exists " + trash, trash.isDirectory());
102 		assertTrue("Exists " + hooks, hooks.isDirectory());
103 		assertTrue("Exists " + objects, objects.isDirectory());
104 		assertTrue("Exists " + objects_pack, objects_pack.isDirectory());
105 		assertTrue("Exists " + objects_info, objects_info.isDirectory());
106 		assertEquals(2L, objects.listFiles().length);
107 		assertTrue("Exists " + refs, refs.isDirectory());
108 		assertTrue("Exists " + refs_heads, refs_heads.isDirectory());
109 		assertTrue("Exists " + refs_tags, refs_tags.isDirectory());
110 		assertTrue("Exists " + HEAD, HEAD.isFile());
111 		assertEquals(23, HEAD.length());
112 	}
113 
114 	@Test
115 	public void test000_openRepoBadArgs() throws IOException {
116 		try {
117 			new FileRepositoryBuilder().build();
118 			fail("Must pass either GIT_DIR or GIT_WORK_TREE");
119 		} catch (IllegalArgumentException e) {
120 			assertEquals(JGitText.get().eitherGitDirOrWorkTreeRequired, e
121 					.getMessage());
122 		}
123 	}
124 
125 	/**
126 	 * Check the default rules for looking up directories and files within a
127 	 * repo when the gitDir is given.
128 	 *
129 	 * @throws IOException
130 	 */
131 	@Test
132 	public void test000_openrepo_default_gitDirSet() throws IOException {
133 		File repo1Parent = new File(trash.getParentFile(), "r1");
134 		Repository repo1initial = new FileRepository(new File(repo1Parent,
135 				Constants.DOT_GIT));
136 		repo1initial.create();
137 		repo1initial.close();
138 
139 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
140 		FileRepository r = (FileRepository) new FileRepositoryBuilder()
141 				.setGitDir(theDir).build();
142 		assertEqualsPath(theDir, r.getDirectory());
143 		assertEqualsPath(repo1Parent, r.getWorkTree());
144 		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
145 		assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase()
146 				.getDirectory());
147 	}
148 
149 	/**
150 	 * Check that we can pass both a git directory and a work tree repo when the
151 	 * gitDir is given.
152 	 *
153 	 * @throws IOException
154 	 */
155 	@Test
156 	public void test000_openrepo_default_gitDirAndWorkTreeSet()
157 			throws IOException {
158 		File repo1Parent = new File(trash.getParentFile(), "r1");
159 		Repository repo1initial = new FileRepository(new File(repo1Parent,
160 				Constants.DOT_GIT));
161 		repo1initial.create();
162 		repo1initial.close();
163 
164 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
165 		FileRepository r = (FileRepository) new FileRepositoryBuilder()
166 				.setGitDir(theDir).setWorkTree(repo1Parent.getParentFile())
167 				.build();
168 		assertEqualsPath(theDir, r.getDirectory());
169 		assertEqualsPath(repo1Parent.getParentFile(), r.getWorkTree());
170 		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
171 		assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase()
172 				.getDirectory());
173 	}
174 
175 	/**
176 	 * Check the default rules for looking up directories and files within a
177 	 * repo when the workTree is given.
178 	 *
179 	 * @throws IOException
180 	 */
181 	@Test
182 	public void test000_openrepo_default_workDirSet() throws IOException {
183 		File repo1Parent = new File(trash.getParentFile(), "r1");
184 		Repository repo1initial = new FileRepository(new File(repo1Parent,
185 				Constants.DOT_GIT));
186 		repo1initial.create();
187 		repo1initial.close();
188 
189 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
190 		FileRepository r = (FileRepository) new FileRepositoryBuilder()
191 				.setWorkTree(repo1Parent).build();
192 		assertEqualsPath(theDir, r.getDirectory());
193 		assertEqualsPath(repo1Parent, r.getWorkTree());
194 		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
195 		assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase()
196 				.getDirectory());
197 	}
198 
199 	/**
200 	 * Check that worktree config has an effect, given absolute path.
201 	 *
202 	 * @throws IOException
203 	 */
204 	@Test
205 	public void test000_openrepo_default_absolute_workdirconfig()
206 			throws IOException {
207 		File repo1Parent = new File(trash.getParentFile(), "r1");
208 		File workdir = new File(trash.getParentFile(), "rw");
209 		FileUtils.mkdir(workdir);
210 		FileRepository repo1initial = new FileRepository(new File(repo1Parent,
211 				Constants.DOT_GIT));
212 		repo1initial.create();
213 		final FileBasedConfig cfg = repo1initial.getConfig();
214 		cfg.setString("core", null, "worktree", workdir.getAbsolutePath());
215 		cfg.save();
216 		repo1initial.close();
217 
218 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
219 		FileRepository r = (FileRepository) new FileRepositoryBuilder()
220 				.setGitDir(theDir).build();
221 		assertEqualsPath(theDir, r.getDirectory());
222 		assertEqualsPath(workdir, r.getWorkTree());
223 		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
224 		assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase()
225 				.getDirectory());
226 	}
227 
228 	/**
229 	 * Check that worktree config has an effect, given a relative path.
230 	 *
231 	 * @throws IOException
232 	 */
233 	@Test
234 	public void test000_openrepo_default_relative_workdirconfig()
235 			throws IOException {
236 		File repo1Parent = new File(trash.getParentFile(), "r1");
237 		File workdir = new File(trash.getParentFile(), "rw");
238 		FileUtils.mkdir(workdir);
239 		FileRepository repo1initial = new FileRepository(new File(repo1Parent,
240 				Constants.DOT_GIT));
241 		repo1initial.create();
242 		final FileBasedConfig cfg = repo1initial.getConfig();
243 		cfg.setString("core", null, "worktree", "../../rw");
244 		cfg.save();
245 		repo1initial.close();
246 
247 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
248 		FileRepository r = (FileRepository) new FileRepositoryBuilder()
249 				.setGitDir(theDir).build();
250 		assertEqualsPath(theDir, r.getDirectory());
251 		assertEqualsPath(workdir, r.getWorkTree());
252 		assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
253 		assertEqualsPath(new File(theDir, "objects"), r.getObjectDatabase()
254 				.getDirectory());
255 	}
256 
257 	/**
258 	 * Check that the given index file is honored and the alternate object
259 	 * directories too
260 	 *
261 	 * @throws IOException
262 	 */
263 	@Test
264 	public void test000_openrepo_alternate_index_file_and_objdirs()
265 			throws IOException {
266 		File repo1Parent = new File(trash.getParentFile(), "r1");
267 		File indexFile = new File(trash, "idx");
268 		File objDir = new File(trash, "../obj");
269 		File altObjDir = db.getObjectDatabase().getDirectory();
270 		Repository repo1initial = new FileRepository(new File(repo1Parent,
271 				Constants.DOT_GIT));
272 		repo1initial.create();
273 		repo1initial.close();
274 
275 		File theDir = new File(repo1Parent, Constants.DOT_GIT);
276 		FileRepository r = (FileRepository) new FileRepositoryBuilder() //
277 				.setGitDir(theDir).setObjectDirectory(objDir) //
278 				.addAlternateObjectDirectory(altObjDir) //
279 				.setIndexFile(indexFile) //
280 				.build();
281 		assertEqualsPath(theDir, r.getDirectory());
282 		assertEqualsPath(theDir.getParentFile(), r.getWorkTree());
283 		assertEqualsPath(indexFile, r.getIndexFile());
284 		assertEqualsPath(objDir, r.getObjectDatabase().getDirectory());
285 		assertNotNull(r.open(ObjectId
286 				.fromString("6db9c2ebf75590eef973081736730a9ea169a0c4")));
287 		// Must close or the default repo pack files created by this test gets
288 		// locked via the alternate object directories on Windows.
289 		r.close();
290 	}
291 
292 	protected void assertEqualsPath(File expected, File actual)
293 			throws IOException {
294 		assertEquals(expected.getCanonicalPath(), actual.getCanonicalPath());
295 	}
296 
297 	@Test
298 	public void test002_WriteEmptyTree() throws IOException {
299 		// One of our test packs contains the empty tree object. If the pack is
300 		// open when we create it we won't write the object file out as a loose
301 		// object (as it already exists in the pack).
302 		//
303 		final Repository newdb = createBareRepository();
304 		try (final ObjectInserter oi = newdb.newObjectInserter()) {
305 			final ObjectId treeId = oi.insert(new TreeFormatter());
306 			assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904",
307 					treeId.name());
308 		}
309 
310 		final File o = new File(new File(new File(newdb.getDirectory(),
311 				"objects"), "4b"), "825dc642cb6eb9a060e54bf8d69288fbee4904");
312 		assertTrue("Exists " + o, o.isFile());
313 		assertTrue("Read-only " + o, !o.canWrite());
314 	}
315 
316 	@Test
317 	public void test002_WriteEmptyTree2() throws IOException {
318 		// File shouldn't exist as it is in a test pack.
319 		//
320 		final ObjectId treeId = insertTree(new TreeFormatter());
321 		assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904", treeId.name());
322 		final File o = new File(new File(
323 				new File(db.getDirectory(), "objects"), "4b"),
324 				"825dc642cb6eb9a060e54bf8d69288fbee4904");
325 		assertFalse("Exists " + o, o.isFile());
326 	}
327 
328 	@Test
329 	public void test006_ReadUglyConfig() throws IOException,
330 			ConfigInvalidException {
331 		final File cfg = new File(db.getDirectory(), Constants.CONFIG);
332 		final FileBasedConfig c = new FileBasedConfig(cfg, db.getFS());
333 		final String configStr = "  [core];comment\n\tfilemode = yes\n"
334 				+ "[user]\n"
335 				+ "  email = A U Thor <thor@example.com> # Just an example...\n"
336 				+ " name = \"A  Thor \\\\ \\\"\\t \"\n"
337 				+ "    defaultCheckInComment = a many line\\n\\\ncomment\\n\\\n"
338 				+ " to test\n";
339 		write(cfg, configStr);
340 		c.load();
341 		assertEquals("yes", c.getString("core", null, "filemode"));
342 		assertEquals("A U Thor <thor@example.com>", c.getString("user", null,
343 				"email"));
344 		assertEquals("A  Thor \\ \"\t ", c.getString("user", null, "name"));
345 		assertEquals("a many line\ncomment\n to test", c.getString("user",
346 				null, "defaultCheckInComment"));
347 		c.save();
348 		final FileReader fr = new FileReader(cfg);
349 		final char[] cbuf = new char[configStr.length()];
350 		fr.read(cbuf);
351 		fr.close();
352 		assertEquals(configStr, new String(cbuf));
353 	}
354 
355 	@Test
356 	public void test007_Open() throws IOException {
357 		try (final FileRepository db2 = new FileRepository(db.getDirectory())) {
358 			assertEquals(db.getDirectory(), db2.getDirectory());
359 			assertEquals(db.getObjectDatabase().getDirectory(), db2
360 					.getObjectDatabase().getDirectory());
361 			assertNotSame(db.getConfig(), db2.getConfig());
362 		}
363 	}
364 
365 	@Test
366 	public void test008_FailOnWrongVersion() throws IOException {
367 		final File cfg = new File(db.getDirectory(), Constants.CONFIG);
368 		final String badvers = "ihopethisisneveraversion";
369 		final String configStr = "[core]\n" + "\trepositoryFormatVersion="
370 				+ badvers + "\n";
371 		write(cfg, configStr);
372 
373 		try (FileRepository unused = new FileRepository(db.getDirectory())) {
374 			fail("incorrectly opened a bad repository");
375 		} catch (IllegalArgumentException ioe) {
376 			assertNotNull(ioe.getMessage());
377 		}
378 	}
379 
380 	@Test
381 	public void test009_CreateCommitOldFormat() throws IOException {
382 		final ObjectId treeId = insertTree(new TreeFormatter());
383 		final CommitBuilder c = new CommitBuilder();
384 		c.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
385 		c.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
386 		c.setMessage("A Commit\n");
387 		c.setTreeId(treeId);
388 		assertEquals(treeId, c.getTreeId());
389 
390 		ObjectId actid = insertCommit(c);
391 
392 		final ObjectId cmtid = ObjectId
393 				.fromString("9208b2459ea6609a5af68627cc031796d0d9329b");
394 		assertEquals(cmtid, actid);
395 
396 		// Verify the commit we just wrote is in the correct format.
397 		ObjectDatabase odb = db.getObjectDatabase();
398 		assertTrue("is ObjectDirectory", odb instanceof ObjectDirectory);
399 		final XInputStream xis = new XInputStream(new FileInputStream(
400 				((ObjectDirectory) odb).fileFor(cmtid)));
401 		try {
402 			assertEquals(0x78, xis.readUInt8());
403 			assertEquals(0x9c, xis.readUInt8());
404 			assertEquals(0, 0x789c % 31);
405 		} finally {
406 			xis.close();
407 		}
408 
409 		// Verify we can read it.
410 		RevCommit c2 = parseCommit(actid);
411 		assertNotNull(c2);
412 		assertEquals(c.getMessage(), c2.getFullMessage());
413 		assertEquals(c.getTreeId(), c2.getTree());
414 		assertEquals(c.getAuthor(), c2.getAuthorIdent());
415 		assertEquals(c.getCommitter(), c2.getCommitterIdent());
416 	}
417 
418 	@Test
419 	public void test020_createBlobTag() throws IOException {
420 		final ObjectId emptyId = insertEmptyBlob();
421 		final TagBuilder t = new TagBuilder();
422 		t.setObjectId(emptyId, Constants.OBJ_BLOB);
423 		t.setTag("test020");
424 		t.setTagger(new PersonIdent(author, 1154236443000L, -4 * 60));
425 		t.setMessage("test020 tagged\n");
426 		ObjectId actid = insertTag(t);
427 		assertEquals("6759556b09fbb4fd8ae5e315134481cc25d46954", actid.name());
428 
429 		RevTag mapTag = parseTag(actid);
430 		assertEquals(Constants.OBJ_BLOB, mapTag.getObject().getType());
431 		assertEquals("test020 tagged\n", mapTag.getFullMessage());
432 		assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag
433 				.getTaggerIdent());
434 		assertEquals("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", mapTag
435 				.getObject().getId().name());
436 	}
437 
438 	@Test
439 	public void test021_createTreeTag() throws IOException {
440 		final ObjectId emptyId = insertEmptyBlob();
441 		TreeFormatter almostEmptyTree = new TreeFormatter();
442 		almostEmptyTree.append("empty", FileMode.REGULAR_FILE, emptyId);
443 		final ObjectId almostEmptyTreeId = insertTree(almostEmptyTree);
444 		final TagBuilder t = new TagBuilder();
445 		t.setObjectId(almostEmptyTreeId, Constants.OBJ_TREE);
446 		t.setTag("test021");
447 		t.setTagger(new PersonIdent(author, 1154236443000L, -4 * 60));
448 		t.setMessage("test021 tagged\n");
449 		ObjectId actid = insertTag(t);
450 		assertEquals("b0517bc8dbe2096b419d42424cd7030733f4abe5", actid.name());
451 
452 		RevTag mapTag = parseTag(actid);
453 		assertEquals(Constants.OBJ_TREE, mapTag.getObject().getType());
454 		assertEquals("test021 tagged\n", mapTag.getFullMessage());
455 		assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag
456 				.getTaggerIdent());
457 		assertEquals("417c01c8795a35b8e835113a85a5c0c1c77f67fb", mapTag
458 				.getObject().getId().name());
459 	}
460 
461 	@Test
462 	public void test022_createCommitTag() throws IOException {
463 		final ObjectId emptyId = insertEmptyBlob();
464 		TreeFormatter almostEmptyTree = new TreeFormatter();
465 		almostEmptyTree.append("empty", FileMode.REGULAR_FILE, emptyId);
466 		final ObjectId almostEmptyTreeId = insertTree(almostEmptyTree);
467 		final CommitBuilder almostEmptyCommit = new CommitBuilder();
468 		almostEmptyCommit.setAuthor(new PersonIdent(author, 1154236443000L,
469 				-2 * 60)); // not exactly the same
470 		almostEmptyCommit.setCommitter(new PersonIdent(author, 1154236443000L,
471 				-2 * 60));
472 		almostEmptyCommit.setMessage("test022\n");
473 		almostEmptyCommit.setTreeId(almostEmptyTreeId);
474 		ObjectId almostEmptyCommitId = insertCommit(almostEmptyCommit);
475 		final TagBuilder t = new TagBuilder();
476 		t.setObjectId(almostEmptyCommitId, Constants.OBJ_COMMIT);
477 		t.setTag("test022");
478 		t.setTagger(new PersonIdent(author, 1154236443000L, -4 * 60));
479 		t.setMessage("test022 tagged\n");
480 		ObjectId actid = insertTag(t);
481 		assertEquals("0ce2ebdb36076ef0b38adbe077a07d43b43e3807", actid.name());
482 
483 		RevTag mapTag = parseTag(actid);
484 		assertEquals(Constants.OBJ_COMMIT, mapTag.getObject().getType());
485 		assertEquals("test022 tagged\n", mapTag.getFullMessage());
486 		assertEquals(new PersonIdent(author, 1154236443000L, -4 * 60), mapTag
487 				.getTaggerIdent());
488 		assertEquals("b5d3b45a96b340441f5abb9080411705c51cc86c", mapTag
489 				.getObject().getId().name());
490 	}
491 
492 	@Test
493 	public void test023_createCommitNonAnullii() throws IOException {
494 		final ObjectId emptyId = insertEmptyBlob();
495 		TreeFormatter almostEmptyTree = new TreeFormatter();
496 		almostEmptyTree.append("empty", FileMode.REGULAR_FILE, emptyId);
497 		final ObjectId almostEmptyTreeId = insertTree(almostEmptyTree);
498 		CommitBuilder commit = new CommitBuilder();
499 		commit.setTreeId(almostEmptyTreeId);
500 		commit.setAuthor(new PersonIdent("Joe H\u00e4cker", "joe@example.com",
501 				4294967295000L, 60));
502 		commit.setCommitter(new PersonIdent("Joe Hacker", "joe2@example.com",
503 				4294967295000L, 60));
504 		commit.setEncoding("UTF-8");
505 		commit.setMessage("\u00dcbergeeks");
506 		ObjectId cid = insertCommit(commit);
507 		assertEquals("4680908112778718f37e686cbebcc912730b3154", cid.name());
508 
509 		RevCommit loadedCommit = parseCommit(cid);
510 		assertEquals(commit.getMessage(), loadedCommit.getFullMessage());
511 	}
512 
513 	@Test
514 	public void test024_createCommitNonAscii() throws IOException {
515 		final ObjectId emptyId = insertEmptyBlob();
516 		TreeFormatter almostEmptyTree = new TreeFormatter();
517 		almostEmptyTree.append("empty", FileMode.REGULAR_FILE, emptyId);
518 		final ObjectId almostEmptyTreeId = insertTree(almostEmptyTree);
519 		CommitBuilder commit = new CommitBuilder();
520 		commit.setTreeId(almostEmptyTreeId);
521 		commit.setAuthor(new PersonIdent("Joe H\u00e4cker", "joe@example.com",
522 				4294967295000L, 60));
523 		commit.setCommitter(new PersonIdent("Joe Hacker", "joe2@example.com",
524 				4294967295000L, 60));
525 		commit.setEncoding("ISO-8859-1");
526 		commit.setMessage("\u00dcbergeeks");
527 		ObjectId cid = insertCommit(commit);
528 		assertEquals("2979b39d385014b33287054b87f77bcb3ecb5ebf", cid.name());
529 	}
530 
531 	@Test
532 	public void test025_computeSha1NoStore() throws IOException {
533 		byte[] data = "test025 some data, more than 16 bytes to get good coverage"
534 				.getBytes("ISO-8859-1");
535 		try (ObjectInserter.Formatter formatter = new ObjectInserter.Formatter()) {
536 			final ObjectId id = formatter.idFor(Constants.OBJ_BLOB, data);
537 			assertEquals("4f561df5ecf0dfbd53a0dc0f37262fef075d9dde", id.name());
538 		}
539 	}
540 
541 	@Test
542 	public void test026_CreateCommitMultipleparents() throws IOException {
543 		final ObjectId treeId;
544 		try (final ObjectInserter oi = db.newObjectInserter()) {
545 			final ObjectId blobId = oi.insert(Constants.OBJ_BLOB,
546 					"and this is the data in me\n".getBytes(Constants.CHARSET
547 							.name()));
548 			TreeFormatter fmt = new TreeFormatter();
549 			fmt.append("i-am-a-file", FileMode.REGULAR_FILE, blobId);
550 			treeId = oi.insert(fmt);
551 			oi.flush();
552 		}
553 		assertEquals(ObjectId
554 				.fromString("00b1f73724f493096d1ffa0b0f1f1482dbb8c936"), treeId);
555 
556 		final CommitBuilder c1 = new CommitBuilder();
557 		c1.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
558 		c1.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
559 		c1.setMessage("A Commit\n");
560 		c1.setTreeId(treeId);
561 		assertEquals(treeId, c1.getTreeId());
562 		ObjectId actid1 = insertCommit(c1);
563 		final ObjectId cmtid1 = ObjectId
564 				.fromString("803aec4aba175e8ab1d666873c984c0308179099");
565 		assertEquals(cmtid1, actid1);
566 
567 		final CommitBuilder c2 = new CommitBuilder();
568 		c2.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
569 		c2.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
570 		c2.setMessage("A Commit 2\n");
571 		c2.setTreeId(treeId);
572 		assertEquals(treeId, c2.getTreeId());
573 		c2.setParentIds(actid1);
574 		ObjectId actid2 = insertCommit(c2);
575 		final ObjectId cmtid2 = ObjectId
576 				.fromString("95d068687c91c5c044fb8c77c5154d5247901553");
577 		assertEquals(cmtid2, actid2);
578 
579 		RevCommit rm2 = parseCommit(cmtid2);
580 		assertNotSame(c2, rm2); // assert the parsed objects is not from the
581 		// cache
582 		assertEquals(c2.getAuthor(), rm2.getAuthorIdent());
583 		assertEquals(actid2, rm2.getId());
584 		assertEquals(c2.getMessage(), rm2.getFullMessage());
585 		assertEquals(c2.getTreeId(), rm2.getTree().getId());
586 		assertEquals(1, rm2.getParentCount());
587 		assertEquals(actid1, rm2.getParent(0));
588 
589 		final CommitBuilder c3 = new CommitBuilder();
590 		c3.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
591 		c3.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
592 		c3.setMessage("A Commit 3\n");
593 		c3.setTreeId(treeId);
594 		assertEquals(treeId, c3.getTreeId());
595 		c3.setParentIds(actid1, actid2);
596 		ObjectId actid3 = insertCommit(c3);
597 		final ObjectId cmtid3 = ObjectId
598 				.fromString("ce6e1ce48fbeeb15a83f628dc8dc2debefa066f4");
599 		assertEquals(cmtid3, actid3);
600 
601 		RevCommit rm3 = parseCommit(cmtid3);
602 		assertNotSame(c3, rm3); // assert the parsed objects is not from the
603 		// cache
604 		assertEquals(c3.getAuthor(), rm3.getAuthorIdent());
605 		assertEquals(actid3, rm3.getId());
606 		assertEquals(c3.getMessage(), rm3.getFullMessage());
607 		assertEquals(c3.getTreeId(), rm3.getTree().getId());
608 		assertEquals(2, rm3.getParentCount());
609 		assertEquals(actid1, rm3.getParent(0));
610 		assertEquals(actid2, rm3.getParent(1));
611 
612 		final CommitBuilder c4 = new CommitBuilder();
613 		c4.setAuthor(new PersonIdent(author, 1154236443000L, -4 * 60));
614 		c4.setCommitter(new PersonIdent(committer, 1154236443000L, -4 * 60));
615 		c4.setMessage("A Commit 4\n");
616 		c4.setTreeId(treeId);
617 		assertEquals(treeId, c3.getTreeId());
618 		c4.setParentIds(actid1, actid2, actid3);
619 		ObjectId actid4 = insertCommit(c4);
620 		final ObjectId cmtid4 = ObjectId
621 				.fromString("d1fca9fe3fef54e5212eb67902c8ed3e79736e27");
622 		assertEquals(cmtid4, actid4);
623 
624 		RevCommit rm4 = parseCommit(cmtid4);
625 		assertNotSame(c4, rm3); // assert the parsed objects is not from the
626 		// cache
627 		assertEquals(c4.getAuthor(), rm4.getAuthorIdent());
628 		assertEquals(actid4, rm4.getId());
629 		assertEquals(c4.getMessage(), rm4.getFullMessage());
630 		assertEquals(c4.getTreeId(), rm4.getTree().getId());
631 		assertEquals(3, rm4.getParentCount());
632 		assertEquals(actid1, rm4.getParent(0));
633 		assertEquals(actid2, rm4.getParent(1));
634 		assertEquals(actid3, rm4.getParent(2));
635 	}
636 
637 	@Test
638 	public void test027_UnpackedRefHigherPriorityThanPacked()
639 			throws IOException {
640 		String unpackedId = "7f822839a2fe9760f386cbbbcb3f92c5fe81def7";
641 		write(new File(db.getDirectory(), "refs/heads/a"), unpackedId + "\n");
642 
643 		ObjectId resolved = db.resolve("refs/heads/a");
644 		assertEquals(unpackedId, resolved.name());
645 	}
646 
647 	@Test
648 	public void test028_LockPackedRef() throws IOException {
649 		writeTrashFile(".git/packed-refs",
650 				"7f822839a2fe9760f386cbbbcb3f92c5fe81def7 refs/heads/foobar");
651 		writeTrashFile(".git/HEAD", "ref: refs/heads/foobar\n");
652 		BUG_WorkAroundRacyGitIssues("packed-refs");
653 		BUG_WorkAroundRacyGitIssues("HEAD");
654 
655 		ObjectId resolve = db.resolve("HEAD");
656 		assertEquals("7f822839a2fe9760f386cbbbcb3f92c5fe81def7", resolve.name());
657 
658 		RefUpdate lockRef = db.updateRef("HEAD");
659 		ObjectId newId = ObjectId
660 				.fromString("07f822839a2fe9760f386cbbbcb3f92c5fe81def");
661 		lockRef.setNewObjectId(newId);
662 		assertEquals(RefUpdate.Result.FORCED, lockRef.forceUpdate());
663 
664 		assertTrue(new File(db.getDirectory(), "refs/heads/foobar").exists());
665 		assertEquals(newId, db.resolve("refs/heads/foobar"));
666 
667 		// Again. The ref already exists
668 		RefUpdate lockRef2 = db.updateRef("HEAD");
669 		ObjectId newId2 = ObjectId
670 				.fromString("7f822839a2fe9760f386cbbbcb3f92c5fe81def7");
671 		lockRef2.setNewObjectId(newId2);
672 		assertEquals(RefUpdate.Result.FORCED, lockRef2.forceUpdate());
673 
674 		assertTrue(new File(db.getDirectory(), "refs/heads/foobar").exists());
675 		assertEquals(newId2, db.resolve("refs/heads/foobar"));
676 	}
677 
678 	@Test
679 	public void test30_stripWorkDir() {
680 		File relCwd = new File(".");
681 		File absCwd = relCwd.getAbsoluteFile();
682 		File absBase = new File(new File(absCwd, "repo"), "workdir");
683 		File relBase = new File(new File(relCwd, "repo"), "workdir");
684 		assertEquals(absBase.getAbsolutePath(), relBase.getAbsolutePath());
685 
686 		File relBaseFile = new File(new File(relBase, "other"), "module.c");
687 		File absBaseFile = new File(new File(absBase, "other"), "module.c");
688 		assertEquals("other/module.c", Repository.stripWorkDir(relBase,
689 				relBaseFile));
690 		assertEquals("other/module.c", Repository.stripWorkDir(relBase,
691 				absBaseFile));
692 		assertEquals("other/module.c", Repository.stripWorkDir(absBase,
693 				relBaseFile));
694 		assertEquals("other/module.c", Repository.stripWorkDir(absBase,
695 				absBaseFile));
696 
697 		File relNonFile = new File(new File(relCwd, "not-repo"), ".gitignore");
698 		File absNonFile = new File(new File(absCwd, "not-repo"), ".gitignore");
699 		assertEquals("", Repository.stripWorkDir(relBase, relNonFile));
700 		assertEquals("", Repository.stripWorkDir(absBase, absNonFile));
701 
702 		assertEquals("", Repository.stripWorkDir(db.getWorkTree(), db
703 				.getWorkTree()));
704 
705 		File file = new File(new File(db.getWorkTree(), "subdir"), "File.java");
706 		assertEquals("subdir/File.java", Repository.stripWorkDir(db
707 				.getWorkTree(), file));
708 
709 	}
710 
711 	private ObjectId insertEmptyBlob() throws IOException {
712 		final ObjectId emptyId;
713 		try (ObjectInserter oi = db.newObjectInserter()) {
714 			emptyId = oi.insert(Constants.OBJ_BLOB, new byte[] {});
715 			oi.flush();
716 		}
717 		return emptyId;
718 	}
719 
720 	private ObjectId insertTree(TreeFormatter tree) throws IOException {
721 		try (ObjectInserter oi = db.newObjectInserter()) {
722 			ObjectId id = oi.insert(tree);
723 			oi.flush();
724 			return id;
725 		}
726 	}
727 
728 	private ObjectId insertCommit(final CommitBuilder builder)
729 			throws IOException, UnsupportedEncodingException {
730 		try (ObjectInserter oi = db.newObjectInserter()) {
731 			ObjectId id = oi.insert(builder);
732 			oi.flush();
733 			return id;
734 		}
735 	}
736 
737 	private RevCommit parseCommit(AnyObjectId id)
738 			throws MissingObjectException, IncorrectObjectTypeException,
739 			IOException {
740 		try (RevWalk rw = new RevWalk(db)) {
741 			return rw.parseCommit(id);
742 		}
743 	}
744 
745 	private ObjectId insertTag(final TagBuilder tag) throws IOException,
746 			UnsupportedEncodingException {
747 		try (ObjectInserter oi = db.newObjectInserter()) {
748 			ObjectId id = oi.insert(tag);
749 			oi.flush();
750 			return id;
751 		}
752 	}
753 
754 	private RevTag parseTag(AnyObjectId id) throws MissingObjectException,
755 			IncorrectObjectTypeException, IOException {
756 		try (RevWalk rw = new RevWalk(db)) {
757 			return rw.parseTag(id);
758 		}
759 	}
760 
761 	/**
762 	 * Kick the timestamp of a local file.
763 	 * <p>
764 	 * We shouldn't have to make these method calls. The cache is using file
765 	 * system timestamps, and on many systems unit tests run faster than the
766 	 * modification clock. Dumping the cache after we make an edit behind
767 	 * RefDirectory's back allows the tests to pass.
768 	 *
769 	 * @param name
770 	 *            the file in the repository to force a time change on.
771 	 */
772 	private void BUG_WorkAroundRacyGitIssues(String name) {
773 		File path = new File(db.getDirectory(), name);
774 		long old = path.lastModified();
775 		long set = 1250379778668L; // Sat Aug 15 20:12:58 GMT-03:30 2009
776 		path.setLastModified(set);
777 		assertTrue("time changed", old != path.lastModified());
778 	}
779 }