View Javadoc
1   /*
2    * Copyright (C) 2008-2010, Google Inc.
3    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
4    *
5    * This program and the accompanying materials are made available under the
6    * terms of the Eclipse Distribution License v. 1.0 which is available at
7    * https://www.eclipse.org/org/documents/edl-v10.php.
8    *
9    * SPDX-License-Identifier: BSD-3-Clause
10   */
11  
12  package org.eclipse.jgit.lib;
13  
14  import java.io.IOException;
15  import java.text.MessageFormat;
16  
17  import org.eclipse.jgit.errors.MissingObjectException;
18  import org.eclipse.jgit.internal.JGitText;
19  import org.eclipse.jgit.revwalk.RevCommit;
20  import org.eclipse.jgit.revwalk.RevObject;
21  import org.eclipse.jgit.revwalk.RevWalk;
22  import org.eclipse.jgit.transport.PushCertificate;
23  import org.eclipse.jgit.util.References;
24  
25  /**
26   * Creates, updates or deletes any reference.
27   */
28  public abstract class RefUpdate {
29  	/**
30  	 * Status of an update request.
31  	 * <p>
32  	 * New values may be added to this enum in the future. Callers may assume that
33  	 * unknown values are failures, and may generally treat them the same as
34  	 * {@link #REJECTED_OTHER_REASON}.
35  	 */
36  	public enum Result {
37  		/** The ref update/delete has not been attempted by the caller. */
38  		NOT_ATTEMPTED,
39  
40  		/**
41  		 * The ref could not be locked for update/delete.
42  		 * <p>
43  		 * This is generally a transient failure and is usually caused by
44  		 * another process trying to access the ref at the same time as this
45  		 * process was trying to update it. It is possible a future operation
46  		 * will be successful.
47  		 */
48  		LOCK_FAILURE,
49  
50  		/**
51  		 * Same value already stored.
52  		 * <p>
53  		 * Both the old value and the new value are identical. No change was
54  		 * necessary for an update. For delete the branch is removed.
55  		 */
56  		NO_CHANGE,
57  
58  		/**
59  		 * The ref was created locally for an update, but ignored for delete.
60  		 * <p>
61  		 * The ref did not exist when the update started, but it was created
62  		 * successfully with the new value.
63  		 */
64  		NEW,
65  
66  		/**
67  		 * The ref had to be forcefully updated/deleted.
68  		 * <p>
69  		 * The ref already existed but its old value was not fully merged into
70  		 * the new value. The configuration permitted a forced update to take
71  		 * place, so ref now contains the new value. History associated with the
72  		 * objects not merged may no longer be reachable.
73  		 */
74  		FORCED,
75  
76  		/**
77  		 * The ref was updated/deleted in a fast-forward way.
78  		 * <p>
79  		 * The tracking ref already existed and its old value was fully merged
80  		 * into the new value. No history was made unreachable.
81  		 */
82  		FAST_FORWARD,
83  
84  		/**
85  		 * Not a fast-forward and not stored.
86  		 * <p>
87  		 * The tracking ref already existed but its old value was not fully
88  		 * merged into the new value. The configuration did not allow a forced
89  		 * update/delete to take place, so ref still contains the old value. No
90  		 * previous history was lost.
91  		 * <p>
92  		 * <em>Note:</em> Despite the general name, this result only refers to the
93  		 * non-fast-forward case. For more general errors, see {@link
94  		 * #REJECTED_OTHER_REASON}.
95  		 */
96  		REJECTED,
97  
98  		/**
99  		 * Rejected because trying to delete the current branch.
100 		 * <p>
101 		 * Has no meaning for update.
102 		 */
103 		REJECTED_CURRENT_BRANCH,
104 
105 		/**
106 		 * The ref was probably not updated/deleted because of I/O error.
107 		 * <p>
108 		 * Unexpected I/O error occurred when writing new ref. Such error may
109 		 * result in uncertain state, but most probably ref was not updated.
110 		 * <p>
111 		 * This kind of error doesn't include {@link #LOCK_FAILURE}, which is a
112 		 * different case.
113 		 */
114 		IO_FAILURE,
115 
116 		/**
117 		 * The ref was renamed from another name
118 		 * <p>
119 		 */
120 		RENAMED,
121 
122 		/**
123 		 * One or more objects aren't in the repository.
124 		 * <p>
125 		 * This is severe indication of either repository corruption on the
126 		 * server side, or a bug in the client wherein the client did not supply
127 		 * all required objects during the pack transfer.
128 		 *
129 		 * @since 4.9
130 		 */
131 		REJECTED_MISSING_OBJECT,
132 
133 		/**
134 		 * Rejected for some other reason not covered by another enum value.
135 		 *
136 		 * @since 4.9
137 		 */
138 		REJECTED_OTHER_REASON;
139 	}
140 
141 	/** New value the caller wants this ref to have. */
142 	private ObjectId newValue;
143 
144 	/** Does this specification ask for forced updated (rewind/reset)? */
145 	private boolean force;
146 
147 	/** Identity to record action as within the reflog. */
148 	private PersonIdent refLogIdent;
149 
150 	/** Message the caller wants included in the reflog. */
151 	private String refLogMessage;
152 
153 	/** Should the Result value be appended to {@link #refLogMessage}. */
154 	private boolean refLogIncludeResult;
155 
156 	/**
157 	 * Should reflogs be written even if the configured default for this ref is
158 	 * not to write it.
159 	 */
160 	private boolean forceRefLog;
161 
162 	/** Old value of the ref, obtained after we lock it. */
163 	private ObjectId oldValue;
164 
165 	/** If non-null, the value {@link #oldValue} must have to continue. */
166 	private ObjectId expValue;
167 
168 	/** Result of the update operation. */
169 	private Result result = Result.NOT_ATTEMPTED;
170 
171 	/** Push certificate associated with this update. */
172 	private PushCertificate pushCert;
173 
174 	private final Ref ref;
175 
176 	/**
177 	 * Is this RefUpdate detaching a symbolic ref?
178 	 *
179 	 * We need this info since this.ref will normally be peeled of in case of
180 	 * detaching a symbolic ref (HEAD for example).
181 	 *
182 	 * Without this flag we cannot decide whether the ref has to be updated or
183 	 * not in case when it was a symbolic ref and the newValue == oldValue.
184 	 */
185 	private boolean detachingSymbolicRef;
186 
187 	private boolean checkConflicting = true;
188 
189 	/**
190 	 * Construct a new update operation for the reference.
191 	 * <p>
192 	 * {@code ref.getObjectId()} will be used to seed {@link #getOldObjectId()},
193 	 * which callers can use as part of their own update logic.
194 	 *
195 	 * @param ref
196 	 *            the reference that will be updated by this operation.
197 	 */
198 	protected RefUpdate(Ref ref) {
199 		this.ref = ref;
200 		oldValue = ref.getObjectId();
201 		refLogMessage = ""; //$NON-NLS-1$
202 	}
203 
204 	/**
205 	 * Get the reference database this update modifies.
206 	 *
207 	 * @return the reference database this update modifies.
208 	 */
209 	protected abstract RefDatabase getRefDatabase();
210 
211 	/**
212 	 * Get the repository storing the database's objects.
213 	 *
214 	 * @return the repository storing the database's objects.
215 	 */
216 	protected abstract Repository getRepository();
217 
218 	/**
219 	 * Try to acquire the lock on the reference.
220 	 * <p>
221 	 * If the locking was successful the implementor must set the current
222 	 * identity value by calling {@link #setOldObjectId(ObjectId)}.
223 	 *
224 	 * @param deref
225 	 *            true if the lock should be taken against the leaf level
226 	 *            reference; false if it should be taken exactly against the
227 	 *            current reference.
228 	 * @return true if the lock was acquired and the reference is likely
229 	 *         protected from concurrent modification; false if it failed.
230 	 * @throws java.io.IOException
231 	 *             the lock couldn't be taken due to an unexpected storage
232 	 *             failure, and not because of a concurrent update.
233 	 */
234 	protected abstract boolean tryLock(boolean deref) throws IOException;
235 
236 	/**
237 	 * Releases the lock taken by {@link #tryLock} if it succeeded.
238 	 */
239 	protected abstract void unlock();
240 
241 	/**
242 	 * Do update
243 	 *
244 	 * @param desiredResult
245 	 *            a {@link org.eclipse.jgit.lib.RefUpdate.Result} object.
246 	 * @return {@code result}
247 	 * @throws java.io.IOException
248 	 */
249 	protected abstract Result doUpdate(Result desiredResult) throws IOException;
250 
251 	/**
252 	 * Do delete
253 	 *
254 	 * @param desiredResult
255 	 *            a {@link org.eclipse.jgit.lib.RefUpdate.Result} object.
256 	 * @return {@code result}
257 	 * @throws java.io.IOException
258 	 */
259 	protected abstract Result doDelete(Result desiredResult) throws IOException;
260 
261 	/**
262 	 * Do link
263 	 *
264 	 * @param target
265 	 *            a {@link java.lang.String} object.
266 	 * @return {@link org.eclipse.jgit.lib.RefUpdate.Result#NEW} on success.
267 	 * @throws java.io.IOException
268 	 */
269 	protected abstract Result doLink(String target) throws IOException;
270 
271 	/**
272 	 * Get the name of the ref this update will operate on.
273 	 *
274 	 * @return name of underlying ref.
275 	 */
276 	public String getName() {
277 		return getRef().getName();
278 	}
279 
280 	/**
281 	 * Get the reference this update will create or modify.
282 	 *
283 	 * @return the reference this update will create or modify.
284 	 */
285 	public Ref getRef() {
286 		return ref;
287 	}
288 
289 	/**
290 	 * Get the new value the ref will be (or was) updated to.
291 	 *
292 	 * @return new value. Null if the caller has not configured it.
293 	 */
294 	public ObjectId getNewObjectId() {
295 		return newValue;
296 	}
297 
298 	/**
299 	 * Tells this RefUpdate that it is actually detaching a symbolic ref.
300 	 */
301 	public void setDetachingSymbolicRef() {
302 		detachingSymbolicRef = true;
303 	}
304 
305 	/**
306 	 * Return whether this update is actually detaching a symbolic ref.
307 	 *
308 	 * @return true if detaching a symref.
309 	 * @since 4.9
310 	 */
311 	public boolean isDetachingSymbolicRef() {
312 		return detachingSymbolicRef;
313 	}
314 
315 	/**
316 	 * Set the new value the ref will update to.
317 	 *
318 	 * @param id
319 	 *            the new value.
320 	 */
321 	public void setNewObjectId(AnyObjectId id) {
322 		newValue = id.copy();
323 	}
324 
325 	/**
326 	 * Get the expected value of the ref after the lock is taken, but before
327 	 * update occurs.
328 	 *
329 	 * @return the expected value of the ref after the lock is taken, but before
330 	 *         update occurs. Null to avoid the compare and swap test. Use
331 	 *         {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate
332 	 *         expectation of a non-existant ref.
333 	 */
334 	public ObjectId getExpectedOldObjectId() {
335 		return expValue;
336 	}
337 
338 	/**
339 	 * Set the expected value of the ref after the lock is taken, but before
340 	 * update occurs.
341 	 *
342 	 * @param id
343 	 *            the expected value of the ref after the lock is taken, but
344 	 *            before update occurs. Null to avoid the compare and swap test.
345 	 *            Use {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate
346 	 *            expectation of a non-existant ref.
347 	 */
348 	public void setExpectedOldObjectId(AnyObjectId id) {
349 		expValue = id != null ? id.toObjectId() : null;
350 	}
351 
352 	/**
353 	 * Check if this update wants to forcefully change the ref.
354 	 *
355 	 * @return true if this update should ignore merge tests.
356 	 */
357 	public boolean isForceUpdate() {
358 		return force;
359 	}
360 
361 	/**
362 	 * Set if this update wants to forcefully change the ref.
363 	 *
364 	 * @param b
365 	 *            true if this update should ignore merge tests.
366 	 */
367 	public void setForceUpdate(boolean b) {
368 		force = b;
369 	}
370 
371 	/**
372 	 * Get identity of the user making the change in the reflog.
373 	 *
374 	 * @return identity of the user making the change in the reflog.
375 	 */
376 	public PersonIdent getRefLogIdent() {
377 		return refLogIdent;
378 	}
379 
380 	/**
381 	 * Set the identity of the user appearing in the reflog.
382 	 * <p>
383 	 * The timestamp portion of the identity is ignored. A new identity with the
384 	 * current timestamp will be created automatically when the update occurs
385 	 * and the log record is written.
386 	 *
387 	 * @param pi
388 	 *            identity of the user. If null the identity will be
389 	 *            automatically determined based on the repository
390 	 *            configuration.
391 	 */
392 	public void setRefLogIdent(PersonIdent pi) {
393 		refLogIdent = pi;
394 	}
395 
396 	/**
397 	 * Get the message to include in the reflog.
398 	 *
399 	 * @return message the caller wants to include in the reflog; null if the
400 	 *         update should not be logged.
401 	 */
402 	public String getRefLogMessage() {
403 		return refLogMessage;
404 	}
405 
406 	/**
407 	 * Whether the ref log message should show the result.
408 	 *
409 	 * @return {@code true} if the ref log message should show the result.
410 	 */
411 	protected boolean isRefLogIncludingResult() {
412 		return refLogIncludeResult;
413 	}
414 
415 	/**
416 	 * Set the message to include in the reflog.
417 	 * <p>
418 	 * Repository implementations may limit which reflogs are written by default,
419 	 * based on the project configuration. If a repo is not configured to write
420 	 * logs for this ref by default, setting the message alone may have no effect.
421 	 * To indicate that the repo should write logs for this update in spite of
422 	 * configured defaults, use {@link #setForceRefLog(boolean)}.
423 	 *
424 	 * @param msg
425 	 *            the message to describe this change. It may be null if
426 	 *            appendStatus is null in order not to append to the reflog
427 	 * @param appendStatus
428 	 *            true if the status of the ref change (fast-forward or
429 	 *            forced-update) should be appended to the user supplied
430 	 *            message.
431 	 */
432 	public void setRefLogMessage(String msg, boolean appendStatus) {
433 		if (msg == null && !appendStatus)
434 			disableRefLog();
435 		else if (msg == null && appendStatus) {
436 			refLogMessage = ""; //$NON-NLS-1$
437 			refLogIncludeResult = true;
438 		} else {
439 			refLogMessage = msg;
440 			refLogIncludeResult = appendStatus;
441 		}
442 	}
443 
444 	/**
445 	 * Don't record this update in the ref's associated reflog.
446 	 */
447 	public void disableRefLog() {
448 		refLogMessage = null;
449 		refLogIncludeResult = false;
450 	}
451 
452 	/**
453 	 * Force writing a reflog for the updated ref.
454 	 *
455 	 * @param force whether to force.
456 	 * @since 4.9
457 	 */
458 	public void setForceRefLog(boolean force) {
459 		forceRefLog = force;
460 	}
461 
462 	/**
463 	 * Check whether the reflog should be written regardless of repo defaults.
464 	 *
465 	 * @return whether force writing is enabled.
466 	 * @since 4.9
467 	 */
468 	protected boolean isForceRefLog() {
469 		return forceRefLog;
470 	}
471 
472 	/**
473 	 * The old value of the ref, prior to the update being attempted.
474 	 * <p>
475 	 * This value may differ before and after the update method. Initially it is
476 	 * populated with the value of the ref before the lock is taken, but the old
477 	 * value may change if someone else modified the ref between the time we
478 	 * last read it and when the ref was locked for update.
479 	 *
480 	 * @return the value of the ref prior to the update being attempted; null if
481 	 *         the updated has not been attempted yet.
482 	 */
483 	public ObjectId getOldObjectId() {
484 		return oldValue;
485 	}
486 
487 	/**
488 	 * Set the old value of the ref.
489 	 *
490 	 * @param old
491 	 *            the old value.
492 	 */
493 	protected void setOldObjectId(ObjectId old) {
494 		oldValue = old;
495 	}
496 
497 	/**
498 	 * Set a push certificate associated with this update.
499 	 * <p>
500 	 * This usually includes a command to update this ref, but is not required to.
501 	 *
502 	 * @param cert
503 	 *            push certificate, may be null.
504 	 * @since 4.1
505 	 */
506 	public void setPushCertificate(PushCertificate cert) {
507 		pushCert = cert;
508 	}
509 
510 	/**
511 	 * Set the push certificate associated with this update.
512 	 * <p>
513 	 * This usually includes a command to update this ref, but is not required to.
514 	 *
515 	 * @return push certificate, may be null.
516 	 * @since 4.1
517 	 */
518 	protected PushCertificate getPushCertificate() {
519 		return pushCert;
520 	}
521 
522 	/**
523 	 * Get the status of this update.
524 	 * <p>
525 	 * The same value that was previously returned from an update method.
526 	 *
527 	 * @return the status of the update.
528 	 */
529 	public Result getResult() {
530 		return result;
531 	}
532 
533 	private void requireCanDoUpdate() {
534 		if (newValue == null)
535 			throw new IllegalStateException(JGitText.get().aNewObjectIdIsRequired);
536 	}
537 
538 	/**
539 	 * Force the ref to take the new value.
540 	 * <p>
541 	 * This is just a convenient helper for setting the force flag, and as such
542 	 * the merge test is performed.
543 	 *
544 	 * @return the result status of the update.
545 	 * @throws java.io.IOException
546 	 *             an unexpected IO error occurred while writing changes.
547 	 */
548 	public Result forceUpdate() throws IOException {
549 		force = true;
550 		return update();
551 	}
552 
553 	/**
554 	 * Gracefully update the ref to the new value.
555 	 * <p>
556 	 * Merge test will be performed according to {@link #isForceUpdate()}.
557 	 * <p>
558 	 * This is the same as:
559 	 *
560 	 * <pre>
561 	 * return update(new RevWalk(getRepository()));
562 	 * </pre>
563 	 *
564 	 * @return the result status of the update.
565 	 * @throws java.io.IOException
566 	 *             an unexpected IO error occurred while writing changes.
567 	 */
568 	public Result update() throws IOException {
569 		try (RevWalkRevWalk.html#RevWalk">RevWalk rw = new RevWalk(getRepository())) {
570 			rw.setRetainBody(false);
571 			return update(rw);
572 		}
573 	}
574 
575 	/**
576 	 * Gracefully update the ref to the new value.
577 	 * <p>
578 	 * Merge test will be performed according to {@link #isForceUpdate()}.
579 	 *
580 	 * @param walk
581 	 *            a RevWalk instance this update command can borrow to perform
582 	 *            the merge test. The walk will be reset to perform the test.
583 	 * @return the result status of the update.
584 	 * @throws java.io.IOException
585 	 *             an unexpected IO error occurred while writing changes.
586 	 */
587 	public Result update(RevWalk walk) throws IOException {
588 		requireCanDoUpdate();
589 		try {
590 			return result = updateImpl(walk, new Store() {
591 				@Override
592 				Result execute(Result status) throws IOException {
593 					if (status == Result.NO_CHANGE)
594 						return status;
595 					return doUpdate(status);
596 				}
597 			});
598 		} catch (IOException x) {
599 			result = Result.IO_FAILURE;
600 			throw x;
601 		}
602 	}
603 
604 	/**
605 	 * Delete the ref.
606 	 * <p>
607 	 * This is the same as:
608 	 *
609 	 * <pre>
610 	 * return delete(new RevWalk(getRepository()));
611 	 * </pre>
612 	 *
613 	 * @return the result status of the delete.
614 	 * @throws java.io.IOException
615 	 */
616 	public Result delete() throws IOException {
617 		try (RevWalkRevWalk.html#RevWalk">RevWalk rw = new RevWalk(getRepository())) {
618 			rw.setRetainBody(false);
619 			return delete(rw);
620 		}
621 	}
622 
623 	/**
624 	 * Delete the ref.
625 	 *
626 	 * @param walk
627 	 *            a RevWalk instance this delete command can borrow to perform
628 	 *            the merge test. The walk will be reset to perform the test.
629 	 * @return the result status of the delete.
630 	 * @throws java.io.IOException
631 	 */
632 	public Result delete(RevWalk walk) throws IOException {
633 		final String myName = detachingSymbolicRef
634 				? getRef().getName()
635 				: getRef().getLeaf().getName();
636 		if (myName.startsWith(Constants.R_HEADS) && !getRepository().isBare()) {
637 			// Don't allow the currently checked out branch to be deleted.
638 			Ref head = getRefDatabase().exactRef(Constants.HEAD);
639 			while (head != null && head.isSymbolic()) {
640 				head = head.getTarget();
641 				if (myName.equals(head.getName()))
642 					return result = Result.REJECTED_CURRENT_BRANCH;
643 			}
644 		}
645 
646 		try {
647 			return result = updateImpl(walk, new Store() {
648 				@Override
649 				Result execute(Result status) throws IOException {
650 					return doDelete(status);
651 				}
652 			});
653 		} catch (IOException x) {
654 			result = Result.IO_FAILURE;
655 			throw x;
656 		}
657 	}
658 
659 	/**
660 	 * Replace this reference with a symbolic reference to another reference.
661 	 * <p>
662 	 * This exact reference (not its traversed leaf) is replaced with a symbolic
663 	 * reference to the requested name.
664 	 *
665 	 * @param target
666 	 *            name of the new target for this reference. The new target name
667 	 *            must be absolute, so it must begin with {@code refs/}.
668 	 * @return {@link org.eclipse.jgit.lib.RefUpdate.Result#NEW} or
669 	 *         {@link org.eclipse.jgit.lib.RefUpdate.Result#FORCED} on success.
670 	 * @throws java.io.IOException
671 	 */
672 	public Result link(String target) throws IOException {
673 		if (!target.startsWith(Constants.R_REFS))
674 			throw new IllegalArgumentException(MessageFormat.format(JGitText.get().illegalArgumentNotA, Constants.R_REFS));
675 		if (checkConflicting && getRefDatabase().isNameConflicting(getName()))
676 			return Result.LOCK_FAILURE;
677 		try {
678 			if (!tryLock(false))
679 				return Result.LOCK_FAILURE;
680 
681 			final Ref old = getRefDatabase().exactRef(getName());
682 			if (old != null && old.isSymbolic()) {
683 				final Ref dst = old.getTarget();
684 				if (target.equals(dst.getName()))
685 					return result = Result.NO_CHANGE;
686 			}
687 
688 			if (old != null && old.getObjectId() != null)
689 				setOldObjectId(old.getObjectId());
690 
691 			final Ref dst = getRefDatabase().exactRef(target);
692 			if (dst != null && dst.getObjectId() != null)
693 				setNewObjectId(dst.getObjectId());
694 
695 			return result = doLink(target);
696 		} catch (IOException x) {
697 			result = Result.IO_FAILURE;
698 			throw x;
699 		} finally {
700 			unlock();
701 		}
702 	}
703 
704 	private Result updateImpl(RevWalk walk, Store store)
705 			throws IOException {
706 		RevObject newObj;
707 		RevObject oldObj;
708 
709 		// don't make expensive conflict check if this is an existing Ref
710 		if (oldValue == null && checkConflicting
711 				&& getRefDatabase().isNameConflicting(getName())) {
712 			return Result.LOCK_FAILURE;
713 		}
714 		try {
715 			// If we're detaching a symbolic reference, we should update the reference
716 			// itself. Otherwise, we will update the leaf reference, which should be
717 			// an ObjectIdRef.
718 			if (!tryLock(!detachingSymbolicRef)) {
719 				return Result.LOCK_FAILURE;
720 			}
721 			if (expValue != null) {
722 				final ObjectId o;
723 				o = oldValue != null ? oldValue : ObjectId.zeroId();
724 				if (!AnyObjectId.isEqual(expValue, o)) {
725 					return Result.LOCK_FAILURE;
726 				}
727 			}
728 			try {
729 				newObj = safeParseNew(walk, newValue);
730 			} catch (MissingObjectException e) {
731 				return Result.REJECTED_MISSING_OBJECT;
732 			}
733 
734 			if (oldValue == null) {
735 				return store.execute(Result.NEW);
736 			}
737 
738 			oldObj = safeParseOld(walk, oldValue);
739 			if (References.isSameObject(newObj, oldObj)
740 					&& !detachingSymbolicRef) {
741 				return store.execute(Result.NO_CHANGE);
742 			}
743 
744 			if (isForceUpdate()) {
745 				return store.execute(Result.FORCED);
746 			}
747 
748 			if (newObj instanceof RevCommite/jgit/revwalk/RevCommit.html#RevCommit">RevCommit && oldObj instanceof RevCommit) {
749 				if (walk.isMergedInto((RevCommit../../../org/eclipse/jgit/revwalk/RevCommit.html#RevCommit">RevCommit) oldObj, (RevCommit) newObj)) {
750 					return store.execute(Result.FAST_FORWARD);
751 				}
752 			}
753 
754 			return Result.REJECTED;
755 		} finally {
756 			unlock();
757 		}
758 	}
759 
760 	/**
761 	 * Enable/disable the check for conflicting ref names. By default conflicts
762 	 * are checked explicitly.
763 	 *
764 	 * @param check
765 	 *            whether to enable the check for conflicting ref names.
766 	 * @since 3.0
767 	 */
768 	public void setCheckConflicting(boolean check) {
769 		checkConflicting = check;
770 	}
771 
772 	private static RevObject safeParseNew(RevWalk rw, AnyObjectId newId)
773 			throws IOException {
774 		if (newId == null || ObjectId.zeroId().equals(newId)) {
775 			return null;
776 		}
777 		return rw.parseAny(newId);
778 	}
779 
780 	private static RevObject safeParseOld(RevWalk rw, AnyObjectId oldId)
781 			throws IOException {
782 		try {
783 			return oldId != null ? rw.parseAny(oldId) : null;
784 		} catch (MissingObjectException e) {
785 			// We can expect some old objects to be missing, like if we are trying to
786 			// force a deletion of a branch and the object it points to has been
787 			// pruned from the database due to freak corruption accidents (it happens
788 			// with 'git new-work-dir').
789 			return null;
790 		}
791 	}
792 
793 	/**
794 	 * Handle the abstraction of storing a ref update. This is because both
795 	 * updating and deleting of a ref have merge testing in common.
796 	 */
797 	private abstract static class Store {
798 		abstract Result execute(Result status) throws IOException;
799 	}
800 }