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