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 }