2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
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
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
;
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
;
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. */
57 * The ref could not be locked for update.
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
67 * Same value already stored.
69 * Both the old value and the new value are identical. No change was
75 * The ref was created locally.
77 * The ref did not exist when the update started, but it was created
78 * successfully with the new value.
83 * The ref had to be forcefully updated.
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.
93 * The ref was updated in a fast-forward way.
95 * The tracking ref already existed and its old value was fully merged
96 * into the new value. No history was made unreachable.
101 * Not a fast-forward and not stored.
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.
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
) {
140 name
= ref
.getName();
141 oldValue
= ref
.getObjectId();
146 * Get the name of the ref this update will operate on.
148 * @return name of this ref.
150 public String
getName() {
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() {
164 * Set the new value the ref will update to.
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() {
183 * Set if this update wants to forcefully change the ref.
186 * true if this update should ignore merge tests.
188 public void setForceUpdate(final boolean 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.
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
211 public void setRefLogMessage(final String msg
, final boolean appendStatus
) {
213 refLogIncludeResult
= appendStatus
;
217 * The old value of the ref, prior to the update being attempted.
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() {
232 * Get the status of this update.
234 * The same value that was previously returned from an update method.
236 * @return the status of the update.
238 public Result
getResult() {
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.
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
{
265 lock
= new LockFile(looseFile
);
267 return Result
.LOCK_FAILURE
;
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
);
281 * Gracefully update the ref to the new value.
283 * Merge test will be performed according to {@link #isForceUpdate()}.
285 * This is the same as:
288 * return update(new RevWalk(repository));
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.
302 * Merge test will be performed according to {@link #isForceUpdate()}.
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
{
321 lock
= new LockFile(looseFile
);
323 return Result
.LOCK_FAILURE
;
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
);
338 return store(lock
, Result
.FORCED
);
339 return Result
.REJECTED
;
343 return store(lock
, Result
.FORCED
);
344 return Result
.REJECTED
;
350 private Result
store(final LockFile lock
, final Result status
)
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
)
363 RefLogWriter
.writeReflog(db
.getRepository(), oldValue
, newValue
, msg
,
366 return Result
.LOCK_FAILURE
;
367 db
.stored(name
, newValue
, lock
.getCommitLastModified());