2 * Copyright (C) 2008 Shawn Pearce <spearce@spearce.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License, version 2, as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org
.spearce
.jgit
.lib
;
20 import java
.io
.IOException
;
22 import org
.spearce
.jgit
.revwalk
.RevCommit
;
23 import org
.spearce
.jgit
.revwalk
.RevObject
;
24 import org
.spearce
.jgit
.revwalk
.RevWalk
;
27 * Updates any locally stored ref.
29 public class RefUpdate
{
30 /** Status of an update request. */
31 public static enum Result
{
32 /** The ref update has not been attempted by the caller. */
36 * The ref could not be locked for update.
38 * This is generally a transient failure and is usually caused by
39 * another process trying to access the ref at the same time as this
40 * process was trying to update it. It is possible a future operation
46 * Same value already stored.
48 * Both the old value and the new value are identical. No change was
54 * The ref was created locally.
56 * The ref did not exist when the update started, but it was created
57 * successfully with the new value.
62 * The ref had to be forcefully updated.
64 * The ref already existed but its old value was not fully merged into
65 * the new value. The configuration permitted a forced update to take
66 * place, so ref now contains the new value. History associated with the
67 * objects not merged may no longer be reachable.
72 * The ref was updated in a fast-forward way.
74 * The tracking ref already existed and its old value was fully merged
75 * into the new value. No history was made unreachable.
80 * Not a fast-forward and not stored.
82 * The tracking ref already existed but its old value was not fully
83 * merged into the new value. The configuration did not allow a forced
84 * update to take place, so ref still contains the old value. No
85 * previous history was lost.
90 /** Repository the ref is stored in. */
91 private final Repository db
;
93 /** Name of the ref. */
94 private final String name
;
96 /** Location of the loose file holding the value of this ref. */
97 private final File looseFile
;
99 /** New value the caller wants this ref to have. */
100 private ObjectId newValue
;
102 /** Does this specification ask for forced updated (rewind/reset)? */
103 private boolean force
;
105 /** Message the caller wants included in the reflog. */
106 private String refLogMessage
;
108 /** Should the Result value be appended to {@link #refLogMessage}. */
109 private boolean refLogIncludeResult
;
111 /** Old value of the ref, obtained after we lock it. */
112 private ObjectId oldValue
;
114 /** Result of the update operation. */
115 private Result result
= Result
.NOT_ATTEMPTED
;
117 RefUpdate(final Repository r
, final Ref ref
, final File f
) {
119 name
= ref
.getName();
120 oldValue
= ref
.getObjectId();
125 * Get the name of the ref this update will operate on.
127 * @return name of this ref.
129 public String
getName() {
134 * Get the new value the ref will be (or was) updated to.
136 * @return new value. Null if the caller has not configured it.
138 public ObjectId
getNewObjectId() {
143 * Set the new value the ref will update to.
148 public void setNewObjectId(final AnyObjectId id
) {
149 newValue
= id
.toObjectId();
153 * Check if this update wants to forcefully change the ref.
155 * @return true if this update should ignore merge tests.
157 public boolean isForceUpdate() {
162 * Set if this update wants to forcefully change the ref.
165 * true if this update should ignore merge tests.
167 public void setForceUpdate(final boolean b
) {
172 * Get the message to include in the reflog.
174 * @return message the caller wants to include in the reflog.
176 public String
getRefLogMessage() {
177 return refLogMessage
;
181 * Set the message to include in the reflog.
184 * the message to describe this change.
185 * @param appendStatus
186 * true if the status of the ref change (fast-forward or
187 * forced-update) should be appended to the user supplied
190 public void setRefLogMessage(final String msg
, final boolean appendStatus
) {
192 refLogIncludeResult
= appendStatus
;
196 * The old value of the ref, prior to the update being attempted.
198 * This value may differ before and after the update method. Initially it is
199 * populated with the value of the ref before the lock is taken, but the old
200 * value may change if someone else modified the ref between the time we
201 * last read it and when the ref was locked for update.
203 * @return the value of the ref prior to the update being attempted; null if
204 * the updated has not been attempted yet.
206 public ObjectId
getOldObjectId() {
211 * Get the status of this update.
213 * The same value that was previously returned from an update method.
215 * @return the status of the update.
217 public Result
getResult() {
221 private void requireCanDoUpdate() {
222 if (newValue
== null)
223 throw new IllegalStateException("A NewObjectId is required.");
227 * Force the ref to take the new value.
229 * No merge tests are performed, so the value of {@link #isForceUpdate()}
230 * will not be honored.
232 * @return the result status of the update.
233 * @throws IOException
234 * an unexpected IO error occurred while writing changes.
236 public Result
forceUpdate() throws IOException
{
237 requireCanDoUpdate();
238 return result
= forceUpdateImpl();
241 private Result
forceUpdateImpl() throws IOException
{
244 lock
= new LockFile(looseFile
);
246 return Result
.LOCK_FAILURE
;
248 oldValue
= lock
.readCurrentObjectId();
249 if (oldValue
== null)
250 return store(lock
, Result
.NEW
);
251 if (oldValue
.equals(newValue
))
252 return Result
.NO_CHANGE
;
253 return store(lock
, Result
.FORCED
);
260 * Gracefully update the ref to the new value.
262 * Merge test will be performed according to {@link #isForceUpdate()}.
264 * This is the same as:
267 * return update(new RevWalk(repository));
270 * @return the result status of the update.
271 * @throws IOException
272 * an unexpected IO error occurred while writing changes.
274 public Result
update() throws IOException
{
275 return update(new RevWalk(db
));
279 * Gracefully update the ref to the new value.
281 * Merge test will be performed according to {@link #isForceUpdate()}.
284 * a RevWalk instance this update command can borrow to perform
285 * the merge test. The walk will be reset to perform the test.
286 * @return the result status of the update.
287 * @throws IOException
288 * an unexpected IO error occurred while writing changes.
290 public Result
update(final RevWalk walk
) throws IOException
{
291 requireCanDoUpdate();
292 return result
= updateImpl(walk
);
295 private Result
updateImpl(final RevWalk walk
) throws IOException
{
300 lock
= new LockFile(looseFile
);
302 return Result
.LOCK_FAILURE
;
304 oldValue
= lock
.readCurrentObjectId();
305 if (oldValue
== null)
306 return store(lock
, Result
.NEW
);
308 newObj
= walk
.parseAny(newValue
);
309 oldObj
= walk
.parseAny(oldValue
);
310 if (newObj
== oldObj
)
311 return Result
.NO_CHANGE
;
313 if (newObj
instanceof RevCommit
&& oldObj
instanceof RevCommit
) {
314 if (walk
.isMergedInto((RevCommit
) oldObj
, (RevCommit
) newObj
))
315 return store(lock
, Result
.FAST_FORWARD
);
317 return store(lock
, Result
.FORCED
);
318 return Result
.REJECTED
;
322 return store(lock
, Result
.FORCED
);
323 return Result
.REJECTED
;
329 private Result
store(final LockFile lock
, final Result status
)
331 lock
.write(newValue
);
332 String msg
= getRefLogMessage();
333 if (msg
!= null && refLogIncludeResult
) {
334 if (status
== Result
.FORCED
)
335 msg
+= ": forced-update";
336 else if (status
== Result
.FAST_FORWARD
)
337 msg
+= ": fast forward";
338 else if (status
== Result
.NEW
)
341 RefLogWriter
.writeReflog(db
, oldValue
, newValue
, msg
, getName());
343 return Result
.LOCK_FAILURE
;