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