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 java.nio.charset.StandardCharsets.UTF_8;
50  import static org.eclipse.jgit.lib.Constants.LOCK_SUFFIX;
51  import static org.junit.Assert.assertEquals;
52  import static org.junit.Assert.assertFalse;
53  import static org.junit.Assert.assertNotNull;
54  import static org.junit.Assert.assertNotSame;
55  import static org.junit.Assert.assertNull;
56  import static org.junit.Assert.assertSame;
57  import static org.junit.Assert.assertTrue;
58  import static org.junit.Assert.fail;
59  
60  import java.io.File;
61  import java.io.IOException;
62  import java.util.List;
63  import java.util.Map;
64  import java.util.Map.Entry;
65  import java.util.Optional;
66  
67  import org.eclipse.jgit.lib.AnyObjectId;
68  import org.eclipse.jgit.lib.Constants;
69  import org.eclipse.jgit.lib.ObjectId;
70  import org.eclipse.jgit.lib.ObjectInserter;
71  import org.eclipse.jgit.lib.PersonIdent;
72  import org.eclipse.jgit.lib.Ref;
73  import org.eclipse.jgit.lib.RefRename;
74  import org.eclipse.jgit.lib.RefUpdate;
75  import org.eclipse.jgit.lib.RefUpdate.Result;
76  import org.eclipse.jgit.lib.ReflogEntry;
77  import org.eclipse.jgit.lib.ReflogReader;
78  import org.eclipse.jgit.lib.Repository;
79  import org.eclipse.jgit.revwalk.RevCommit;
80  import org.eclipse.jgit.revwalk.RevWalk;
81  import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
82  import org.junit.Test;
83  
84  public class RefUpdateTest extends SampleDataRepositoryTestCase {
85  
86  	private void writeSymref(String src, String dst) throws IOException {
87  		RefUpdate u = db.updateRef(src);
88  		switch (u.link(dst)) {
89  		case NEW:
90  		case FORCED:
91  		case NO_CHANGE:
92  			break;
93  		default:
94  			fail("link " + src + " to " + dst);
95  		}
96  	}
97  
98  	private RefUpdate updateRef(String name) throws IOException {
99  		final RefUpdate ref = db.updateRef(name);
100 		ref.setNewObjectId(db.resolve(Constants.HEAD));
101 		return ref;
102 	}
103 
104 	private void delete(RefUpdate ref, Result expected)
105 			throws IOException {
106 		delete(ref, expected, true, true);
107 	}
108 
109 	private void delete(final RefUpdate ref, final Result expected,
110 			final boolean exists, final boolean removed) throws IOException {
111 		delete(db, ref, expected, exists, removed);
112 	}
113 
114 	private void delete(Repository repo, final RefUpdate ref,
115 			final Result expected, final boolean exists, final boolean removed)
116 			throws IOException {
117 		assertEquals(exists, getRef(repo, ref.getName()).isPresent());
118 		assertEquals(expected, ref.delete());
119 		assertEquals(!removed, getRef(repo, ref.getName()).isPresent());
120 	}
121 
122 	private Optional<Ref> getRef(Repository repo, String name)
123 			throws IOException {
124 		return getRef(repo.getRefDatabase().getRefs(), name);
125 	}
126 
127 	private Optional<Ref> getRef(List<Ref> refs, String name) {
128 		return refs.stream().filter(r -> r.getName().equals(name)).findAny();
129 	}
130 
131 	@Test
132 	public void testNoCacheObjectIdSubclass() throws IOException {
133 		final String newRef = "refs/heads/abc";
134 		final RefUpdate ru = updateRef(newRef);
135 		final SubclassedId newid = new SubclassedId(ru.getNewObjectId());
136 		ru.setNewObjectId(newid);
137 		Result update = ru.update();
138 		assertEquals(Result.NEW, update);
139 		final Ref r = getRef(db, newRef).get();
140 		assertEquals(newRef, r.getName());
141 		assertNotNull(r.getObjectId());
142 		assertNotSame(newid, r.getObjectId());
143 		assertSame(ObjectId.class, r.getObjectId().getClass());
144 		assertEquals(newid, r.getObjectId());
145 		List<ReflogEntry> reverseEntries1 = db
146 				.getReflogReader("refs/heads/abc").getReverseEntries();
147 		ReflogEntry entry1 = reverseEntries1.get(0);
148 		assertEquals(1, reverseEntries1.size());
149 		assertEquals(ObjectId.zeroId(), entry1.getOldId());
150 		assertEquals(r.getObjectId(), entry1.getNewId());
151 		assertEquals(new PersonIdent(db).toString(),  entry1.getWho().toString());
152 		assertEquals("", entry1.getComment());
153 		List<ReflogEntry> reverseEntries2 = db.getReflogReader("HEAD")
154 				.getReverseEntries();
155 		assertEquals(0, reverseEntries2.size());
156 	}
157 
158 	@Test
159 	public void testNewNamespaceConflictWithLoosePrefixNameExists()
160 			throws IOException {
161 		final String newRef = "refs/heads/z";
162 		final RefUpdate ru = updateRef(newRef);
163 		Result update = ru.update();
164 		assertEquals(Result.NEW, update);
165 		// end setup
166 		final String newRef2 = "refs/heads/z/a";
167 		final RefUpdate ru2 = updateRef(newRef2);
168 		Result update2 = ru2.update();
169 		assertEquals(Result.LOCK_FAILURE, update2);
170 		assertEquals(1, db.getReflogReader("refs/heads/z").getReverseEntries().size());
171 		assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
172 	}
173 
174 	@Test
175 	public void testNewNamespaceConflictWithPackedPrefixNameExists()
176 			throws IOException {
177 		final String newRef = "refs/heads/master/x";
178 		final RefUpdate ru = updateRef(newRef);
179 		Result update = ru.update();
180 		assertEquals(Result.LOCK_FAILURE, update);
181 		assertNull(db.getReflogReader("refs/heads/master/x"));
182 		assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
183 	}
184 
185 	@Test
186 	public void testNewNamespaceConflictWithLoosePrefixOfExisting()
187 			throws IOException {
188 		final String newRef = "refs/heads/z/a";
189 		final RefUpdate ru = updateRef(newRef);
190 		Result update = ru.update();
191 		assertEquals(Result.NEW, update);
192 		// end setup
193 		final String newRef2 = "refs/heads/z";
194 		final RefUpdate ru2 = updateRef(newRef2);
195 		Result update2 = ru2.update();
196 		assertEquals(Result.LOCK_FAILURE, update2);
197 		assertEquals(1, db.getReflogReader("refs/heads/z/a").getReverseEntries().size());
198 		assertNull(db.getReflogReader("refs/heads/z"));
199 		assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
200 	}
201 
202 	@Test
203 	public void testNewNamespaceConflictWithPackedPrefixOfExisting()
204 			throws IOException {
205 		final String newRef = "refs/heads/prefix";
206 		final RefUpdate ru = updateRef(newRef);
207 		Result update = ru.update();
208 		assertEquals(Result.LOCK_FAILURE, update);
209 		assertNull(db.getReflogReader("refs/heads/prefix"));
210 		assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
211 	}
212 
213 	/**
214 	 * Delete a ref that is pointed to by HEAD
215 	 *
216 	 * @throws IOException
217 	 */
218 	@Test
219 	public void testDeleteHEADreferencedRef() throws IOException {
220 		ObjectId pid = db.resolve("refs/heads/master^");
221 		RefUpdate updateRef = db.updateRef("refs/heads/master");
222 		updateRef.setNewObjectId(pid);
223 		updateRef.setForceUpdate(true);
224 		Result update = updateRef.update();
225 		assertEquals(Result.FORCED, update); // internal
226 
227 		RefUpdate updateRef2 = db.updateRef("refs/heads/master");
228 		Result delete = updateRef2.delete();
229 		assertEquals(Result.REJECTED_CURRENT_BRANCH, delete);
230 		assertEquals(pid, db.resolve("refs/heads/master"));
231 		assertEquals(1,db.getReflogReader("refs/heads/master").getReverseEntries().size());
232 		assertEquals(0,db.getReflogReader("HEAD").getReverseEntries().size());
233 	}
234 
235 	@Test
236 	public void testLooseDelete() throws IOException {
237 		final String newRef = "refs/heads/abc";
238 		RefUpdate ref = updateRef(newRef);
239 		ref.update(); // create loose ref
240 		ref = updateRef(newRef); // refresh
241 		delete(ref, Result.NO_CHANGE);
242 		assertNull(db.getReflogReader("refs/heads/abc"));
243 	}
244 
245 	@Test
246 	public void testDeleteHead() throws IOException {
247 		final RefUpdate ref = updateRef(Constants.HEAD);
248 		delete(ref, Result.REJECTED_CURRENT_BRANCH, true, false);
249 		assertEquals(0, db.getReflogReader("refs/heads/master").getReverseEntries().size());
250 		assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
251 	}
252 
253 	@Test
254 	public void testDeleteHeadInBareRepo() throws IOException {
255 		Repository bareRepo = createBareRepository();
256 		String master = "refs/heads/master";
257 		Ref head = bareRepo.exactRef(Constants.HEAD);
258 		assertNotNull(head);
259 		assertTrue(head.isSymbolic());
260 		assertEquals(master, head.getLeaf().getName());
261 		assertNull(head.getObjectId());
262 		assertNull(bareRepo.exactRef(master));
263 
264 		ObjectId blobId;
265 		try (ObjectInserter ins = bareRepo.newObjectInserter()) {
266 			blobId = ins.insert(Constants.OBJ_BLOB, "contents".getBytes(UTF_8));
267 			ins.flush();
268 		}
269 
270 		// Create master via HEAD, so we delete it.
271 		RefUpdate ref = bareRepo.updateRef(Constants.HEAD);
272 		ref.setNewObjectId(blobId);
273 		assertEquals(Result.NEW, ref.update());
274 
275 		head = bareRepo.exactRef(Constants.HEAD);
276 		assertTrue(head.isSymbolic());
277 		assertEquals(master, head.getLeaf().getName());
278 		assertEquals(blobId, head.getLeaf().getObjectId());
279 		assertEquals(blobId, bareRepo.exactRef(master).getObjectId());
280 
281 		// Unlike in a non-bare repo, deleting the HEAD is allowed, and leaves HEAD
282 		// back in a dangling state.
283 		ref = bareRepo.updateRef(Constants.HEAD);
284 		ref.setExpectedOldObjectId(blobId);
285 		ref.setForceUpdate(true);
286 		delete(bareRepo, ref, Result.FORCED, true, true);
287 
288 		head = bareRepo.exactRef(Constants.HEAD);
289 		assertNotNull(head);
290 		assertTrue(head.isSymbolic());
291 		assertEquals(master, head.getLeaf().getName());
292 		assertNull(head.getObjectId());
293 		assertNull(bareRepo.exactRef(master));
294 	}
295 
296 	@Test
297 	public void testDeleteSymref() throws IOException {
298 		RefUpdate dst = updateRef("refs/heads/abc");
299 		assertEquals(Result.NEW, dst.update());
300 		ObjectId id = dst.getNewObjectId();
301 
302 		RefUpdate u = db.updateRef("refs/symref");
303 		assertEquals(Result.NEW, u.link(dst.getName()));
304 
305 		Ref ref = db.exactRef(u.getName());
306 		assertNotNull(ref);
307 		assertTrue(ref.isSymbolic());
308 		assertEquals(dst.getName(), ref.getLeaf().getName());
309 		assertEquals(id, ref.getLeaf().getObjectId());
310 
311 		u = db.updateRef(u.getName());
312 		u.setDetachingSymbolicRef();
313 		u.setForceUpdate(true);
314 		assertEquals(Result.FORCED, u.delete());
315 
316 		assertNull(db.exactRef(u.getName()));
317 		ref = db.exactRef(dst.getName());
318 		assertNotNull(ref);
319 		assertFalse(ref.isSymbolic());
320 		assertEquals(id, ref.getObjectId());
321 	}
322 
323 	/**
324 	 * Delete a loose ref and make sure the directory in refs is deleted too,
325 	 * and the reflog dir too
326 	 *
327 	 * @throws IOException
328 	 */
329 	@Test
330 	public void testDeleteLooseAndItsDirectory() throws IOException {
331 		ObjectId pid = db.resolve("refs/heads/c^");
332 		RefUpdate updateRef = db.updateRef("refs/heads/z/c");
333 		updateRef.setNewObjectId(pid);
334 		updateRef.setForceUpdate(true);
335 		updateRef.setRefLogMessage("new test ref", false);
336 		Result update = updateRef.update();
337 		assertEquals(Result.NEW, update); // internal
338 		assertTrue(new File(db.getDirectory(), Constants.R_HEADS + "z")
339 				.exists());
340 		assertTrue(new File(db.getDirectory(), "logs/refs/heads/z").exists());
341 
342 		// The real test here
343 		RefUpdate updateRef2 = db.updateRef("refs/heads/z/c");
344 		updateRef2.setForceUpdate(true);
345 		Result delete = updateRef2.delete();
346 		assertEquals(Result.FORCED, delete);
347 		assertNull(db.resolve("refs/heads/z/c"));
348 		assertFalse(new File(db.getDirectory(), Constants.R_HEADS + "z")
349 				.exists());
350 		assertFalse(new File(db.getDirectory(), "logs/refs/heads/z").exists());
351 	}
352 
353 	@Test
354 	public void testDeleteNotFound() throws IOException {
355 		final RefUpdate ref = updateRef("refs/heads/xyz");
356 		delete(ref, Result.NEW, false, true);
357 	}
358 
359 	@Test
360 	public void testDeleteFastForward() throws IOException {
361 		final RefUpdate ref = updateRef("refs/heads/a");
362 		delete(ref, Result.FAST_FORWARD);
363 	}
364 
365 	@Test
366 	public void testDeleteForce() throws IOException {
367 		final RefUpdate ref = db.updateRef("refs/heads/b");
368 		ref.setNewObjectId(db.resolve("refs/heads/a"));
369 		delete(ref, Result.REJECTED, true, false);
370 		ref.setForceUpdate(true);
371 		delete(ref, Result.FORCED);
372 	}
373 
374 	@Test
375 	public void testDeleteWithoutHead() throws IOException {
376 		// Prepare repository without HEAD
377 		RefUpdate refUpdate = db.updateRef(Constants.HEAD, true);
378 		refUpdate.setForceUpdate(true);
379 		refUpdate.setNewObjectId(ObjectId.zeroId());
380 		Result updateResult = refUpdate.update();
381 		assertEquals(Result.FORCED, updateResult);
382 		Result deleteHeadResult = db.updateRef(Constants.HEAD).delete();
383 		assertEquals(Result.NO_CHANGE, deleteHeadResult);
384 
385 		// Any result is ok as long as it's not an NPE
386 		db.updateRef(Constants.R_HEADS + "master").delete();
387 	}
388 
389 	@Test
390 	public void testRefKeySameAsName() {
391 		@SuppressWarnings("deprecation")
392 		Map<String, Ref> allRefs = db.getAllRefs();
393 		for (Entry<String, Ref> e : allRefs.entrySet()) {
394 			assertEquals(e.getKey(), e.getValue().getName());
395 		}
396 	}
397 
398 	/**
399 	 * Try modify a ref forward, fast forward
400 	 *
401 	 * @throws IOException
402 	 */
403 	@Test
404 	public void testUpdateRefForward() throws IOException {
405 		ObjectId ppid = db.resolve("refs/heads/master^");
406 		ObjectId pid = db.resolve("refs/heads/master");
407 
408 		RefUpdate updateRef = db.updateRef("refs/heads/master");
409 		updateRef.setNewObjectId(ppid);
410 		updateRef.setForceUpdate(true);
411 		Result update = updateRef.update();
412 		assertEquals(Result.FORCED, update);
413 		assertEquals(ppid, db.resolve("refs/heads/master"));
414 
415 		// real test
416 		RefUpdate updateRef2 = db.updateRef("refs/heads/master");
417 		updateRef2.setNewObjectId(pid);
418 		Result update2 = updateRef2.update();
419 		assertEquals(Result.FAST_FORWARD, update2);
420 		assertEquals(pid, db.resolve("refs/heads/master"));
421 	}
422 
423 	/**
424 	 * Update the HEAD ref. Only it should be changed, not what it points to.
425 	 *
426 	 * @throws Exception
427 	 */
428 	@Test
429 	public void testUpdateRefDetached() throws Exception {
430 		ObjectId pid = db.resolve("refs/heads/master");
431 		ObjectId ppid = db.resolve("refs/heads/master^");
432 		RefUpdate updateRef = db.updateRef("HEAD", true);
433 		updateRef.setForceUpdate(true);
434 		updateRef.setNewObjectId(ppid);
435 		Result update = updateRef.update();
436 		assertEquals(Result.FORCED, update);
437 		assertEquals(ppid, db.resolve("HEAD"));
438 		Ref ref = db.exactRef("HEAD");
439 		assertEquals("HEAD", ref.getName());
440 		assertTrue("is detached", !ref.isSymbolic());
441 
442 		// the branch HEAD referred to is left untouched
443 		assertEquals(pid, db.resolve("refs/heads/master"));
444 		ReflogReader reflogReader = db.getReflogReader("HEAD");
445 		ReflogEntry e = reflogReader.getReverseEntries().get(0);
446 		assertEquals(pid, e.getOldId());
447 		assertEquals(ppid, e.getNewId());
448 		assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
449 		assertEquals("GIT_COMMITTER_NAME", e.getWho().getName());
450 		assertEquals(1250379778000L, e.getWho().getWhen().getTime());
451 	}
452 
453 	/**
454 	 * Update the HEAD ref when the referenced branch is unborn
455 	 *
456 	 * @throws Exception
457 	 */
458 	@Test
459 	public void testUpdateRefDetachedUnbornHead() throws Exception {
460 		ObjectId ppid = db.resolve("refs/heads/master^");
461 		writeSymref("HEAD", "refs/heads/unborn");
462 		RefUpdate updateRef = db.updateRef("HEAD", true);
463 		updateRef.setForceUpdate(true);
464 		updateRef.setNewObjectId(ppid);
465 		Result update = updateRef.update();
466 		assertEquals(Result.NEW, update);
467 		assertEquals(ppid, db.resolve("HEAD"));
468 		Ref ref = db.exactRef("HEAD");
469 		assertEquals("HEAD", ref.getName());
470 		assertTrue("is detached", !ref.isSymbolic());
471 
472 		// the branch HEAD referred to is left untouched
473 		assertNull(db.resolve("refs/heads/unborn"));
474 		ReflogReader reflogReader = db.getReflogReader("HEAD");
475 		ReflogEntry e = reflogReader.getReverseEntries().get(0);
476 		assertEquals(ObjectId.zeroId(), e.getOldId());
477 		assertEquals(ppid, e.getNewId());
478 		assertEquals("GIT_COMMITTER_EMAIL", e.getWho().getEmailAddress());
479 		assertEquals("GIT_COMMITTER_NAME", e.getWho().getName());
480 		assertEquals(1250379778000L, e.getWho().getWhen().getTime());
481 	}
482 
483 	/**
484 	 * Delete a ref that exists both as packed and loose. Make sure the ref
485 	 * cannot be resolved after delete.
486 	 *
487 	 * @throws IOException
488 	 */
489 	@Test
490 	public void testDeleteLoosePacked() throws IOException {
491 		ObjectId pid = db.resolve("refs/heads/c^");
492 		RefUpdate updateRef = db.updateRef("refs/heads/c");
493 		updateRef.setNewObjectId(pid);
494 		updateRef.setForceUpdate(true);
495 		Result update = updateRef.update();
496 		assertEquals(Result.FORCED, update); // internal
497 
498 		// The real test here
499 		RefUpdate updateRef2 = db.updateRef("refs/heads/c");
500 		updateRef2.setForceUpdate(true);
501 		Result delete = updateRef2.delete();
502 		assertEquals(Result.FORCED, delete);
503 		assertNull(db.resolve("refs/heads/c"));
504 	}
505 
506 	/**
507 	 * Try modify a ref to same
508 	 *
509 	 * @throws IOException
510 	 */
511 	@Test
512 	public void testUpdateRefNoChange() throws IOException {
513 		ObjectId pid = db.resolve("refs/heads/master");
514 		RefUpdate updateRef = db.updateRef("refs/heads/master");
515 		updateRef.setNewObjectId(pid);
516 		Result update = updateRef.update();
517 		assertEquals(Result.NO_CHANGE, update);
518 		assertEquals(pid, db.resolve("refs/heads/master"));
519 	}
520 
521 	/**
522 	 * Test case originating from
523 	 * <a href="http://bugs.eclipse.org/285991">bug 285991</a>
524 	 *
525 	 * Make sure the in memory cache is updated properly after
526 	 * update of symref. This one did not fail because the
527 	 * ref was packed due to implementation issues.
528 	 *
529 	 * @throws Exception
530 	 */
531 	@Test
532 	public void testRefsCacheAfterUpdate() throws Exception {
533 		// Do not use the default repo for this case.
534 		List<Ref> allRefs = db.getRefDatabase().getRefs();
535 		ObjectId oldValue = db.resolve("HEAD");
536 		ObjectId newValue = db.resolve("HEAD^");
537 		// first make HEAD refer to loose ref
538 		RefUpdate updateRef = db.updateRef(Constants.HEAD);
539 		updateRef.setForceUpdate(true);
540 		updateRef.setNewObjectId(newValue);
541 		Result update = updateRef.update();
542 		assertEquals(Result.FORCED, update);
543 
544 		// now update that ref
545 		updateRef = db.updateRef(Constants.HEAD);
546 		updateRef.setNewObjectId(oldValue);
547 		update = updateRef.update();
548 		assertEquals(Result.FAST_FORWARD, update);
549 
550 		allRefs = db.getRefDatabase().getRefs();
551 		Ref master = getRef(allRefs, "refs/heads/master").get();
552 		Ref head = getRef(allRefs, "HEAD").get();
553 		assertEquals("refs/heads/master", master.getName());
554 		assertEquals("HEAD", head.getName());
555 		assertTrue("is symbolic reference", head.isSymbolic());
556 		assertSame(master, head.getTarget());
557 	}
558 
559 	/**
560 	 * Test case originating from
561 	 * <a href="http://bugs.eclipse.org/285991">bug 285991</a>
562 	 *
563 	 * Make sure the in memory cache is updated properly after
564 	 * update of symref.
565 	 *
566 	 * @throws Exception
567 	 */
568 	@Test
569 	public void testRefsCacheAfterUpdateLooseOnly() throws Exception {
570 		// Do not use the default repo for this case.
571 		List<Ref> allRefs = db.getRefDatabase().getRefs();
572 		ObjectId oldValue = db.resolve("HEAD");
573 		writeSymref(Constants.HEAD, "refs/heads/newref");
574 		RefUpdate updateRef = db.updateRef(Constants.HEAD);
575 		updateRef.setForceUpdate(true);
576 		updateRef.setNewObjectId(oldValue);
577 		Result update = updateRef.update();
578 		assertEquals(Result.NEW, update);
579 
580 		allRefs = db.getRefDatabase().getRefs();
581 		Ref head = getRef(allRefs, "HEAD").get();
582 		Ref newref = getRef(allRefs, "refs/heads/newref").get();
583 		assertEquals("refs/heads/newref", newref.getName());
584 		assertEquals("HEAD", head.getName());
585 		assertTrue("is symbolic reference", head.isSymbolic());
586 		assertSame(newref, head.getTarget());
587 	}
588 
589 	/**
590 	 * Try modify a ref, but get wrong expected old value
591 	 *
592 	 * @throws IOException
593 	 */
594 	@Test
595 	public void testUpdateRefLockFailureWrongOldValue() throws IOException {
596 		ObjectId pid = db.resolve("refs/heads/master");
597 		RefUpdate updateRef = db.updateRef("refs/heads/master");
598 		updateRef.setNewObjectId(pid);
599 		updateRef.setExpectedOldObjectId(db.resolve("refs/heads/master^"));
600 		Result update = updateRef.update();
601 		assertEquals(Result.LOCK_FAILURE, update);
602 		assertEquals(pid, db.resolve("refs/heads/master"));
603 	}
604 
605 	/**
606 	 * Try modify a ref forward, fast forward, checking old value first
607 	 *
608 	 * @throws IOException
609 	 */
610 	@Test
611 	public void testUpdateRefForwardWithCheck1() throws IOException {
612 		ObjectId ppid = db.resolve("refs/heads/master^");
613 		ObjectId pid = db.resolve("refs/heads/master");
614 
615 		RefUpdate updateRef = db.updateRef("refs/heads/master");
616 		updateRef.setNewObjectId(ppid);
617 		updateRef.setForceUpdate(true);
618 		Result update = updateRef.update();
619 		assertEquals(Result.FORCED, update);
620 		assertEquals(ppid, db.resolve("refs/heads/master"));
621 
622 		// real test
623 		RefUpdate updateRef2 = db.updateRef("refs/heads/master");
624 		updateRef2.setExpectedOldObjectId(ppid);
625 		updateRef2.setNewObjectId(pid);
626 		Result update2 = updateRef2.update();
627 		assertEquals(Result.FAST_FORWARD, update2);
628 		assertEquals(pid, db.resolve("refs/heads/master"));
629 	}
630 
631 	/**
632 	 * Try modify a ref forward, fast forward, checking old commit first
633 	 *
634 	 * @throws IOException
635 	 */
636 	@Test
637 	public void testUpdateRefForwardWithCheck2() throws IOException {
638 		ObjectId ppid = db.resolve("refs/heads/master^");
639 		ObjectId pid = db.resolve("refs/heads/master");
640 
641 		RefUpdate updateRef = db.updateRef("refs/heads/master");
642 		updateRef.setNewObjectId(ppid);
643 		updateRef.setForceUpdate(true);
644 		Result update = updateRef.update();
645 		assertEquals(Result.FORCED, update);
646 		assertEquals(ppid, db.resolve("refs/heads/master"));
647 
648 		// real test
649 		try (RevWalk rw = new RevWalk(db)) {
650 			RevCommit old = rw.parseCommit(ppid);
651 			RefUpdate updateRef2 = db.updateRef("refs/heads/master");
652 			updateRef2.setExpectedOldObjectId(old);
653 			updateRef2.setNewObjectId(pid);
654 			Result update2 = updateRef2.update();
655 			assertEquals(Result.FAST_FORWARD, update2);
656 			assertEquals(pid, db.resolve("refs/heads/master"));
657 		}
658 	}
659 
660 	/**
661 	 * Try modify a ref that is locked
662 	 *
663 	 * @throws IOException
664 	 */
665 	@Test
666 	public void testUpdateRefLockFailureLocked() throws IOException {
667 		ObjectId opid = db.resolve("refs/heads/master");
668 		ObjectId pid = db.resolve("refs/heads/master^");
669 		RefUpdate updateRef = db.updateRef("refs/heads/master");
670 		updateRef.setNewObjectId(pid);
671 		LockFile lockFile1 = new LockFile(new File(db.getDirectory(),
672 				"refs/heads/master"));
673 		try {
674 			assertTrue(lockFile1.lock()); // precondition to test
675 			Result update = updateRef.update();
676 			assertEquals(Result.LOCK_FAILURE, update);
677 			assertEquals(opid, db.resolve("refs/heads/master"));
678 			LockFile lockFile2 = new LockFile(new File(db.getDirectory(),"refs/heads/master"));
679 			assertFalse(lockFile2.lock()); // was locked, still is
680 		} finally {
681 			lockFile1.unlock();
682 		}
683 	}
684 
685 	/**
686 	 * Try to delete a ref. Delete requires force.
687 	 *
688 	 * @throws IOException
689 	 */
690 	@Test
691 	public void testDeleteLoosePackedRejected() throws IOException {
692 		ObjectId pid = db.resolve("refs/heads/c^");
693 		ObjectId oldpid = db.resolve("refs/heads/c");
694 		RefUpdate updateRef = db.updateRef("refs/heads/c");
695 		updateRef.setNewObjectId(pid);
696 		Result update = updateRef.update();
697 		assertEquals(Result.REJECTED, update);
698 		assertEquals(oldpid, db.resolve("refs/heads/c"));
699 	}
700 
701 	@Test
702 	public void testRenameBranchNoPreviousLog() throws IOException {
703 		assertFalse("precondition, no log on old branchg", new File(db
704 				.getDirectory(), "logs/refs/heads/b").exists());
705 		ObjectId rb = db.resolve("refs/heads/b");
706 		ObjectId oldHead = db.resolve(Constants.HEAD);
707 		assertFalse(rb.equals(oldHead)); // assumption for this test
708 		RefRename renameRef = db.renameRef("refs/heads/b",
709 				"refs/heads/new/name");
710 		Result result = renameRef.rename();
711 		assertEquals(Result.RENAMED, result);
712 		assertEquals(rb, db.resolve("refs/heads/new/name"));
713 		assertNull(db.resolve("refs/heads/b"));
714 		assertEquals(1, db.getReflogReader("new/name").getReverseEntries().size());
715 		assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name")
716 				.getLastEntry().getComment());
717 		assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
718 		assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged
719 	}
720 
721 	@Test
722 	public void testRenameBranchHasPreviousLog() throws IOException {
723 		ObjectId rb = db.resolve("refs/heads/b");
724 		ObjectId oldHead = db.resolve(Constants.HEAD);
725 		assertFalse("precondition for this test, branch b != HEAD", rb
726 				.equals(oldHead));
727 		writeReflog(db, rb, "Just a message", "refs/heads/b");
728 		assertTrue("log on old branch", new File(db.getDirectory(),
729 				"logs/refs/heads/b").exists());
730 		RefRename renameRef = db.renameRef("refs/heads/b",
731 				"refs/heads/new/name");
732 		Result result = renameRef.rename();
733 		assertEquals(Result.RENAMED, result);
734 		assertEquals(rb, db.resolve("refs/heads/new/name"));
735 		assertNull(db.resolve("refs/heads/b"));
736 		assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size());
737 		assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name")
738 				.getLastEntry().getComment());
739 		assertEquals("Just a message", db.getReflogReader("new/name")
740 				.getReverseEntries().get(1).getComment());
741 		assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
742 		assertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged
743 	}
744 
745 	@Test
746 	public void testRenameCurrentBranch() throws IOException {
747 		ObjectId rb = db.resolve("refs/heads/b");
748 		writeSymref(Constants.HEAD, "refs/heads/b");
749 		ObjectId oldHead = db.resolve(Constants.HEAD);
750 		assertEquals("internal test condition, b == HEAD", oldHead, rb);
751 		writeReflog(db, rb, "Just a message", "refs/heads/b");
752 		assertTrue("log on old branch", new File(db.getDirectory(),
753 				"logs/refs/heads/b").exists());
754 		RefRename renameRef = db.renameRef("refs/heads/b",
755 				"refs/heads/new/name");
756 		Result result = renameRef.rename();
757 		assertEquals(Result.RENAMED, result);
758 		assertEquals(rb, db.resolve("refs/heads/new/name"));
759 		assertNull(db.resolve("refs/heads/b"));
760 		assertEquals("Branch: renamed b to new/name", db.getReflogReader(
761 				"new/name").getLastEntry().getComment());
762 		assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
763 		assertEquals(rb, db.resolve(Constants.HEAD));
764 		assertEquals(2, db.getReflogReader("new/name").getReverseEntries().size());
765 		assertEquals("Branch: renamed b to new/name", db.getReflogReader("new/name").getReverseEntries().get(0).getComment());
766 		assertEquals("Just a message", db.getReflogReader("new/name").getReverseEntries().get(1).getComment());
767 	}
768 
769 	@Test
770 	public void testRenameBranchAlsoInPack() throws IOException {
771 		ObjectId rb = db.resolve("refs/heads/b");
772 		ObjectId rb2 = db.resolve("refs/heads/b~1");
773 		assertEquals(Ref.Storage.PACKED, db.exactRef("refs/heads/b").getStorage());
774 		RefUpdate updateRef = db.updateRef("refs/heads/b");
775 		updateRef.setNewObjectId(rb2);
776 		updateRef.setForceUpdate(true);
777 		Result update = updateRef.update();
778 		assertEquals("internal check new ref is loose", Result.FORCED, update);
779 		assertEquals(Ref.Storage.LOOSE, db.exactRef("refs/heads/b").getStorage());
780 		writeReflog(db, rb, "Just a message", "refs/heads/b");
781 		assertTrue("log on old branch", new File(db.getDirectory(),
782 				"logs/refs/heads/b").exists());
783 		RefRename renameRef = db.renameRef("refs/heads/b",
784 				"refs/heads/new/name");
785 		Result result = renameRef.rename();
786 		assertEquals(Result.RENAMED, result);
787 		assertEquals(rb2, db.resolve("refs/heads/new/name"));
788 		assertNull(db.resolve("refs/heads/b"));
789 		assertEquals("Branch: renamed b to new/name", db.getReflogReader(
790 				"new/name").getLastEntry().getComment());
791 		assertEquals(3, db.getReflogReader("refs/heads/new/name").getReverseEntries().size());
792 		assertEquals("Branch: renamed b to new/name", db.getReflogReader("refs/heads/new/name").getReverseEntries().get(0).getComment());
793 		assertEquals(0, db.getReflogReader("HEAD").getReverseEntries().size());
794 		// make sure b's log file is gone too.
795 		assertFalse(new File(db.getDirectory(), "logs/refs/heads/b").exists());
796 
797 		// Create new Repository instance, to reread caches and make sure our
798 		// assumptions are persistent.
799 		try (Repository ndb = new FileRepository(db.getDirectory())) {
800 			assertEquals(rb2, ndb.resolve("refs/heads/new/name"));
801 			assertNull(ndb.resolve("refs/heads/b"));
802 		}
803 	}
804 
805 	public void tryRenameWhenLocked(String toLock, String fromName,
806 			String toName, String headPointsTo) throws IOException {
807 		// setup
808 		writeSymref(Constants.HEAD, headPointsTo);
809 		ObjectId oldfromId = db.resolve(fromName);
810 		ObjectId oldHeadId = db.resolve(Constants.HEAD);
811 		writeReflog(db, oldfromId, "Just a message", fromName);
812 		List<ReflogEntry> oldFromLog = db
813 				.getReflogReader(fromName).getReverseEntries();
814 		List<ReflogEntry> oldHeadLog = oldHeadId != null ? db
815 				.getReflogReader(Constants.HEAD).getReverseEntries() : null;
816 
817 		assertTrue("internal check, we have a log", new File(db.getDirectory(),
818 				"logs/" + fromName).exists());
819 
820 		// "someone" has branch X locked
821 		LockFile lockFile = new LockFile(new File(db.getDirectory(), toLock));
822 		try {
823 			assertTrue(lockFile.lock());
824 
825 			// Now this is our test
826 			RefRename renameRef = db.renameRef(fromName, toName);
827 			Result result = renameRef.rename();
828 			assertEquals(Result.LOCK_FAILURE, result);
829 
830 			// Check that the involved refs are the same despite the failure
831 			assertExists(false, toName);
832 			if (!toLock.equals(toName))
833 				assertExists(false, toName + LOCK_SUFFIX);
834 			assertExists(true, toLock + LOCK_SUFFIX);
835 			if (!toLock.equals(fromName))
836 				assertExists(false, "logs/" + fromName + LOCK_SUFFIX);
837 			assertExists(false, "logs/" + toName + LOCK_SUFFIX);
838 			assertEquals(oldHeadId, db.resolve(Constants.HEAD));
839 			assertEquals(oldfromId, db.resolve(fromName));
840 			assertNull(db.resolve(toName));
841 			assertEquals(oldFromLog.toString(), db.getReflogReader(fromName)
842 					.getReverseEntries().toString());
843 			if (oldHeadId != null && oldHeadLog != null)
844 				assertEquals(oldHeadLog.toString(), db.getReflogReader(
845 						Constants.HEAD).getReverseEntries().toString());
846 		} finally {
847 			lockFile.unlock();
848 		}
849 	}
850 
851 	private void assertExists(boolean positive, String toName) {
852 		assertEquals(toName + (positive ? " " : " does not ") + "exist",
853 				positive, new File(db.getDirectory(), toName).exists());
854 	}
855 
856 	@Test
857 	public void testRenameBranchCannotLockAFileHEADisFromLockHEAD()
858 			throws IOException {
859 		tryRenameWhenLocked("HEAD", "refs/heads/b", "refs/heads/new/name",
860 				"refs/heads/b");
861 	}
862 
863 	@Test
864 	public void testRenameBranchCannotLockAFileHEADisFromLockFrom()
865 			throws IOException {
866 		tryRenameWhenLocked("refs/heads/b", "refs/heads/b",
867 				"refs/heads/new/name", "refs/heads/b");
868 	}
869 
870 	@Test
871 	public void testRenameBranchCannotLockAFileHEADisFromLockTo()
872 			throws IOException {
873 		tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b",
874 				"refs/heads/new/name", "refs/heads/b");
875 	}
876 
877 	@Test
878 	public void testRenameBranchCannotLockAFileHEADisToLockFrom()
879 			throws IOException {
880 		tryRenameWhenLocked("refs/heads/b", "refs/heads/b",
881 				"refs/heads/new/name", "refs/heads/new/name");
882 	}
883 
884 	@Test
885 	public void testRenameBranchCannotLockAFileHEADisToLockTo()
886 			throws IOException {
887 		tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b",
888 				"refs/heads/new/name", "refs/heads/new/name");
889 	}
890 
891 	@Test
892 	public void testRenameBranchCannotLockAFileHEADisOtherLockFrom()
893 			throws IOException {
894 		tryRenameWhenLocked("refs/heads/b", "refs/heads/b",
895 				"refs/heads/new/name", "refs/heads/a");
896 	}
897 
898 	@Test
899 	public void testRenameBranchCannotLockAFileHEADisOtherLockTo()
900 			throws IOException {
901 		tryRenameWhenLocked("refs/heads/new/name", "refs/heads/b",
902 				"refs/heads/new/name", "refs/heads/a");
903 	}
904 
905 	@Test
906 	public void testRenameRefNameColission1avoided() throws IOException {
907 		// setup
908 		ObjectId rb = db.resolve("refs/heads/b");
909 		writeSymref(Constants.HEAD, "refs/heads/a");
910 		RefUpdate updateRef = db.updateRef("refs/heads/a");
911 		updateRef.setNewObjectId(rb);
912 		updateRef.setRefLogMessage("Setup", false);
913 		assertEquals(Result.FAST_FORWARD, updateRef.update());
914 		ObjectId oldHead = db.resolve(Constants.HEAD);
915 		assertEquals(oldHead, rb); // assumption for this test
916 		writeReflog(db, rb, "Just a message", "refs/heads/a");
917 		assertTrue("internal check, we have a log", new File(db.getDirectory(),
918 				"logs/refs/heads/a").exists());
919 
920 		// Now this is our test
921 		RefRename renameRef = db.renameRef("refs/heads/a", "refs/heads/a/b");
922 		Result result = renameRef.rename();
923 		assertEquals(Result.RENAMED, result);
924 		assertNull(db.resolve("refs/heads/a"));
925 		assertEquals(rb, db.resolve("refs/heads/a/b"));
926 		assertEquals(3, db.getReflogReader("a/b").getReverseEntries().size());
927 		assertEquals("Branch: renamed a to a/b", db.getReflogReader("a/b")
928 				.getReverseEntries().get(0).getComment());
929 		assertEquals("Just a message", db.getReflogReader("a/b")
930 				.getReverseEntries().get(1).getComment());
931 		assertEquals("Setup", db.getReflogReader("a/b").getReverseEntries()
932 				.get(2).getComment());
933 		// same thing was logged to HEAD
934 		assertEquals("Branch: renamed a to a/b", db.getReflogReader("HEAD")
935 				.getReverseEntries().get(0).getComment());
936 	}
937 
938 	@Test
939 	public void testRenameRefNameColission2avoided() throws IOException {
940 		// setup
941 		ObjectId rb = db.resolve("refs/heads/b");
942 		writeSymref(Constants.HEAD, "refs/heads/prefix/a");
943 		RefUpdate updateRef = db.updateRef("refs/heads/prefix/a");
944 		updateRef.setNewObjectId(rb);
945 		updateRef.setRefLogMessage("Setup", false);
946 		updateRef.setForceUpdate(true);
947 		assertEquals(Result.FORCED, updateRef.update());
948 		ObjectId oldHead = db.resolve(Constants.HEAD);
949 		assertEquals(oldHead, rb); // assumption for this test
950 		writeReflog(db, rb, "Just a message", "refs/heads/prefix/a");
951 		assertTrue("internal check, we have a log", new File(db.getDirectory(),
952 				"logs/refs/heads/prefix/a").exists());
953 
954 		// Now this is our test
955 		RefRename renameRef = db.renameRef("refs/heads/prefix/a",
956 				"refs/heads/prefix");
957 		Result result = renameRef.rename();
958 		assertEquals(Result.RENAMED, result);
959 
960 		assertNull(db.resolve("refs/heads/prefix/a"));
961 		assertEquals(rb, db.resolve("refs/heads/prefix"));
962 		assertEquals(3, db.getReflogReader("prefix").getReverseEntries().size());
963 		assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader(
964 				"prefix").getReverseEntries().get(0).getComment());
965 		assertEquals("Just a message", db.getReflogReader("prefix")
966 				.getReverseEntries().get(1).getComment());
967 		assertEquals("Setup", db.getReflogReader("prefix").getReverseEntries()
968 				.get(2).getComment());
969 		assertEquals("Branch: renamed prefix/a to prefix", db.getReflogReader(
970 				"HEAD").getReverseEntries().get(0).getComment());
971 	}
972 
973 	@Test
974 	public void testCreateMissingObject() throws IOException {
975 		String name = "refs/heads/abc";
976 		ObjectId bad =
977 				ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
978 		RefUpdate ru = db.updateRef(name);
979 		ru.setNewObjectId(bad);
980 		Result update = ru.update();
981 		assertEquals(Result.REJECTED_MISSING_OBJECT, update);
982 
983 		Ref ref = db.exactRef(name);
984 		assertNull(ref);
985 	}
986 
987 	@Test
988 	public void testUpdateMissingObject() throws IOException {
989 		String name = "refs/heads/abc";
990 		RefUpdate ru = updateRef(name);
991 		Result update = ru.update();
992 		assertEquals(Result.NEW, update);
993 		ObjectId oldId = ru.getNewObjectId();
994 
995 		ObjectId bad =
996 				ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
997 		ru = db.updateRef(name);
998 		ru.setNewObjectId(bad);
999 		update = ru.update();
1000 		assertEquals(Result.REJECTED_MISSING_OBJECT, update);
1001 
1002 		Ref ref = db.exactRef(name);
1003 		assertNotNull(ref);
1004 		assertEquals(oldId, ref.getObjectId());
1005 	}
1006 
1007 	@Test
1008 	public void testForceUpdateMissingObject() throws IOException {
1009 		String name = "refs/heads/abc";
1010 		RefUpdate ru = updateRef(name);
1011 		Result update = ru.update();
1012 		assertEquals(Result.NEW, update);
1013 		ObjectId oldId = ru.getNewObjectId();
1014 
1015 		ObjectId bad =
1016 				ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
1017 		ru = db.updateRef(name);
1018 		ru.setNewObjectId(bad);
1019 		update = ru.forceUpdate();
1020 		assertEquals(Result.REJECTED_MISSING_OBJECT, update);
1021 
1022 		Ref ref = db.exactRef(name);
1023 		assertNotNull(ref);
1024 		assertEquals(oldId, ref.getObjectId());
1025 	}
1026 
1027 	private static void writeReflog(Repository db, ObjectId newId, String msg,
1028 			String refName) throws IOException {
1029 		RefDirectory refs = (RefDirectory) db.getRefDatabase();
1030 		RefDirectoryUpdate update = refs.newUpdate(refName, true);
1031 		update.setNewObjectId(newId);
1032 		refs.log(false, update, msg, true);
1033 	}
1034 
1035 	private static class SubclassedId extends ObjectId {
1036 		SubclassedId(AnyObjectId src) {
1037 			super(src);
1038 		}
1039 	}
1040 }