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 }