Switch jgit library to the EDL (3-clause BSD)
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / lib / RefUpdate.java
blob48044fbeb8a1a72e78a261064f279745bf3f2fb7
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.revwalk.RevCommit;
44 import org.spearce.jgit.revwalk.RevObject;
45 import org.spearce.jgit.revwalk.RevWalk;
47 /**
48 * Updates any locally stored ref.
50 public class RefUpdate {
51 /** Status of an update request. */
52 public static enum Result {
53 /** The ref update has not been attempted by the caller. */
54 NOT_ATTEMPTED,
56 /**
57 * The ref could not be locked for update.
58 * <p>
59 * This is generally a transient failure and is usually caused by
60 * another process trying to access the ref at the same time as this
61 * process was trying to update it. It is possible a future operation
62 * will be successful.
64 LOCK_FAILURE,
66 /**
67 * Same value already stored.
68 * <p>
69 * Both the old value and the new value are identical. No change was
70 * necessary.
72 NO_CHANGE,
74 /**
75 * The ref was created locally.
76 * <p>
77 * The ref did not exist when the update started, but it was created
78 * successfully with the new value.
80 NEW,
82 /**
83 * The ref had to be forcefully updated.
84 * <p>
85 * The ref already existed but its old value was not fully merged into
86 * the new value. The configuration permitted a forced update to take
87 * place, so ref now contains the new value. History associated with the
88 * objects not merged may no longer be reachable.
90 FORCED,
92 /**
93 * The ref was updated in a fast-forward way.
94 * <p>
95 * The tracking ref already existed and its old value was fully merged
96 * into the new value. No history was made unreachable.
98 FAST_FORWARD,
101 * Not a fast-forward and not stored.
102 * <p>
103 * The tracking ref already existed but its old value was not fully
104 * merged into the new value. The configuration did not allow a forced
105 * update to take place, so ref still contains the old value. No
106 * previous history was lost.
108 REJECTED
111 /** Repository the ref is stored in. */
112 private final RefDatabase db;
114 /** Name of the ref. */
115 private final String name;
117 /** Location of the loose file holding the value of this ref. */
118 private final File looseFile;
120 /** New value the caller wants this ref to have. */
121 private ObjectId newValue;
123 /** Does this specification ask for forced updated (rewind/reset)? */
124 private boolean force;
126 /** Message the caller wants included in the reflog. */
127 private String refLogMessage;
129 /** Should the Result value be appended to {@link #refLogMessage}. */
130 private boolean refLogIncludeResult;
132 /** Old value of the ref, obtained after we lock it. */
133 private ObjectId oldValue;
135 /** Result of the update operation. */
136 private Result result = Result.NOT_ATTEMPTED;
138 RefUpdate(final RefDatabase r, final Ref ref, final File f) {
139 db = r;
140 name = ref.getName();
141 oldValue = ref.getObjectId();
142 looseFile = f;
146 * Get the name of the ref this update will operate on.
148 * @return name of this ref.
150 public String getName() {
151 return name;
155 * Get the new value the ref will be (or was) updated to.
157 * @return new value. Null if the caller has not configured it.
159 public ObjectId getNewObjectId() {
160 return newValue;
164 * Set the new value the ref will update to.
166 * @param id
167 * the new value.
169 public void setNewObjectId(final AnyObjectId id) {
170 newValue = id.toObjectId();
174 * Check if this update wants to forcefully change the ref.
176 * @return true if this update should ignore merge tests.
178 public boolean isForceUpdate() {
179 return force;
183 * Set if this update wants to forcefully change the ref.
185 * @param b
186 * true if this update should ignore merge tests.
188 public void setForceUpdate(final boolean b) {
189 force = b;
193 * Get the message to include in the reflog.
195 * @return message the caller wants to include in the reflog.
197 public String getRefLogMessage() {
198 return refLogMessage;
202 * Set the message to include in the reflog.
204 * @param msg
205 * the message to describe this change.
206 * @param appendStatus
207 * true if the status of the ref change (fast-forward or
208 * forced-update) should be appended to the user supplied
209 * message.
211 public void setRefLogMessage(final String msg, final boolean appendStatus) {
212 refLogMessage = msg;
213 refLogIncludeResult = appendStatus;
217 * The old value of the ref, prior to the update being attempted.
218 * <p>
219 * This value may differ before and after the update method. Initially it is
220 * populated with the value of the ref before the lock is taken, but the old
221 * value may change if someone else modified the ref between the time we
222 * last read it and when the ref was locked for update.
224 * @return the value of the ref prior to the update being attempted; null if
225 * the updated has not been attempted yet.
227 public ObjectId getOldObjectId() {
228 return oldValue;
232 * Get the status of this update.
233 * <p>
234 * The same value that was previously returned from an update method.
236 * @return the status of the update.
238 public Result getResult() {
239 return result;
242 private void requireCanDoUpdate() {
243 if (newValue == null)
244 throw new IllegalStateException("A NewObjectId is required.");
248 * Force the ref to take the new value.
249 * <p>
250 * No merge tests are performed, so the value of {@link #isForceUpdate()}
251 * will not be honored.
253 * @return the result status of the update.
254 * @throws IOException
255 * an unexpected IO error occurred while writing changes.
257 public Result forceUpdate() throws IOException {
258 requireCanDoUpdate();
259 return result = forceUpdateImpl();
262 private Result forceUpdateImpl() throws IOException {
263 final LockFile lock;
265 lock = new LockFile(looseFile);
266 if (!lock.lock())
267 return Result.LOCK_FAILURE;
268 try {
269 oldValue = db.idOf(name);
270 if (oldValue == null)
271 return store(lock, Result.NEW);
272 if (oldValue.equals(newValue))
273 return Result.NO_CHANGE;
274 return store(lock, Result.FORCED);
275 } finally {
276 lock.unlock();
281 * Gracefully update the ref to the new value.
282 * <p>
283 * Merge test will be performed according to {@link #isForceUpdate()}.
284 * <p>
285 * This is the same as:
287 * <pre>
288 * return update(new RevWalk(repository));
289 * </pre>
291 * @return the result status of the update.
292 * @throws IOException
293 * an unexpected IO error occurred while writing changes.
295 public Result update() throws IOException {
296 return update(new RevWalk(db.getRepository()));
300 * Gracefully update the ref to the new value.
301 * <p>
302 * Merge test will be performed according to {@link #isForceUpdate()}.
304 * @param walk
305 * a RevWalk instance this update command can borrow to perform
306 * the merge test. The walk will be reset to perform the test.
307 * @return the result status of the update.
308 * @throws IOException
309 * an unexpected IO error occurred while writing changes.
311 public Result update(final RevWalk walk) throws IOException {
312 requireCanDoUpdate();
313 return result = updateImpl(walk);
316 private Result updateImpl(final RevWalk walk) throws IOException {
317 final LockFile lock;
318 RevObject newObj;
319 RevObject oldObj;
321 lock = new LockFile(looseFile);
322 if (!lock.lock())
323 return Result.LOCK_FAILURE;
324 try {
325 oldValue = db.idOf(name);
326 if (oldValue == null)
327 return store(lock, Result.NEW);
329 newObj = walk.parseAny(newValue);
330 oldObj = walk.parseAny(oldValue);
331 if (newObj == oldObj)
332 return Result.NO_CHANGE;
334 if (newObj instanceof RevCommit && oldObj instanceof RevCommit) {
335 if (walk.isMergedInto((RevCommit) oldObj, (RevCommit) newObj))
336 return store(lock, Result.FAST_FORWARD);
337 if (isForceUpdate())
338 return store(lock, Result.FORCED);
339 return Result.REJECTED;
342 if (isForceUpdate())
343 return store(lock, Result.FORCED);
344 return Result.REJECTED;
345 } finally {
346 lock.unlock();
350 private Result store(final LockFile lock, final Result status)
351 throws IOException {
352 lock.setNeedStatInformation(true);
353 lock.write(newValue);
354 String msg = getRefLogMessage();
355 if (msg != null && refLogIncludeResult) {
356 if (status == Result.FORCED)
357 msg += ": forced-update";
358 else if (status == Result.FAST_FORWARD)
359 msg += ": fast forward";
360 else if (status == Result.NEW)
361 msg += ": created";
363 RefLogWriter.writeReflog(db.getRepository(), oldValue, newValue, msg,
364 getName());
365 if (!lock.commit())
366 return Result.LOCK_FAILURE;
367 db.stored(name, newValue, lock.getCommitLastModified());
368 return status;