Added extra javadoc for delete to significant RefUpdate results
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / lib / RefUpdate.java
blobc6536e3f9a7c17fa53c453c17746c558fcb0daf0
1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
21 * written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org.spearce.jgit.lib;
40 import java.io.File;
41 import java.io.IOException;
43 import org.spearce.jgit.errors.MissingObjectException;
44 import org.spearce.jgit.lib.Ref.Storage;
45 import org.spearce.jgit.revwalk.RevCommit;
46 import org.spearce.jgit.revwalk.RevObject;
47 import org.spearce.jgit.revwalk.RevWalk;
49 /**
50 * Updates any locally stored ref.
52 public class RefUpdate {
53 /** Status of an update request. */
54 public static enum Result {
55 /** The ref update/delete has not been attempted by the caller. */
56 NOT_ATTEMPTED,
58 /**
59 * The ref could not be locked for update/delete.
60 * <p>
61 * This is generally a transient failure and is usually caused by
62 * another process trying to access the ref at the same time as this
63 * process was trying to update it. It is possible a future operation
64 * will be successful.
66 LOCK_FAILURE,
68 /**
69 * Same value already stored.
70 * <p>
71 * Both the old value and the new value are identical. No change was
72 * necessary for an update. For delete the branch is removed.
74 NO_CHANGE,
76 /**
77 * The ref was created locally for an update, but ignored for delete.
78 * <p>
79 * The ref did not exist when the update started, but it was created
80 * successfully with the new value.
82 NEW,
84 /**
85 * The ref had to be forcefully updated/deleted.
86 * <p>
87 * The ref already existed but its old value was not fully merged into
88 * the new value. The configuration permitted a forced update to take
89 * place, so ref now contains the new value. History associated with the
90 * objects not merged may no longer be reachable.
92 FORCED,
94 /**
95 * The ref was updated/deleted in a fast-forward way.
96 * <p>
97 * The tracking ref already existed and its old value was fully merged
98 * into the new value. No history was made unreachable.
100 FAST_FORWARD,
103 * Not a fast-forward and not stored.
104 * <p>
105 * The tracking ref already existed but its old value was not fully
106 * merged into the new value. The configuration did not allow a forced
107 * update/delete to take place, so ref still contains the old value. No
108 * previous history was lost.
110 REJECTED,
113 * Rejected because trying to delete the current branch.
114 * <p>
115 * Has no meaning for update.
117 REJECTED_CURRENT_BRANCH,
120 * The ref was probably not updated/deleted because of I/O error.
121 * <p>
122 * Unexpected I/O error occurred when writing new ref. Such error may
123 * result in uncertain state, but most probably ref was not updated.
124 * <p>
125 * This kind of error doesn't include {@link #LOCK_FAILURE}, which is a
126 * different case.
128 IO_FAILURE
131 /** Repository the ref is stored in. */
132 private final RefDatabase db;
134 /** Name of the ref. */
135 private final String name;
137 /** Location of the loose file holding the value of this ref. */
138 private final File looseFile;
140 /** New value the caller wants this ref to have. */
141 private ObjectId newValue;
143 /** Does this specification ask for forced updated (rewind/reset)? */
144 private boolean force;
146 /** Message the caller wants included in the reflog. */
147 private String refLogMessage;
149 /** Should the Result value be appended to {@link #refLogMessage}. */
150 private boolean refLogIncludeResult;
152 /** Old value of the ref, obtained after we lock it. */
153 private ObjectId oldValue;
155 /** Result of the update operation. */
156 private Result result = Result.NOT_ATTEMPTED;
158 private final Ref ref;
160 RefUpdate(final RefDatabase r, final Ref ref, final File f) {
161 db = r;
162 this.ref = ref;
163 name = ref.getName();
164 oldValue = ref.getObjectId();
165 looseFile = f;
169 * Get the name of the ref this update will operate on.
171 * @return name of this ref.
173 public String getName() {
174 return name;
178 * Get the new value the ref will be (or was) updated to.
180 * @return new value. Null if the caller has not configured it.
182 public ObjectId getNewObjectId() {
183 return newValue;
187 * Set the new value the ref will update to.
189 * @param id
190 * the new value.
192 public void setNewObjectId(final AnyObjectId id) {
193 newValue = id.toObjectId();
197 * Check if this update wants to forcefully change the ref.
199 * @return true if this update should ignore merge tests.
201 public boolean isForceUpdate() {
202 return force;
206 * Set if this update wants to forcefully change the ref.
208 * @param b
209 * true if this update should ignore merge tests.
211 public void setForceUpdate(final boolean b) {
212 force = b;
216 * Get the message to include in the reflog.
218 * @return message the caller wants to include in the reflog.
220 public String getRefLogMessage() {
221 return refLogMessage;
225 * Set the message to include in the reflog.
227 * @param msg
228 * the message to describe this change.
229 * @param appendStatus
230 * true if the status of the ref change (fast-forward or
231 * forced-update) should be appended to the user supplied
232 * message.
234 public void setRefLogMessage(final String msg, final boolean appendStatus) {
235 refLogMessage = msg;
236 refLogIncludeResult = appendStatus;
240 * The old value of the ref, prior to the update being attempted.
241 * <p>
242 * This value may differ before and after the update method. Initially it is
243 * populated with the value of the ref before the lock is taken, but the old
244 * value may change if someone else modified the ref between the time we
245 * last read it and when the ref was locked for update.
247 * @return the value of the ref prior to the update being attempted; null if
248 * the updated has not been attempted yet.
250 public ObjectId getOldObjectId() {
251 return oldValue;
255 * Get the status of this update.
256 * <p>
257 * The same value that was previously returned from an update method.
259 * @return the status of the update.
261 public Result getResult() {
262 return result;
265 private void requireCanDoUpdate() {
266 if (newValue == null)
267 throw new IllegalStateException("A NewObjectId is required.");
271 * Force the ref to take the new value.
272 * <p>
273 * This is just a convenient helper for setting the force flag, and as such
274 * the merge test is performed.
276 * @return the result status of the update.
277 * @throws IOException
278 * an unexpected IO error occurred while writing changes.
280 public Result forceUpdate() throws IOException {
281 force = true;
282 return update();
286 * Gracefully update the ref to the new value.
287 * <p>
288 * Merge test will be performed according to {@link #isForceUpdate()}.
289 * <p>
290 * This is the same as:
292 * <pre>
293 * return update(new RevWalk(repository));
294 * </pre>
296 * @return the result status of the update.
297 * @throws IOException
298 * an unexpected IO error occurred while writing changes.
300 public Result update() throws IOException {
301 return update(new RevWalk(db.getRepository()));
305 * Gracefully update the ref to the new value.
306 * <p>
307 * Merge test will be performed according to {@link #isForceUpdate()}.
309 * @param walk
310 * a RevWalk instance this update command can borrow to perform
311 * the merge test. The walk will be reset to perform the test.
312 * @return the result status of the update.
313 * @throws IOException
314 * an unexpected IO error occurred while writing changes.
316 public Result update(final RevWalk walk) throws IOException {
317 requireCanDoUpdate();
318 try {
319 return result = updateImpl(walk, new UpdateStore());
320 } catch (IOException x) {
321 result = Result.IO_FAILURE;
322 throw x;
327 * Delete the ref.
329 * @return the result status of the delete.
330 * @throws IOException
332 public Result delete() throws IOException {
333 if (name.startsWith(Constants.R_HEADS)) {
334 final Ref head = db.readRef(Constants.HEAD);
335 if (head != null && name.equals(head.getName()))
336 return Result.REJECTED_CURRENT_BRANCH;
339 try {
340 return updateImpl(new RevWalk(db.getRepository()),
341 new DeleteStore());
342 } catch (IOException x) {
343 result = Result.IO_FAILURE;
344 throw x;
348 private Result updateImpl(final RevWalk walk, final Store store)
349 throws IOException {
350 final LockFile lock;
351 RevObject newObj;
352 RevObject oldObj;
354 lock = new LockFile(looseFile);
355 if (!lock.lock())
356 return Result.LOCK_FAILURE;
357 try {
358 oldValue = db.idOf(name);
359 if (oldValue == null)
360 return store.store(lock, Result.NEW);
362 newObj = safeParse(walk, newValue);
363 oldObj = safeParse(walk, oldValue);
364 if (newObj == oldObj)
365 return store.store(lock, Result.NO_CHANGE);
367 if (newObj instanceof RevCommit && oldObj instanceof RevCommit) {
368 if (walk.isMergedInto((RevCommit) oldObj, (RevCommit) newObj))
369 return store.store(lock, Result.FAST_FORWARD);
372 if (isForceUpdate())
373 return store.store(lock, Result.FORCED);
374 return Result.REJECTED;
375 } finally {
376 lock.unlock();
380 private static RevObject safeParse(final RevWalk rw, final AnyObjectId id)
381 throws IOException {
382 try {
383 return rw.parseAny(id);
384 } catch (MissingObjectException e) {
385 // We can expect some objects to be missing, like if we are
386 // trying to force a deletion of a branch and the object it
387 // points to has been pruned from the database due to freak
388 // corruption accidents (it happens with 'git new-work-dir').
390 return null;
394 private Result updateStore(final LockFile lock, final Result status)
395 throws IOException {
396 if (status == Result.NO_CHANGE)
397 return status;
398 lock.setNeedStatInformation(true);
399 lock.write(newValue);
400 String msg = getRefLogMessage();
401 if (msg != null && refLogIncludeResult) {
402 if (status == Result.FORCED)
403 msg += ": forced-update";
404 else if (status == Result.FAST_FORWARD)
405 msg += ": fast forward";
406 else if (status == Result.NEW)
407 msg += ": created";
409 RefLogWriter.writeReflog(db.getRepository(), oldValue, newValue, msg,
410 getName());
411 if (!lock.commit())
412 return Result.LOCK_FAILURE;
413 db.stored(name, newValue, lock.getCommitLastModified());
414 return status;
418 * Handle the abstraction of storing a ref update. This is because both
419 * updating and deleting of a ref have merge testing in common.
421 private abstract class Store {
422 abstract Result store(final LockFile lock, final Result status)
423 throws IOException;
426 private class UpdateStore extends Store {
428 @Override
429 Result store(final LockFile lock, final Result status)
430 throws IOException {
431 return updateStore(lock, status);
435 private class DeleteStore extends Store {
437 @Override
438 Result store(LockFile lock, Result status) throws IOException {
439 Storage storage = ref.getStorage();
440 if (storage == Storage.NEW)
441 return status;
442 if (storage.isPacked())
443 db.removePackedRef(ref.getName());
444 if (storage.isLoose())
445 if (!looseFile.delete())
446 throw new IOException("File cannot be deleted: "
447 + looseFile);
448 new File(db.getRepository().getDirectory(), Constants.LOGS + "/"
449 + ref.getName()).delete();
450 return status;