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