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