2
This code is derived from jgit (http://eclipse.org/jgit).
3
Copyright owners are documented in jgit's IP log.
5
This program and the accompanying materials are made available
6
under the terms of the Eclipse Distribution License v1.0 which
7
accompanies this distribution, is reproduced below, and is
8
available at http://www.eclipse.org/org/documents/edl-v10.php
12
Redistribution and use in source and binary forms, with or
13
without modification, are permitted provided that the following
16
- Redistributions of source code must retain the above copyright
17
notice, this list of conditions and the following disclaimer.
19
- Redistributions in binary form must reproduce the above
20
copyright notice, this list of conditions and the following
21
disclaimer in the documentation and/or other materials provided
22
with the distribution.
24
- Neither the name of the Eclipse Foundation, Inc. nor the
25
names of its contributors may be used to endorse or promote
26
products derived from this software without specific prior
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
53
/// <summary>Creates, updates or deletes any reference.</summary>
54
/// <remarks>Creates, updates or deletes any reference.</remarks>
55
public abstract class RefUpdate
57
/// <summary>Status of an update request.</summary>
58
/// <remarks>Status of an update request.</remarks>
68
REJECTED_CURRENT_BRANCH,
73
/// <summary>New value the caller wants this ref to have.</summary>
74
/// <remarks>New value the caller wants this ref to have.</remarks>
75
private ObjectId newValue;
77
/// <summary>Does this specification ask for forced updated (rewind/reset)?</summary>
80
/// <summary>Identity to record action as within the reflog.</summary>
81
/// <remarks>Identity to record action as within the reflog.</remarks>
82
private PersonIdent refLogIdent;
84
/// <summary>Message the caller wants included in the reflog.</summary>
85
/// <remarks>Message the caller wants included in the reflog.</remarks>
86
private string refLogMessage;
89
/// Should the Result value be appended to
90
/// <see cref="refLogMessage">refLogMessage</see>
93
private bool refLogIncludeResult;
95
/// <summary>Old value of the ref, obtained after we lock it.</summary>
96
/// <remarks>Old value of the ref, obtained after we lock it.</remarks>
97
private ObjectId oldValue;
100
/// If non-null, the value
101
/// <see cref="oldValue">oldValue</see>
102
/// must have to continue.
104
private ObjectId expValue;
106
/// <summary>Result of the update operation.</summary>
107
/// <remarks>Result of the update operation.</remarks>
108
private RefUpdate.Result result = RefUpdate.Result.NOT_ATTEMPTED;
110
private readonly Ref @ref;
113
/// Is this RefUpdate detaching a symbolic ref?
114
/// We need this info since this.ref will normally be peeled of in case of
115
/// detaching a symbolic ref (HEAD for example).
118
/// Is this RefUpdate detaching a symbolic ref?
119
/// We need this info since this.ref will normally be peeled of in case of
120
/// detaching a symbolic ref (HEAD for example).
121
/// Without this flag we cannot decide whether the ref has to be updated or
122
/// not in case when it was a symbolic ref and the newValue == oldValue.
124
private bool detachingSymbolicRef;
126
/// <summary>Construct a new update operation for the reference.</summary>
128
/// Construct a new update operation for the reference.
130
/// <code>ref.getObjectId()</code>
131
/// will be used to seed
132
/// <see cref="GetOldObjectId()">GetOldObjectId()</see>
134
/// which callers can use as part of their own update logic.
136
/// <param name="ref">the reference that will be updated by this operation.</param>
137
protected internal RefUpdate(Ref @ref)
140
oldValue = @ref.GetObjectId();
141
refLogMessage = string.Empty;
144
/// <returns>the reference database this update modifies.</returns>
145
protected internal abstract RefDatabase GetRefDatabase();
147
/// <returns>the repository storing the database's objects.</returns>
148
protected internal abstract Repository GetRepository();
150
/// <summary>Try to acquire the lock on the reference.</summary>
152
/// Try to acquire the lock on the reference.
154
/// If the locking was successful the implementor must set the current
155
/// identity value by calling
156
/// <see cref="SetOldObjectId(ObjectId)">SetOldObjectId(ObjectId)</see>
159
/// <param name="deref">
160
/// true if the lock should be taken against the leaf level
161
/// reference; false if it should be taken exactly against the
162
/// current reference.
165
/// true if the lock was acquired and the reference is likely
166
/// protected from concurrent modification; false if it failed.
168
/// <exception cref="System.IO.IOException">
169
/// the lock couldn't be taken due to an unexpected storage
170
/// failure, and not because of a concurrent update.
172
protected internal abstract bool TryLock(bool deref);
175
/// Releases the lock taken by
176
/// <see cref="TryLock(bool)">TryLock(bool)</see>
179
protected internal abstract void Unlock();
181
/// <param name="desiredResult"></param>
184
/// <code>result</code>
186
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
187
protected internal abstract RefUpdate.Result DoUpdate(RefUpdate.Result desiredResult
190
/// <param name="desiredResult"></param>
193
/// <code>result</code>
195
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
196
protected internal abstract RefUpdate.Result DoDelete(RefUpdate.Result desiredResult
199
/// <param name="target"></param>
202
/// <see cref="Result.NEW">Result.NEW</see>
205
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
206
protected internal abstract RefUpdate.Result DoLink(string target);
208
/// <summary>Get the name of the ref this update will operate on.</summary>
209
/// <remarks>Get the name of the ref this update will operate on.</remarks>
210
/// <returns>name of underlying ref.</returns>
211
public virtual string GetName()
213
return GetRef().GetName();
216
/// <returns>the reference this update will create or modify.</returns>
217
public virtual Ref GetRef()
222
/// <summary>Get the new value the ref will be (or was) updated to.</summary>
223
/// <remarks>Get the new value the ref will be (or was) updated to.</remarks>
224
/// <returns>new value. Null if the caller has not configured it.</returns>
225
public virtual ObjectId GetNewObjectId()
230
/// <summary>Tells this RefUpdate that it is actually detaching a symbolic ref.</summary>
231
/// <remarks>Tells this RefUpdate that it is actually detaching a symbolic ref.</remarks>
232
public virtual void SetDetachingSymbolicRef()
234
detachingSymbolicRef = true;
237
/// <summary>Set the new value the ref will update to.</summary>
238
/// <remarks>Set the new value the ref will update to.</remarks>
239
/// <param name="id">the new value.</param>
240
public virtual void SetNewObjectId(AnyObjectId id)
242
newValue = id.Copy();
246
/// the expected value of the ref after the lock is taken, but before
247
/// update occurs. Null to avoid the compare and swap test. Use
248
/// <see cref="ObjectId.ZeroId()">ObjectId.ZeroId()</see>
249
/// to indicate expectation of a
250
/// non-existant ref.
252
public virtual ObjectId GetExpectedOldObjectId()
257
/// <param name="id">
258
/// the expected value of the ref after the lock is taken, but
259
/// before update occurs. Null to avoid the compare and swap test.
261
/// <see cref="ObjectId.ZeroId()">ObjectId.ZeroId()</see>
262
/// to indicate expectation of a
263
/// non-existant ref.
265
public virtual void SetExpectedOldObjectId(AnyObjectId id)
267
expValue = id != null ? id.ToObjectId() : null;
270
/// <summary>Check if this update wants to forcefully change the ref.</summary>
271
/// <remarks>Check if this update wants to forcefully change the ref.</remarks>
272
/// <returns>true if this update should ignore merge tests.</returns>
273
public virtual bool IsForceUpdate()
278
/// <summary>Set if this update wants to forcefully change the ref.</summary>
279
/// <remarks>Set if this update wants to forcefully change the ref.</remarks>
280
/// <param name="b">true if this update should ignore merge tests.</param>
281
public virtual void SetForceUpdate(bool b)
286
/// <returns>identity of the user making the change in the reflog.</returns>
287
public virtual PersonIdent GetRefLogIdent()
292
/// <summary>Set the identity of the user appearing in the reflog.</summary>
294
/// Set the identity of the user appearing in the reflog.
296
/// The timestamp portion of the identity is ignored. A new identity with the
297
/// current timestamp will be created automatically when the update occurs
298
/// and the log record is written.
300
/// <param name="pi">
301
/// identity of the user. If null the identity will be
302
/// automatically determined based on the repository
305
public virtual void SetRefLogIdent(PersonIdent pi)
310
/// <summary>Get the message to include in the reflog.</summary>
311
/// <remarks>Get the message to include in the reflog.</remarks>
313
/// message the caller wants to include in the reflog; null if the
314
/// update should not be logged.
316
public virtual string GetRefLogMessage()
318
return refLogMessage;
323
/// <code>true</code>
324
/// if the ref log message should show the result.
326
protected internal virtual bool IsRefLogIncludingResult()
328
return refLogIncludeResult;
331
/// <summary>Set the message to include in the reflog.</summary>
332
/// <remarks>Set the message to include in the reflog.</remarks>
333
/// <param name="msg">
334
/// the message to describe this change. It may be null if
335
/// appendStatus is null in order not to append to the reflog
337
/// <param name="appendStatus">
338
/// true if the status of the ref change (fast-forward or
339
/// forced-update) should be appended to the user supplied
342
public virtual void SetRefLogMessage(string msg, bool appendStatus)
344
if (msg == null && !appendStatus)
350
if (msg == null && appendStatus)
352
refLogMessage = string.Empty;
353
refLogIncludeResult = true;
358
refLogIncludeResult = appendStatus;
363
/// <summary>Don't record this update in the ref's associated reflog.</summary>
364
/// <remarks>Don't record this update in the ref's associated reflog.</remarks>
365
public virtual void DisableRefLog()
367
refLogMessage = null;
368
refLogIncludeResult = false;
371
/// <summary>The old value of the ref, prior to the update being attempted.</summary>
373
/// The old value of the ref, prior to the update being attempted.
375
/// This value may differ before and after the update method. Initially it is
376
/// populated with the value of the ref before the lock is taken, but the old
377
/// value may change if someone else modified the ref between the time we
378
/// last read it and when the ref was locked for update.
381
/// the value of the ref prior to the update being attempted; null if
382
/// the updated has not been attempted yet.
384
public virtual ObjectId GetOldObjectId()
389
/// <summary>Set the old value of the ref.</summary>
390
/// <remarks>Set the old value of the ref.</remarks>
391
/// <param name="old">the old value.</param>
392
protected internal virtual void SetOldObjectId(ObjectId old)
397
/// <summary>Get the status of this update.</summary>
399
/// Get the status of this update.
401
/// The same value that was previously returned from an update method.
403
/// <returns>the status of the update.</returns>
404
public virtual RefUpdate.Result GetResult()
409
private void RequireCanDoUpdate()
411
if (newValue == null)
413
throw new InvalidOperationException(JGitText.Get().aNewObjectIdIsRequired);
417
/// <summary>Force the ref to take the new value.</summary>
419
/// Force the ref to take the new value.
421
/// This is just a convenient helper for setting the force flag, and as such
422
/// the merge test is performed.
424
/// <returns>the result status of the update.</returns>
425
/// <exception cref="System.IO.IOException">an unexpected IO error occurred while writing changes.
427
public virtual RefUpdate.Result ForceUpdate()
433
/// <summary>Gracefully update the ref to the new value.</summary>
435
/// Gracefully update the ref to the new value.
437
/// Merge test will be performed according to
438
/// <see cref="IsForceUpdate()">IsForceUpdate()</see>
441
/// This is the same as:
443
/// return update(new RevWalk(getRepository()));
446
/// <returns>the result status of the update.</returns>
447
/// <exception cref="System.IO.IOException">an unexpected IO error occurred while writing changes.
449
public virtual RefUpdate.Result Update()
451
RevWalk rw = new RevWalk(GetRepository());
462
/// <summary>Gracefully update the ref to the new value.</summary>
464
/// Gracefully update the ref to the new value.
466
/// Merge test will be performed according to
467
/// <see cref="IsForceUpdate()">IsForceUpdate()</see>
470
/// <param name="walk">
471
/// a RevWalk instance this update command can borrow to perform
472
/// the merge test. The walk will be reset to perform the test.
474
/// <returns>the result status of the update.</returns>
475
/// <exception cref="System.IO.IOException">an unexpected IO error occurred while writing changes.
477
public virtual RefUpdate.Result Update(RevWalk walk)
479
RequireCanDoUpdate();
482
return result = UpdateImpl(walk, new _Store_484(this));
484
catch (IOException x)
486
result = RefUpdate.Result.IO_FAILURE;
491
private sealed class _Store_484 : RefUpdate.Store
493
public _Store_484(RefUpdate _enclosing) : base(_enclosing)
495
this._enclosing = _enclosing;
498
/// <exception cref="System.IO.IOException"></exception>
499
internal override RefUpdate.Result Execute(RefUpdate.Result status)
501
if (status == RefUpdate.Result.NO_CHANGE)
505
return this._enclosing.DoUpdate(status);
508
private readonly RefUpdate _enclosing;
511
/// <summary>Delete the ref.</summary>
515
/// This is the same as:
517
/// return delete(new RevWalk(getRepository()));
520
/// <returns>the result status of the delete.</returns>
521
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
522
public virtual RefUpdate.Result Delete()
524
RevWalk rw = new RevWalk(GetRepository());
535
/// <summary>Delete the ref.</summary>
536
/// <remarks>Delete the ref.</remarks>
537
/// <param name="walk">
538
/// a RevWalk instance this delete command can borrow to perform
539
/// the merge test. The walk will be reset to perform the test.
541
/// <returns>the result status of the delete.</returns>
542
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
543
public virtual RefUpdate.Result Delete(RevWalk walk)
545
string myName = GetRef().GetLeaf().GetName();
546
if (myName.StartsWith(Constants.R_HEADS))
548
Ref head = GetRefDatabase().GetRef(Constants.HEAD);
549
while (head.IsSymbolic())
551
head = head.GetTarget();
552
if (myName.Equals(head.GetName()))
554
return result = RefUpdate.Result.REJECTED_CURRENT_BRANCH;
560
return result = UpdateImpl(walk, new _Store_540(this));
562
catch (IOException x)
564
result = RefUpdate.Result.IO_FAILURE;
569
private sealed class _Store_540 : RefUpdate.Store
571
public _Store_540(RefUpdate _enclosing) : base(_enclosing)
573
this._enclosing = _enclosing;
576
/// <exception cref="System.IO.IOException"></exception>
577
internal override RefUpdate.Result Execute(RefUpdate.Result status)
579
return this._enclosing.DoDelete(status);
582
private readonly RefUpdate _enclosing;
585
/// <summary>Replace this reference with a symbolic reference to another reference.</summary>
587
/// Replace this reference with a symbolic reference to another reference.
589
/// This exact reference (not its traversed leaf) is replaced with a symbolic
590
/// reference to the requested name.
592
/// <param name="target">
593
/// name of the new target for this reference. The new target name
594
/// must be absolute, so it must begin with
595
/// <code>refs/</code>
600
/// <see cref="Result.NEW">Result.NEW</see>
602
/// <see cref="Result.FORCED">Result.FORCED</see>
605
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
606
public virtual RefUpdate.Result Link(string target)
608
if (!target.StartsWith(Constants.R_REFS))
610
throw new ArgumentException(MessageFormat.Format(JGitText.Get().illegalArgumentNotA
611
, Constants.R_REFS));
613
if (GetRefDatabase().IsNameConflicting(GetName()))
615
return RefUpdate.Result.LOCK_FAILURE;
621
return RefUpdate.Result.LOCK_FAILURE;
623
Ref old = GetRefDatabase().GetRef(GetName());
624
if (old != null && old.IsSymbolic())
626
Ref dst = old.GetTarget();
627
if (target.Equals(dst.GetName()))
629
return result = RefUpdate.Result.NO_CHANGE;
632
if (old != null && old.GetObjectId() != null)
634
SetOldObjectId(old.GetObjectId());
636
Ref dst_1 = GetRefDatabase().GetRef(target);
637
if (dst_1 != null && dst_1.GetObjectId() != null)
639
SetNewObjectId(dst_1.GetObjectId());
641
return result = DoLink(target);
643
catch (IOException x)
645
result = RefUpdate.Result.IO_FAILURE;
654
/// <exception cref="System.IO.IOException"></exception>
655
private RefUpdate.Result UpdateImpl(RevWalk walk, RefUpdate.Store store)
659
if (GetRefDatabase().IsNameConflicting(GetName()))
661
return RefUpdate.Result.LOCK_FAILURE;
667
return RefUpdate.Result.LOCK_FAILURE;
669
if (expValue != null)
672
o = oldValue != null ? oldValue : ObjectId.ZeroId;
673
if (!AnyObjectId.Equals(expValue, o))
675
return RefUpdate.Result.LOCK_FAILURE;
678
if (oldValue == null)
680
return store.Execute(RefUpdate.Result.NEW);
682
newObj = SafeParse(walk, newValue);
683
oldObj = SafeParse(walk, oldValue);
684
if (newObj == oldObj && !detachingSymbolicRef)
686
return store.Execute(RefUpdate.Result.NO_CHANGE);
688
if (newObj is RevCommit && oldObj is RevCommit)
690
if (walk.IsMergedInto((RevCommit)oldObj, (RevCommit)newObj))
692
return store.Execute(RefUpdate.Result.FAST_FORWARD);
697
return store.Execute(RefUpdate.Result.FORCED);
699
return RefUpdate.Result.REJECTED;
707
/// <exception cref="System.IO.IOException"></exception>
708
private static RevObject SafeParse(RevWalk rw, AnyObjectId id)
712
return id != null ? rw.ParseAny(id) : null;
714
catch (MissingObjectException)
716
// We can expect some objects to be missing, like if we are
717
// trying to force a deletion of a branch and the object it
718
// points to has been pruned from the database due to freak
719
// corruption accidents (it happens with 'git new-work-dir').
725
/// <summary>Handle the abstraction of storing a ref update.</summary>
727
/// Handle the abstraction of storing a ref update. This is because both
728
/// updating and deleting of a ref have merge testing in common.
730
private abstract class Store
732
/// <exception cref="System.IO.IOException"></exception>
733
internal abstract RefUpdate.Result Execute(RefUpdate.Result status);
735
internal Store(RefUpdate _enclosing)
737
this._enclosing = _enclosing;
740
private readonly RefUpdate _enclosing;