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