View Javadoc
1   /*
2    * Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
3    * Copyright (C) 2009-2010, Google Inc.
4    * Copyright (C) 2008-2013, Robin Rosenberg <robin.rosenberg@dewire.com>
5    * and other copyright owners as documented in the project's IP log.
6    *
7    * This program and the accompanying materials are made available
8    * under the terms of the Eclipse Distribution License v1.0 which
9    * accompanies this distribution, is reproduced below, and is
10   * available at http://www.eclipse.org/org/documents/edl-v10.php
11   *
12   * All rights reserved.
13   *
14   * Redistribution and use in source and binary forms, with or
15   * without modification, are permitted provided that the following
16   * conditions are met:
17   *
18   * - Redistributions of source code must retain the above copyright
19   *   notice, this list of conditions and the following disclaimer.
20   *
21   * - Redistributions in binary form must reproduce the above
22   *   copyright notice, this list of conditions and the following
23   *   disclaimer in the documentation and/or other materials provided
24   *   with the distribution.
25   *
26   * - Neither the name of the Eclipse Foundation, Inc. nor the
27   *   names of its contributors may be used to endorse or promote
28   *   products derived from this software without specific prior
29   *   written permission.
30   *
31   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
32   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
33   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
36   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
38   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
40   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
43   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44   */
45  
46  package org.eclipse.jgit.internal.storage.file;
47  
48  import static org.eclipse.jgit.junit.Assert.assertEquals;
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.assertNull;
54  import static org.junit.Assert.assertSame;
55  import static org.junit.Assert.assertTrue;
56  import static org.junit.Assert.fail;
57  
58  import java.io.File;
59  import java.io.IOException;
60  import java.util.List;
61  import java.util.Map;
62  import java.util.Map.Entry;
63  
64  import org.eclipse.jgit.lib.AnyObjectId;
65  import org.eclipse.jgit.lib.Constants;
66  import org.eclipse.jgit.lib.ObjectId;
67  import org.eclipse.jgit.lib.PersonIdent;
68  import org.eclipse.jgit.lib.Ref;
69  import org.eclipse.jgit.lib.RefRename;
70  import org.eclipse.jgit.lib.RefUpdate;
71  import org.eclipse.jgit.lib.RefUpdate.Result;
72  import org.eclipse.jgit.lib.ReflogEntry;
73  import org.eclipse.jgit.lib.ReflogReader;
74  import org.eclipse.jgit.lib.Repository;
75  import org.eclipse.jgit.revwalk.RevCommit;
76  import org.eclipse.jgit.revwalk.RevWalk;
77  import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
78  import org.junit.Test;
79  
80  public class RefUpdateTest extends SampleDataRepositoryTestCase {
81  
82  	private void writeSymref(String src, String dst) throws IOException {
83  		RefUpdate u = db.updateRef(src);
84  		switch (u.link(dst)) {
85  		case NEW:
86  		case FORCED:
87  		case NO_CHANGE:
88  			break;
89  		default:
90  			fail("link " + src + " to " + dst);
91  		}
92  	}
93  
94  	private RefUpdate updateRef(final String name) throws IOException {
95  		final RefUpdate ref = db.updateRef(name);
96  		ref.setNewObjectId(db.resolve(Constants.HEAD));
97  		return ref;
98  	}
99  
100 	private void delete(final RefUpdate ref, final Result expected)
101 			throws IOException {
102 		delete(ref, expected, true, true);
103 	}
104 
105 	private void delete(final RefUpdate ref, final Result expected,
106 			final boolean exists, final boolean removed) throws IOException {
107 		delete(db, ref, expected, exists, removed);
108 	}
109 
110 	private void delete(Repository repo, final RefUpdate ref, final Result expected,
111 			final boolean exists, final boolean removed) throws IOException {
112 		assertEquals(exists, repo.getAllRefs().containsKey(ref.getName()));
113 		assertEquals(expected, ref.delete());
114 		assertEquals(!removed, repo.getAllRefs().containsKey(ref.getName()));
115 	}
116 
117 	@Test
118 	public void testNoCacheObjectIdSubclass() throws IOException {
119 		final String newRef = "refs/heads/abc";
120 		final RefUpdate ru = updateRef(newRef);
121 		final SubclassedId newid = new SubclassedId(ru.getNewObjectId());
122 		ru.setNewObjectId(newid);
123 		Result update = ru.update();
124 		assertEquals(Result.NEW, update);
125 		final Ref r = db.getAllRefs().get(newRef);
126 		assertNotNull(r);
127 		assertEquals(newRef, r.getName());
128 		assertNotNull(r.getObjectId());
129 		assertNotSame(newid, r.getObjectId());
130 		assertSame(ObjectId.class, r.getObjectId().getClass());
131 		assertEquals(newid, r.getObjectId());
132 		List<ReflogEntry> reverseEntries1 = db
133 				.getReflogReader("refs/heads/abc").getReverseEntries();
134 		ReflogEntry entry1 = reverseEntries1.get(0);
135 		assertEquals(1, reverseEntries1.size());
136 		assertEquals(ObjectId.zeroId(), entry1.getOldId());
137 		assertEquals(r.getObjectId(), entry1.getNewId());
138 		assertEquals(new PersonIdent(db).toString(),  entry1.getWho().toString());
139 		assertEquals("", entry1.getComment());
140 		List<ReflogEntry> reverseEntries2 = db.getReflogReader("HEAD")
141 				.getReverseEntries();
142 		assertEquals(0, reverseEntries2.size());
143 	}
144 
145 	@Test
146 	public void testNewNamespaceConflictWithLoosePrefixNameExists()
147 			throws IOException {
148 		final String newRef = "refs/heads/z";
149 		final RefUpdate ru = updateRef(newRef);
150 		Result update = ru.update();
151 		assertEquals(Result.NEW, update);
152 		// end setup
153 		final String newRef2 = "refs/heads/z/a";
154 		final RefUpdate ru2 = updateRef(newRef2);
155 		Result update2 = ru2.update();
156 		assertEquals(Result.LOCK_FAILURE, update2);
157 		assertEquals(1, db.getReflogReader("refs/heads/z").getReverseEntries().size());
158 		assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
159 	}
160 
161 	@Test
162 	public void testNewNamespaceConflictWithPackedPrefixNameExists()
163 			throws IOException {
164 		final String newRef = "refs/heads/master/x";
165 		final RefUpdate ru = updateRef(newRef);
166 		Result update = ru.update();
167 		assertEquals(Result.LOCK_FAILURE, update);
168 		assertNull(db.getReflogReader("refs/heads/master/x"));
169 		assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
170 	}
171 
172 	@Test
173 	public void testNewNamespaceConflictWithLoosePrefixOfExisting()
174 			throws IOException {
175 		final String newRef = "refs/heads/z/a";
176 		final RefUpdate ru = updateRef(newRef);
177 		Result update = ru.update();
178 		assertEquals(Result.NEW, update);
179 		// end setup
180 		final String newRef2 = "refs/heads/z";
181 		final RefUpdate ru2 = updateRef(newRef2);
182 		Result update2 = ru2.update();
183 		assertEquals(Result.LOCK_FAILURE, update2);
184 		assertEquals(1, db.getReflogReader("refs/heads/z/a").getReverseEntries().size());
185 		assertNull(db.getReflogReader("refs/heads/z"));
186 		assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
187 	}
188 
189 	@Test
190 	public void testNewNamespaceConflictWithPackedPrefixOfExisting()
191 			throws IOException {
192 		final String newRef = "refs/heads/prefix";
193 		final RefUpdate ru = updateRef(newRef);
194 		Result update = ru.update();
195 		assertEquals(Result.LOCK_FAILURE, update);
196 		assertNull(db.getReflogReader("refs/heads/prefix"));
197 		assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
198 	}
199 
200 	/**
201 	 * Delete a ref that is pointed to by HEAD
202 	 *
203 	 * @throws IOException
204 	 */
205 	@Test
206 	public void testDeleteHEADreferencedRef() throws IOException {
207 		ObjectId pid = db.resolve("refs/heads/master^");
208 		RefUpdate updateRef = db.updateRef("refs/heads/master");
209 		updateRef.setNewObjectId(pid);
210 		updateRef.setForceUpdate(true);
211 		Result update = updateRef.update();
212 		assertEquals(Result.FORCED, update); // internal
213 
214 		RefUpdate updateRef2 = db.updateRef("refs/heads/master");
215 		Result delete = updateRef2.delete();
216 		assertEquals(Result.REJECTED_CURRENT_BRANCH, delete);
217 		assertEquals(pid, db.resolve("refs/heads/master"));
218 		assertEquals(1,db.getReflogReader("refs/heads/master").getReverseEntries().size());
219 		assertEquals(0,db.getReflogReader("HEAD").getReverseEntries().size());
220 	}
221 
222 	@Test
223 	public void testLooseDelete() throws IOException {
224 		final String newRef = "refs/heads/abc";
225 		RefUpdate ref = updateRef(newRef);
226 		ref.update(); // create loose ref
227 		ref = updateRef(newRef); // refresh
228 		delete(ref, Result.NO_CHANGE);
229 		assertNull(db.getReflogReader("refs/heads/abc"));
230 	}
231 
232 	@Test
233 	public void testDeleteHead() throws IOException {
234 		final RefUpdate ref = updateRef(Constants.HEAD);
235 		delete(ref, Result.REJECTED_CURRENT_BRANCH, true, false);
236 		assertEquals(0, db.getReflogReader("refs/heads/master").getReverseEntries().size());
237 		assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
238 	}
239 
240 	@Test
241 	public void testDeleteHeadInBareRepo() throws IOException {
242 		Repository bareRepo = createBareRepository();
243 		RefUpdate ref = bareRepo.updateRef(Constants.HEAD);
244 		ref.setNewObjectId(ObjectId
245 				.fromString("0123456789012345678901234567890123456789"));
246 		// Create the HEAD ref so we can delete it.
247 		assertEquals(Result.NEW, ref.update());
248 		ref = bareRepo.updateRef(Constants.HEAD);
249 		delete(bareRepo, ref, Result.NO_CHANGE, true, true);
250 	}
251 	/**
252 	 * Delete a loose ref and make sure the directory in refs is deleted too,
253 	 * and the reflog dir too
254 	 *
255 	 * @throws IOException
256 	 */
257 	@Test
258 	public void testDeleteLooseAndItsDirectory() throws IOException {
259 		ObjectId pid = db.resolve("refs/heads/c^");
260 		RefUpdate updateRef = db.updateRef("refs/heads/z/c");
261 		updateRef.setNewObjectId(pid);
262 		updateRef.setForceUpdate(true);
263 		updateRef.setRefLogMessage("new test ref", false);
264 		Result update = updateRef.update();
265 		assertEquals(Result.NEW, update); // internal
266 		assertTrue(new File(db.getDirectory(), Constants.R_HEADS + "z")
267 				.exists());
268 		assertTrue(new File(db.getDirectory(), "logs/refs/heads/z").exists());
269 
270 		// The real test here
271 		RefUpdate updateRef2 = db.updateRef("refs/heads/z/c");
272 		updateRef2.setForceUpdate(true);
273 		Result delete = updateRef2.delete();
274 		assertEquals(Result.FORCED, delete);
275 		assertNull(db.resolve("refs/heads/z/c"));
276 		assertFalse(new File(db.getDirectory(), Constants.R_HEADS + "z")
277 				.exists());
278 		assertFalse(new File(db.getDirectory(), "logs/refs/heads/z").exists());
279 	}
280 
281 	@Test
282 	public void testDeleteNotFound() throws IOException {
283 		final RefUpdate ref = updateRef("refs/heads/xyz");
284 		delete(ref, Result.NEW, false, true);
285 	}
286 
287 	@Test
288 	public void testDeleteFastForward() throws IOException {
289 		final RefUpdate ref = updateRef("refs/heads/a");
290 		delete(ref, Result.FAST_FORWARD);
291 	}
292 
293 	@Test
294 	public void testDeleteForce() throws IOException {
295 		final RefUpdate ref = db.updateRef("refs/heads/b");
296 		ref.setNewObjectId(db.resolve("refs/heads/a"));
297 		delete(ref, Result.REJECTED, true, false);
298 		ref.setForceUpdate(true);
299 		delete(ref, Result.FORCED);
300 	}
301 
302 	@Test
303 	public void testDeleteWithoutHead() throws IOException {
304 		// Prepare repository without HEAD
305 		RefUpdate refUpdate = db.updateRef(Constants.HEAD, true);
306 		refUpdate.setForceUpdate(true);
307 		refUpdate.setNewObjectId(ObjectId.zeroId());
308 		Result updateResult = refUpdate.update();
309 		assertEquals(Result.FORCED, updateResult);
310 		Result deleteHeadResult = db.updateRef(Constants.HEAD).delete();
311 		assertEquals(Result.NO_CHANGE, deleteHeadResult);
312 
313 		// Any result is ok as long as it's not an NPE
314 		db.updateRef(Constants.R_HEADS + "master").delete();
315 	}
316 
317 	@Test
318 	public void testRefKeySameAsName() {
319 		Map<String, Ref> allRefs = db.getAllRefs();
320 		for (Entry<String, Ref> e : allRefs.entrySet()) {
321 			assertEquals(e.getKey(), e.getValue().getName());
322 
323 		}
324 	}
325 
326 	/**
327 	 * Try modify a ref forward, fast forward
328 	 *
329 	 * @throws IOException
330 	 */
331 	@Test
332 	public void testUpdateRefForward() throws IOException {
333 		ObjectId ppid = db.resolve("refs/heads/master^");
334 		ObjectId pid = db.resolve("refs/heads/master");
335 
336 		RefUpdate updateRef = db.updateRef("refs/heads/master");
337 		updateRef.setNewObjectId(ppid);
338 		updateRef.setForceUpdate(true);
339 		Result update = updateRef.update();
340 		assertEquals(Result.FORCED, update);
341 		assertEquals(ppid, db.resolve("refs/heads/master"));
342 
343 		// real test
344 		RefUpdate updateRef2 = db.updateRef("refs/heads/master");
345 		updateRef2.setNewObjectId(pid);
346 		Result update2 = updateRef2.update();
347 		assertEquals(Result.FAST_FORWARD, update2);
348 		assertEquals(pid, db.resolve("refs/heads/master"));
349 	}
350 
351 	/**
352 	 * Update the HEAD ref. Only it should be changed, not what it points to.
353 	 *
354 	 * @throws Exception
355 	 */
356 	@Test
357 	public void testUpdateRefDetached() throws Exception {
358 		ObjectId pid = db.resolve("refs/heads/master");
359 		ObjectId ppid = db.resolve("refs/heads/master^");
360 		RefUpdate updateRef = db.updateRef("HEAD", true);
361 		updateRef.setForceUpdate(true);
362 		updateRef.setNewObjectId(ppid);
363 		Result update = updateRef.update();
364 		assertEquals(Result.FORCED, update);
365 		assertEquals(ppid, db.resolve("HEAD"));
366 		Ref ref = db.exactRef("HEAD");
367 		assertEquals("HEAD", ref.getName());
368 		assertTrue("is detached", !ref.isSymbolic());
369 
370 		// the branch HEAD referred to is left untouched
371 		assertEquals(pid, db.resolve("refs/heads/master"));
372 		ReflogReader reflogReader = db.getReflogReader("HEAD");
373 		ReflogEntry e = reflogReader.getReverseEntries().get(0);
374 		assertEquals(pid, e.getOldId());
375 		assertEquals(ppid, e.getNewId());
376 		assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
377 		assertEquals("GIT_COMMITTER_NAME", e.getWho().getName());
378 		assertEquals(1250379778000L, e.getWho().getWhen().getTime());
379 	}
380 
381 	/**
382 	 * Update the HEAD ref when the referenced branch is unborn
383 	 *
384 	 * @throws Exception
385 	 */
386 	@Test
387 	public void testUpdateRefDetachedUnbornHead() throws Exception {
388 		ObjectId ppid = db.resolve("refs/heads/master^");
389 		writeSymref("HEAD", "refs/heads/unborn");
390 		RefUpdate updateRef = db.updateRef("HEAD", true);
391 		updateRef.setForceUpdate(true);
392 		updateRef.setNewObjectId(ppid);
393 		Result update = updateRef.update();
394 		assertEquals(Result.NEW, update);
395 		assertEquals(ppid, db.resolve("HEAD"));
396 		Ref ref = db.exactRef("HEAD");
397 		assertEquals("HEAD", ref.getName());
398 		assertTrue("is detached", !ref.isSymbolic());
399 
400 		// the branch HEAD referred to is left untouched
401 		assertNull(db.resolve("refs/heads/unborn"));
402 		ReflogReader reflogReader = db.getReflogReader("HEAD");
403 		ReflogEntry e = reflogReader.getReverseEntries().get(0);
404 		assertEquals(ObjectId.zeroId(), e.getOldId());
405 		assertEquals(ppid, e.getNewId());
406 		assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
407 		assertEquals("GIT_COMMITTER_NAME", e.getWho().getName());
408 		assertEquals(1250379778000L, e.getWho().getWhen().getTime());
409 	}
410 
411 	/**
412 	 * Delete a ref that exists both as packed and loose. Make sure the ref
413 	 * cannot be resolved after delete.
414 	 *
415 	 * @throws IOException
416 	 */
417 	@Test
418 	public void testDeleteLoosePacked() throws IOException {
419 		ObjectId pid = db.resolve("refs/heads/c^");
420 		RefUpdate updateRef = db.updateRef("refs/heads/c");
421 		updateRef.setNewObjectId(pid);
422 		updateRef.setForceUpdate(true);
423 		Result update = updateRef.update();
424 		assertEquals(Result.FORCED, update); // internal
425 
426 		// The real test here
427 		RefUpdate updateRef2 = db.updateRef("refs/heads/c");
428 		updateRef2.setForceUpdate(true);
429 		Result delete = updateRef2.delete();
430 		assertEquals(Result.FORCED, delete);
431 		assertNull(db.resolve("refs/heads/c"));
432 	}
433 
434 	/**
435 	 * Try modify a ref to same
436 	 *
437 	 * @throws IOException
438 	 */
439 	@Test
440 	public void testUpdateRefNoChange() throws IOException {
441 		ObjectId pid = db.resolve("refs/heads/master");
442 		RefUpdate updateRef = db.updateRef("refs/heads/master");
443 		updateRef.setNewObjectId(pid);
444 		Result update = updateRef.update();
445 		assertEquals(Result.NO_CHANGE, update);
446 		assertEquals(pid, db.resolve("refs/heads/master"));
447 	}
448 
449 	/**
450 	 * Test case originating from
451 	 * <a href="http://bugs.eclipse.org/285991">bug 285991</a>
452 	 *
453 	 * Make sure the in memory cache is updated properly after
454 	 * update of symref. This one did not fail because the
455 	 * ref was packed due to implementation issues.
456 	 *
457 	 * @throws Exception
458 	 */
459 	@Test
460 	public void testRefsCacheAfterUpdate() throws Exception {
461 		// Do not use the defalt repo for this case.
462 		Map<String, Ref> allRefs = db.getAllRefs();
463 		ObjectId oldValue = db.resolve("HEAD");
464 		ObjectId newValue = db.resolve("HEAD^");
465 		// first make HEAD refer to loose ref
466 		RefUpdate updateRef = db.updateRef(Constants.HEAD);
467 		updateRef.setForceUpdate(true);
468 		updateRef.setNewObjectId(newValue);
469 		Result update = updateRef.update();
470 		assertEquals(Result.FORCED, update);
471 
472 		// now update that ref
473 		updateRef = db.updateRef(Constants.HEAD);
474 		updateRef.setNewObjectId(oldValue);
475 		update = updateRef.update();
476 		assertEquals(Result.FAST_FORWARD, update);
477 
478 		allRefs = db.getAllRefs();
479 		Ref master = allRefs.get("refs/heads/master");
480 		Ref head = allRefs.get("HEAD");
481 		assertEquals("refs/heads/master", master.getName());
482 		assertEquals("HEAD", head.getName());
483 		assertTrue("is symbolic reference", head.isSymbolic());
484 		assertSame(master, head.getTarget());
485 	}
486 
487 	/**
488 	 * Test case originating from
489 	 * <a href="http://bugs.eclipse.org/285991">bug 285991</a>
490 	 *
491 	 * Make sure the in memory cache is updated properly after
492 	 * update of symref.
493 	 *
494 	 * @throws Exception
495 	 */
496 	@Test
497 	public void testRefsCacheAfterUpdateLooseOnly() throws Exception {
498 		// Do not use the defalt repo for this case.
499 		Map<String, Ref> allRefs = db.getAllRefs();
500 		ObjectId oldValue = db.resolve("HEAD");
501 		writeSymref(Constants.HEAD, "refs/heads/newref");
502 		RefUpdate updateRef = db.updateRef(Constants.HEAD);
503 		updateRef.setForceUpdate(true);
504 		updateRef.setNewObjectId(oldValue);
505 		Result update = updateRef.update();
506 		assertEquals(Result.NEW, update);
507 
508 		allRefs = db.getAllRefs();
509 		Ref head = allRefs.get("HEAD");
510 		Ref newref = allRefs.get("refs/heads/newref");
511 		assertEquals("refs/heads/newref", newref.getName());
512 		assertEquals("HEAD", head.getName());
513 		assertTrue("is symbolic reference", head.isSymbolic());
514 		assertSame(newref, head.getTarget());
515 	}
516 
517 	/**
518 	 * Try modify a ref, but get wrong expected old value
519 	 *
520 	 * @throws IOException
521 	 */
522 	@Test
523 	public void testUpdateRefLockFailureWrongOldValue() throws IOException {
524 		ObjectId pid = db.resolve("refs/heads/master");
525 		RefUpdate updateRef = db.updateRef("refs/heads/master");
526 		updateRef.setNewObjectId(pid);
527 		updateRef.setExpectedOldObjectId(db.resolve("refs/heads/master^"));
528 		Result update = updateRef.update();
529 		assertEquals(Result.LOCK_FAILURE, update);
530 		assertEquals(pid, db.resolve("refs/heads/master"));
531 	}
532 
533 	/**
534 	 * Try modify a ref forward, fast forward, checking old value first
535 	 *
536 	 * @throws IOException
537 	 */
538 	@Test
539 	public void testUpdateRefForwardWithCheck1() throws IOException {
540 		ObjectId ppid = db.resolve("refs/heads/master^");
541 		ObjectId pid = db.resolve("refs/heads/master");
542 
543 		RefUpdate updateRef = db.updateRef("refs/heads/master");
544 		updateRef.setNewObjectId(ppid);
545 		updateRef.setForceUpdate(true);
546 		Result update = updateRef.update();
547 		assertEquals(Result.FORCED, update);
548 		assertEquals(ppid, db.resolve("refs/heads/master"));
549 
550 		// real test
551 		RefUpdate updateRef2 = db.updateRef("refs/heads/master");
552 		updateRef2.setExpectedOldObjectId(ppid);
553 		updateRef2.setNewObjectId(pid);
554 		Result update2 = updateRef2.update();
555 		assertEquals(Result.FAST_FORWARD, update2);
556 		assertEquals(pid, db.resolve("refs/heads/master"));
557 	}
558 
559 	/**
560 	 * Try modify a ref forward, fast forward, checking old commit first
561 	 *
562 	 * @throws IOException
563 	 */
564 	@Test
565 	public void testUpdateRefForwardWithCheck2() throws IOException {
566 		ObjectId ppid = db.resolve("refs/heads/master^");
567 		ObjectId pid = db.resolve("refs/heads/master");
568 
569 		RefUpdate updateRef = db.updateRef("refs/heads/master");
570 		updateRef.setNewObjectId(ppid);
571 		updateRef.setForceUpdate(true);
572 		Result update = updateRef.update();
573 		assertEquals(Result.FORCED, update);
574 		assertEquals(ppid, db.resolve("refs/heads/master"));
575 
576 		// real test
577 		try (RevWalk rw = new RevWalk(db)) {
578 			RevCommit old = rw.parseCommit(ppid);
579 			RefUpdate updateRef2 = db.updateRef("refs/heads/master");
580 			updateRef2.setExpectedOldObjectId(old);
581 			updateRef2.setNewObjectId(pid);
582 			Result update2 = updateRef2.update();
583 			assertEquals(Result.FAST_FORWARD, update2);
584 			assertEquals(pid, db.resolve("refs/heads/master"));
585 		}
586 	}
587 
588 	/**
589 	 * Try modify a ref that is locked
590 	 *
591 	 * @throws IOException
592 	 */
593 	@Test
594 	public void testUpdateRefLockFailureLocked() throws IOException {
595 		ObjectId opid = db.resolve("refs/heads/master");
596 		ObjectId pid = db.resolve("refs/heads/master^");
597 		RefUpdate updateRef = db.updateRef("refs/heads/master");
598 		updateRef.setNewObjectId(pid);
599 		LockFile lockFile1 = new LockFile(new File(db.getDirectory(),
600 				"refs/heads/master"));
601 		try {
602 			assertTrue(lockFile1.lock()); // precondition to test
603 			Result update = updateRef.update();
604 			assertEquals(Result.LOCK_FAILURE, update);
605 			assertEquals(opid, db.resolve("refs/heads/master"));
606 			LockFile lockFile2 = new LockFile(new File(db.getDirectory(),"refs/heads/master"));
607 			assertFalse(lockFile2.lock()); // was locked, still is
608 		} finally {
609 			lockFile1.unlock();
610 		}
611 	}
612 
613 	/**
614 	 * Try to delete a ref. Delete requires force.
615 	 *
616 	 * @throws IOException
617 	 */
618 	@Test
619 	public void testDeleteLoosePackedRejected() throws IOException {
620 		ObjectId pid = db.resolve("refs/heads/c^");
621 		ObjectId oldpid = db.resolve("refs/heads/c");
622 		RefUpdate updateRef = db.updateRef("refs/heads/c");
623 		updateRef.setNewObjectId(pid);
624 		Result update = updateRef.update();
625 		assertEquals(Result.REJECTED, update);
626 		assertEquals(oldpid, db.resolve("refs/heads/c"));
627 	}
628 
629 	@Test
630 	public void testRenameBranchNoPreviousLog() throws IOException {
631 		assertFalse("precondition, no log on old branchg", new File(db
632 				.getDirectory(), "logs/refs/heads/b").exists());
633 		ObjectId rb = db.resolve("refs/heads/b");
634 		ObjectId oldHead = db.resolve(Constants.HEAD);
635 		assertFalse(rb.equals(oldHead)); // assumption for this test
636 		RefRename renameRef = db.renameRef("refs/heads/b",
637 				"refs/heads/new/name");
638 		Result result = renameRef.rename();
639 		assertEquals(Result.RENAMED, result);
640 		assertEquals(rb, db.resolve("refs/heads/new/name"));
641 		assertNull(db.resolve("refs/heads/b"));
642 		assertEquals(1, db.getReflogReader("new/name").getReverseEntries().size());
643 		assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name")
644 				.getLastEntry().getComment());
645 		assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
646 		assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged
647 	}
648 
649 	@Test
650 	public void testRenameBranchHasPreviousLog() throws IOException {
651 		ObjectId rb = db.resolve("refs/heads/b");
652 		ObjectId oldHead = db.resolve(Constants.HEAD);
653 		assertFalse("precondition for this test, branch b != HEAD", rb
654 				.equals(oldHead));
655 		writeReflog(db, rb, "Just a message", "refs/heads/b");
656 		assertTrue("log on old branch", new File(db.getDirectory(),
657 				"logs/refs/heads/b").exists());
658 		RefRename renameRef = db.renameRef("refs/heads/b",
659 				"refs/heads/new/name");
660 		Result result = renameRef.rename();
661 		assertEquals(Result.RENAMED, result);
662 		assertEquals(rb, db.resolve("refs/heads/new/name"));
663 		assertNull(db.resolve("refs/heads/b"));
664 		assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size());
665 		assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name")
666 				.getLastEntry().getComment());
667 		assertEquals("Just a message", db.getReflogReader("new/name")
668 				.getReverseEntries().get(1).getComment());
669 		assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
670 		assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged
671 	}
672 
673 	@Test
674 	public void testRenameCurrentBranch() throws IOException {
675 		ObjectId rb = db.resolve("refs/heads/b");
676 		writeSymref(Constants.HEAD, "refs/heads/b");
677 		ObjectId oldHead = db.resolve(Constants.HEAD);
678 		assertEquals("internal test condition, b == HEAD", oldHead, rb);
679 		writeReflog(db, rb, "Just a message", "refs/heads/b");
680 		assertTrue("log on old branch", new File(db.getDirectory(),
681 				"logs/refs/heads/b").exists());
682 		RefRename renameRef = db.renameRef("refs/heads/b",
683 				"refs/heads/new/name");
684 		Result result = renameRef.rename();
685 		assertEquals(Result.RENAMED, result);
686 		assertEquals(rb, db.resolve("refs/heads/new/name"));
687 		assertNull(db.resolve("refs/heads/b"));
688 		assertEquals("Branch: renamed b to new/name", db.getReflogReader(
689 				"new/name").getLastEntry().getComment());
690 		assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
691 		assertEquals(rb, db.resolve(Constants.HEAD));
692 		assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size());
693 		assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name").getReverseEntries().get(0).getComment());
694 		assertEquals("Just a message", db.getReflogReader("new/name").getReverseEntries().get(1).getComment());
695 	}
696 
697 	@Test
698 	public void testRenameBranchAlsoInPack() throws IOException {
699 		ObjectId rb = db.resolve("refs/heads/b");
700 		ObjectId rb2 = db.resolve("refs/heads/b~1");
701 		assertEquals(Ref.Storage.PACKED, db.exactRef("refs/heads/b").getStorage());
702 		RefUpdate updateRef = db.updateRef("refs/heads/b");
703 		updateRef.setNewObjectId(rb2);
704 		updateRef.setForceUpdate(true);
705 		Result update = updateRef.update();
706 		assertEquals("internal check new ref is loose", Result.FORCED, update);
707 		assertEquals(Ref.Storage.LOOSE, db.exactRef("refs/heads/b").getStorage());
708 		writeReflog(db, rb, "Just a message", "refs/heads/b");
709 		assertTrue("log on old branch", new File(db.getDirectory(),
710 				"logs/refs/heads/b").exists());
711 		RefRename renameRef = db.renameRef("refs/heads/b",
712 				"refs/heads/new/name");
713 		Result result = renameRef.rename();
714 		assertEquals(Result.RENAMED, result);
715 		assertEquals(rb2, db.resolve("refs/heads/new/name"));
716 		assertNull(db.resolve("refs/heads/b"));
717 		assertEquals("Branch: renamed b to new/name", db.getReflogReader(
718 				"new/name").getLastEntry().getComment());
719 		assertEquals(3, db.getReflogReader("refs/heads/new/name").getReverseEntries().size());
720 		assertEquals("Branch: renamed b to new/name", db.getReflogReader("refs/heads/new/name").getReverseEntries().get(0).getComment());
721 		assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
722 		// make sure b's log file is gone too.
723 		assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
724 
725 		// Create new Repository instance, to reread caches and make sure our
726 		// assumptions are persistent.
727 		try (Repository ndb = new FileRepository(db.getDirectory())) {
728 			assertEquals(rb2, ndb.resolve("refs/heads/new/name"));
729 			assertNull(ndb.resolve("refs/heads/b"));
730 		}
731 	}
732 
733 	public void tryRenameWhenLocked(String toLock, String fromName,
734 			String toName, String headPointsTo) throws IOException {
735 		// setup
736 		writeSymref(Constants.HEAD, headPointsTo);
737 		ObjectId oldfromId = db.resolve(fromName);
738 		ObjectId oldHeadId = db.resolve(Constants.HEAD);
739 		writeReflog(db, oldfromId, "Just a message", fromName);
740 		List<ReflogEntry> oldFromLog = db
741 				.getReflogReader(fromName).getReverseEntries();
742 		List<ReflogEntry> oldHeadLog = oldHeadId != null ? db
743 				.getReflogReader(Constants.HEAD).getReverseEntries() : null;
744 
745 		assertTrue("internal check, we have a log", new File(db.getDirectory(),
746 				"logs/" + fromName).exists());
747 
748 		// "someone" has branch X locked
749 		LockFile lockFile = new LockFile(new File(db.getDirectory(), toLock));
750 		try {
751 			assertTrue(lockFile.lock());
752 
753 			// Now this is our test
754 			RefRename renameRef = db.renameRef(fromName, toName);
755 			Result result = renameRef.rename();
756 			assertEquals(Result.LOCK_FAILURE, result);
757 
758 			// Check that the involved refs are the same despite the failure
759 			assertExists(false, toName);
760 			if (!toLock.equals(toName))
761 				assertExists(false, toName + ".lock");
762 			assertExists(true, toLock + ".lock");
763 			if (!toLock.equals(fromName))
764 				assertExists(false, "logs/" + fromName + ".lock");
765 			assertExists(false, "logs/" + toName + ".lock");
766 			assertEquals(oldHeadId, db.resolve(Constants.HEAD));
767 			assertEquals(oldfromId, db.resolve(fromName));
768 			assertNull(db.resolve(toName));
769 			assertEquals(oldFromLog.toString(), db.getReflogReader(fromName)
770 					.getReverseEntries().toString());
771 			if (oldHeadId != null && oldHeadLog != null)
772 				assertEquals(oldHeadLog.toString(), db.getReflogReader(
773 						Constants.HEAD).getReverseEntries().toString());
774 		} finally {
775 			lockFile.unlock();
776 		}
777 	}
778 
779 	private void assertExists(boolean positive, String toName) {
780 		assertEquals(toName + (positive ? " " : " does not ") + "exist",
781 				positive, new File(db.getDirectory(), toName).exists());
782 	}
783 
784 	@Test
785 	public void testRenameBranchCannotLockAFileHEADisFromLockHEAD()
786 			throws IOException {
787 		tryRenameWhenLocked("HEAD", "refs/heads/b", "refs/heads/new/name",
788 				"refs/heads/b");
789 	}
790 
791 	@Test
792 	public void testRenameBranchCannotLockAFileHEADisFromLockFrom()
793 			throws IOException {
794 		tryRenameWhenLocked("refs/heads/b", "refs/heads/b",
795 				"refs/heads/new/name", "refs/heads/b");
796 	}
797 
798 	@Test
799 	public void testRenameBranchCannotLockAFileHEADisFromLockTo()
800 			throws IOException {
801 		tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b",
802 				"refs/heads/new/name", "refs/heads/b");
803 	}
804 
805 	@Test
806 	public void testRenameBranchCannotLockAFileHEADisToLockFrom()
807 			throws IOException {
808 		tryRenameWhenLocked("refs/heads/b", "refs/heads/b",
809 				"refs/heads/new/name", "refs/heads/new/name");
810 	}
811 
812 	@Test
813 	public void testRenameBranchCannotLockAFileHEADisToLockTo()
814 			throws IOException {
815 		tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b",
816 				"refs/heads/new/name", "refs/heads/new/name");
817 	}
818 
819 	@Test
820 	public void testRenameBranchCannotLockAFileHEADisOtherLockFrom()
821 			throws IOException {
822 		tryRenameWhenLocked("refs/heads/b", "refs/heads/b",
823 				"refs/heads/new/name", "refs/heads/a");
824 	}
825 
826 	@Test
827 	public void testRenameBranchCannotLockAFileHEADisOtherLockTo()
828 			throws IOException {
829 		tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b",
830 				"refs/heads/new/name", "refs/heads/a");
831 	}
832 
833 	@Test
834 	public void testRenameRefNameColission1avoided() throws IOException {
835 		// setup
836 		ObjectId rb = db.resolve("refs/heads/b");
837 		writeSymref(Constants.HEAD, "refs/heads/a");
838 		RefUpdate updateRef = db.updateRef("refs/heads/a");
839 		updateRef.setNewObjectId(rb);
840 		updateRef.setRefLogMessage("Setup", false);
841 		assertEquals(Result.FAST_FORWARD, updateRef.update());
842 		ObjectId oldHead = db.resolve(Constants.HEAD);
843 		assertEquals(oldHead, rb); // assumption for this test
844 		writeReflog(db, rb, "Just a message", "refs/heads/a");
845 		assertTrue("internal check, we have a log", new File(db.getDirectory(),
846 				"logs/refs/heads/a").exists());
847 
848 		// Now this is our test
849 		RefRename renameRef = db.renameRef("refs/heads/a", "refs/heads/a/b");
850 		Result result = renameRef.rename();
851 		assertEquals(Result.RENAMED, result);
852 		assertNull(db.resolve("refs/heads/a"));
853 		assertEquals(rb, db.resolve("refs/heads/a/b"));
854 		assertEquals(3, db.getReflogReader("a/b").getReverseEntries().size());
855 		assertEquals("Branch: renamed a to a/b", db.getReflogReader("a/b")
856 				.getReverseEntries().get(0).getComment());
857 		assertEquals("Just a message", db.getReflogReader("a/b")
858 				.getReverseEntries().get(1).getComment());
859 		assertEquals("Setup", db.getReflogReader("a/b").getReverseEntries()
860 				.get(2).getComment());
861 		// same thing was logged to HEAD
862 		assertEquals("Branch: renamed a to a/b", db.getReflogReader("HEAD")
863 				.getReverseEntries().get(0).getComment());
864 	}
865 
866 	@Test
867 	public void testRenameRefNameColission2avoided() throws IOException {
868 		// setup
869 		ObjectId rb = db.resolve("refs/heads/b");
870 		writeSymref(Constants.HEAD, "refs/heads/prefix/a");
871 		RefUpdate updateRef = db.updateRef("refs/heads/prefix/a");
872 		updateRef.setNewObjectId(rb);
873 		updateRef.setRefLogMessage("Setup", false);
874 		updateRef.setForceUpdate(true);
875 		assertEquals(Result.FORCED, updateRef.update());
876 		ObjectId oldHead = db.resolve(Constants.HEAD);
877 		assertEquals(oldHead, rb); // assumption for this test
878 		writeReflog(db, rb, "Just a message", "refs/heads/prefix/a");
879 		assertTrue("internal check, we have a log", new File(db.getDirectory(),
880 				"logs/refs/heads/prefix/a").exists());
881 
882 		// Now this is our test
883 		RefRename renameRef = db.renameRef("refs/heads/prefix/a",
884 				"refs/heads/prefix");
885 		Result result = renameRef.rename();
886 		assertEquals(Result.RENAMED, result);
887 
888 		assertNull(db.resolve("refs/heads/prefix/a"));
889 		assertEquals(rb, db.resolve("refs/heads/prefix"));
890 		assertEquals(3, db.getReflogReader("prefix").getReverseEntries().size());
891 		assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader(
892 				"prefix").getReverseEntries().get(0).getComment());
893 		assertEquals("Just a message", db.getReflogReader("prefix")
894 				.getReverseEntries().get(1).getComment());
895 		assertEquals("Setup", db.getReflogReader("prefix").getReverseEntries()
896 				.get(2).getComment());
897 		assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader(
898 				"HEAD").getReverseEntries().get(0).getComment());
899 	}
900 
901 	private static void writeReflog(Repository db, ObjectId newId, String msg,
902 			String refName) throws IOException {
903 		RefDirectory refs = (RefDirectory) db.getRefDatabase();
904 		RefDirectoryUpdate update = refs.newUpdate(refName, true);
905 		update.setNewObjectId(newId);
906 		refs.log(update, msg, true);
907 	}
908 
909 	private static class SubclassedId extends ObjectId {
910 		SubclassedId(AnyObjectId src) {
911 			super(src);
912 		}
913 	}
914 }