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.
45
using System.Collections.Generic;
52
using NGit.Storage.File;
59
/// <summary>Represents a Git repository.</summary>
61
/// Represents a Git repository.
63
/// A repository holds all objects and refs used for managing source code (could
64
/// be any type of file, but source code is what SCM's are typically used for).
66
/// This class is thread-safe.
68
public abstract class Repository
70
private static readonly ListenerList globalListeners = new ListenerList();
72
/// <returns>the global listener list observing all events in this JVM.</returns>
73
public static ListenerList GetGlobalListenerList()
75
return globalListeners;
78
private readonly AtomicInteger useCnt = new AtomicInteger(1);
80
/// <summary>Metadata directory holding the repository's critical files.</summary>
81
/// <remarks>Metadata directory holding the repository's critical files.</remarks>
82
private readonly FilePath gitDir;
84
/// <summary>File abstraction used to resolve paths.</summary>
85
/// <remarks>File abstraction used to resolve paths.</remarks>
86
private readonly FS fs;
88
private GitIndex index;
90
private readonly ListenerList myListeners = new ListenerList();
92
/// <summary>If not bare, the top level directory of the working files.</summary>
93
/// <remarks>If not bare, the top level directory of the working files.</remarks>
94
private readonly FilePath workTree;
96
/// <summary>If not bare, the index file caching the working file states.</summary>
97
/// <remarks>If not bare, the index file caching the working file states.</remarks>
98
private readonly FilePath indexFile;
100
/// <summary>Initialize a new repository instance.</summary>
101
/// <remarks>Initialize a new repository instance.</remarks>
102
/// <param name="options">options to configure the repository.</param>
103
protected internal Repository(BaseRepositoryBuilder options)
105
gitDir = options.GetGitDir();
106
fs = options.GetFS();
107
workTree = options.GetWorkTree();
108
indexFile = options.GetIndexFile();
111
/// <returns>listeners observing only events on this repository.</returns>
112
public virtual ListenerList Listeners
120
/// <summary>Fire an event to all registered listeners.</summary>
122
/// Fire an event to all registered listeners.
124
/// The source repository of the event is automatically set to this
125
/// repository, before the event is delivered to any listeners.
127
/// <param name="event">the event to deliver.</param>
128
public virtual void FireEvent<_T0>(RepositoryEvent<_T0> @event) where _T0:RepositoryListener
130
@event.SetRepository(this);
131
myListeners.Dispatch(@event);
132
globalListeners.Dispatch(@event);
135
/// <summary>Create a new Git repository.</summary>
137
/// Create a new Git repository.
139
/// Repository with working tree is created using this method. This method is
141
/// <code>create(false)</code>
144
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
145
/// <seealso cref="Create(bool)">Create(bool)</seealso>
146
public virtual void Create()
152
/// Create a new Git repository initializing the necessary files and
156
/// Create a new Git repository initializing the necessary files and
159
/// <param name="bare">
160
/// if true, a bare repository (a repository without a working
161
/// directory) is created.
163
/// <exception cref="System.IO.IOException">in case of IO problem</exception>
164
public abstract void Create(bool bare);
166
/// <returns>local metadata directory; null if repository isn't local.</returns>
167
public virtual FilePath Directory
175
/// <returns>the object database which stores this repository's data.</returns>
176
public abstract NGit.ObjectDatabase ObjectDatabase
182
/// a new inserter to create objects in
183
/// <see cref="ObjectDatabase()">ObjectDatabase()</see>
186
public virtual ObjectInserter NewObjectInserter()
188
return ObjectDatabase.NewInserter();
192
/// a new reader to read objects from
193
/// <see cref="ObjectDatabase()">ObjectDatabase()</see>
196
public virtual ObjectReader NewObjectReader()
198
return ObjectDatabase.NewReader();
201
/// <returns>the reference database which stores the reference namespace.</returns>
202
public abstract NGit.RefDatabase RefDatabase
207
/// <returns>the configuration of this repository</returns>
208
public abstract StoredConfig GetConfig();
210
/// <returns>the used file system abstraction</returns>
211
public virtual FS FileSystem
219
/// <param name="objectId"></param>
221
/// true if the specified object is stored in this repo or any of the
222
/// known shared repositories.
224
public virtual bool HasObject(AnyObjectId objectId)
228
return ObjectDatabase.Has(objectId);
232
// Legacy API, assume error means "no"
237
/// <summary>Open an object from this repository.</summary>
239
/// Open an object from this repository.
241
/// This is a one-shot call interface which may be faster than allocating a
242
/// <see cref="NewObjectReader()">NewObjectReader()</see>
243
/// to perform the lookup.
245
/// <param name="objectId">identity of the object to open.</param>
248
/// <see cref="ObjectLoader">ObjectLoader</see>
249
/// for accessing the object.
251
/// <exception cref="NGit.Errors.MissingObjectException">the object does not exist.</exception>
252
/// <exception cref="System.IO.IOException">the object store cannot be accessed.</exception>
253
public virtual ObjectLoader Open(AnyObjectId objectId)
255
return ObjectDatabase.Open(objectId);
258
/// <summary>Open an object from this repository.</summary>
260
/// Open an object from this repository.
262
/// This is a one-shot call interface which may be faster than allocating a
263
/// <see cref="NewObjectReader()">NewObjectReader()</see>
264
/// to perform the lookup.
266
/// <param name="objectId">identity of the object to open.</param>
267
/// <param name="typeHint">
268
/// hint about the type of object being requested;
269
/// <see cref="ObjectReader.OBJ_ANY">ObjectReader.OBJ_ANY</see>
270
/// if the object type is not known,
271
/// or does not matter to the caller.
275
/// <see cref="ObjectLoader">ObjectLoader</see>
276
/// for accessing the object.
278
/// <exception cref="NGit.Errors.MissingObjectException">the object does not exist.</exception>
279
/// <exception cref="NGit.Errors.IncorrectObjectTypeException">
280
/// typeHint was not OBJ_ANY, and the object's actual type does
281
/// not match typeHint.
283
/// <exception cref="System.IO.IOException">the object store cannot be accessed.</exception>
284
public virtual ObjectLoader Open(AnyObjectId objectId, int typeHint)
286
return ObjectDatabase.Open(objectId, typeHint);
289
/// <summary>Create a command to update, create or delete a ref in this repository.</summary>
290
/// <remarks>Create a command to update, create or delete a ref in this repository.</remarks>
291
/// <param name="ref">name of the ref the caller wants to modify.</param>
293
/// an update command. The caller must finish populating this command
294
/// and then invoke one of the update methods to actually make a
297
/// <exception cref="System.IO.IOException">
298
/// a symbolic ref was passed in and could not be resolved back
299
/// to the base ref, as the symbolic ref could not be read.
301
public virtual RefUpdate UpdateRef(string @ref)
303
return UpdateRef(@ref, false);
306
/// <summary>Create a command to update, create or delete a ref in this repository.</summary>
307
/// <remarks>Create a command to update, create or delete a ref in this repository.</remarks>
308
/// <param name="ref">name of the ref the caller wants to modify.</param>
309
/// <param name="detach">true to create a detached head</param>
311
/// an update command. The caller must finish populating this command
312
/// and then invoke one of the update methods to actually make a
315
/// <exception cref="System.IO.IOException">
316
/// a symbolic ref was passed in and could not be resolved back
317
/// to the base ref, as the symbolic ref could not be read.
319
public virtual RefUpdate UpdateRef(string @ref, bool detach)
321
return RefDatabase.NewUpdate(@ref, detach);
324
/// <summary>Create a command to rename a ref in this repository</summary>
325
/// <param name="fromRef">name of ref to rename from</param>
326
/// <param name="toRef">name of ref to rename to</param>
327
/// <returns>an update command that knows how to rename a branch to another.</returns>
328
/// <exception cref="System.IO.IOException">the rename could not be performed.</exception>
329
public virtual RefRename RenameRef(string fromRef, string toRef)
331
return RefDatabase.NewRename(fromRef, toRef);
334
/// <summary>Parse a git revision string and return an object id.</summary>
336
/// Parse a git revision string and return an object id.
337
/// Combinations of these operators are supported:
339
/// <li><b>HEAD</b>, <b>MERGE_HEAD</b>, <b>FETCH_HEAD</b></li>
340
/// <li><b>SHA-1</b>: a complete or abbreviated SHA-1</li>
341
/// <li><b>refs/...</b>: a complete reference name</li>
342
/// <li><b>short-name</b>: a short reference name under
343
/// <code>refs/heads</code>
345
/// <code>refs/tags</code>
347
/// <code>refs/remotes</code>
349
/// <li><b>tag-NN-gABBREV</b>: output from describe, parsed by treating
350
/// <code>ABBREV</code>
351
/// as an abbreviated SHA-1.</li>
352
/// <li><i>id</i><b>^</b>: first parent of commit <i>id</i>, this is the same
354
/// <code>id^1</code>
356
/// <li><i>id</i><b>^0</b>: ensure <i>id</i> is a commit</li>
357
/// <li><i>id</i><b>^n</b>: n-th parent of commit <i>id</i></li>
358
/// <li><i>id</i><b>~n</b>: n-th historical ancestor of <i>id</i>, by first
360
/// <code>id~3</code>
362
/// <code>id^1^1^1</code>
364
/// <code>id^^^</code>
366
/// <li><i>id</i><b>:path</b>: Lookup path under tree named by <i>id</i></li>
367
/// <li><i>id</i><b>^{commit}</b>: ensure <i>id</i> is a commit</li>
368
/// <li><i>id</i><b>^{tree}</b>: ensure <i>id</i> is a tree</li>
369
/// <li><i>id</i><b>^{tag}</b>: ensure <i>id</i> is a tag</li>
370
/// <li><i>id</i><b>^{blob}</b>: ensure <i>id</i> is a blob</li>
373
/// The following operators are specified by Git conventions, but are not
374
/// supported by this method:
376
/// <li><b>ref@{n}</b>: n-th version of ref as given by its reflog</li>
377
/// <li><b>ref@{time}</b>: value of ref at the designated time</li>
380
/// <param name="revstr">A git object references expression</param>
381
/// <returns>an ObjectId or null if revstr can't be resolved to any ObjectId</returns>
382
/// <exception cref="NGit.Errors.AmbiguousObjectException">
383
/// <code>revstr</code>
384
/// contains an abbreviated ObjectId and this
385
/// repository contains more than one object which match to the
386
/// input abbreviation.
388
/// <exception cref="NGit.Errors.IncorrectObjectTypeException">
389
/// the id parsed does not meet the type required to finish
390
/// applying the operators in the expression.
392
/// <exception cref="NGit.Errors.RevisionSyntaxException">
393
/// the expression is not supported by this implementation, or
394
/// does not meet the standard syntax.
396
/// <exception cref="System.IO.IOException">on serious errors</exception>
397
public virtual ObjectId Resolve(string revstr)
399
RevWalk rw = new RevWalk(this);
402
return Resolve(rw, revstr);
410
/// <exception cref="System.IO.IOException"></exception>
411
private ObjectId Resolve(RevWalk rw, string revstr)
413
char[] rev = revstr.ToCharArray();
414
RevObject @ref = null;
415
for (int i = 0; i < rev.Length; ++i)
423
@ref = ParseSimple(rw, new string(rev, 0, i));
429
if (i + 1 < rev.Length)
445
@ref = rw.ParseCommit(@ref);
446
for (j = i + 1; j < rev.Length; ++j)
448
if (!char.IsDigit(rev[j]))
453
string parentnum = new string(rev, i + 1, j - i - 1);
457
pnum = System.Convert.ToInt32(parentnum);
459
catch (FormatException)
461
throw new RevisionSyntaxException(JGitText.Get().invalidCommitParentNumber, revstr
466
RevCommit commit = (RevCommit)@ref;
467
if (pnum > commit.ParentCount)
473
@ref = commit.GetParent(pnum - 1);
484
for (k = i + 2; k < rev.Length; ++k)
488
item = new string(rev, i + 2, k - i - 2);
495
if (item.Equals("tree"))
497
@ref = rw.ParseTree(@ref);
501
if (item.Equals("commit"))
503
@ref = rw.ParseCommit(@ref);
507
if (item.Equals("blob"))
509
@ref = rw.Peel(@ref);
510
if (!(@ref is RevBlob))
512
throw new IncorrectObjectTypeException(@ref, Constants.TYPE_BLOB);
517
if (item.Equals(string.Empty))
519
@ref = rw.Peel(@ref);
523
throw new RevisionSyntaxException(revstr);
531
throw new RevisionSyntaxException(revstr);
538
@ref = rw.ParseAny(@ref);
539
if (@ref is RevCommit)
541
RevCommit commit = ((RevCommit)@ref);
542
if (commit.ParentCount == 0)
548
@ref = commit.GetParent(0);
553
throw new IncorrectObjectTypeException(@ref, Constants.TYPE_COMMIT);
561
@ref = rw.Peel(@ref);
562
if (@ref is RevCommit)
564
RevCommit commit = ((RevCommit)@ref);
565
if (commit.ParentCount == 0)
571
@ref = commit.GetParent(0);
576
throw new IncorrectObjectTypeException(@ref, Constants.TYPE_COMMIT);
586
@ref = ParseSimple(rw, new string(rev, 0, i));
592
@ref = rw.Peel(@ref);
593
if (!(@ref is RevCommit))
595
throw new IncorrectObjectTypeException(@ref, Constants.TYPE_COMMIT);
598
for (l = i + 1; l < rev.Length; ++l)
600
if (!char.IsDigit(rev[l]))
605
string distnum = new string(rev, i + 1, l - i - 1);
609
dist = System.Convert.ToInt32(distnum);
611
catch (FormatException)
613
throw new RevisionSyntaxException(JGitText.Get().invalidAncestryLength, revstr);
617
RevCommit commit = (RevCommit)@ref;
618
if (commit.ParentCount == 0)
623
commit = commit.GetParent(0);
624
rw.ParseHeaders(commit);
636
for (m = i + 2; m < rev.Length; ++m)
640
time = new string(rev, i + 2, m - i - 2);
646
throw new RevisionSyntaxException(JGitText.Get().reflogsNotYetSupportedByRevisionParser
658
// We might not yet have parsed the left hand side.
664
id = Resolve(rw, Constants.HEAD);
668
id = Resolve(rw, new string(rev, 0, i));
671
catch (RevisionSyntaxException)
673
throw new RevisionSyntaxException(revstr);
679
tree = rw.ParseTree(id);
683
tree = rw.ParseTree(@ref);
685
if (i == rev.Length - i)
689
TreeWalk tw = TreeWalk.ForPath(rw.GetObjectReader(), new string(rev, i + 1, rev.Length
691
return tw != null ? tw.GetObjectId(0) : null;
698
throw new RevisionSyntaxException(revstr);
704
return @ref != null ? @ref.Copy() : ResolveSimple(revstr);
707
private static bool IsHex(char c)
709
return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
714
private static bool IsAllHex(string str, int ptr)
716
while (ptr < str.Length)
718
if (!IsHex(str[ptr++]))
726
/// <exception cref="System.IO.IOException"></exception>
727
private RevObject ParseSimple(RevWalk rw, string revstr)
729
ObjectId id = ResolveSimple(revstr);
730
return id != null ? rw.ParseAny(id) : null;
733
/// <exception cref="System.IO.IOException"></exception>
734
private ObjectId ResolveSimple(string revstr)
736
if (ObjectId.IsId(revstr))
738
return ObjectId.FromString(revstr);
740
Ref r = RefDatabase.GetRef(revstr);
743
return r.GetObjectId();
745
if (AbbreviatedObjectId.IsId(revstr))
747
return ResolveAbbreviation(revstr);
749
int dashg = revstr.IndexOf("-g");
750
if (4 < revstr.Length && 0 <= dashg && IsHex(revstr[dashg + 2]) && IsHex(revstr[dashg
751
+ 3]) && IsAllHex(revstr, dashg + 4))
753
// Possibly output from git describe?
754
string s = Sharpen.Runtime.Substring(revstr, dashg + 2);
755
if (AbbreviatedObjectId.IsId(s))
757
return ResolveAbbreviation(s);
763
/// <exception cref="System.IO.IOException"></exception>
764
/// <exception cref="NGit.Errors.AmbiguousObjectException"></exception>
765
private ObjectId ResolveAbbreviation(string revstr)
767
AbbreviatedObjectId id = AbbreviatedObjectId.FromString(revstr);
768
ObjectReader reader = NewObjectReader();
771
ICollection<ObjectId> matches = reader.Resolve(id);
772
if (matches.Count == 0)
778
if (matches.Count == 1)
780
return matches.Iterator().Next();
784
throw new AmbiguousObjectException(id, matches);
795
/// Increment the use counter by one, requiring a matched
796
/// <see cref="Close()">Close()</see>
799
public virtual void IncrementOpen()
801
useCnt.IncrementAndGet();
804
/// <summary>Decrement the use count, and maybe close resources.</summary>
805
/// <remarks>Decrement the use count, and maybe close resources.</remarks>
806
public virtual void Close()
808
if (useCnt.DecrementAndGet() == 0)
815
/// Invoked when the use count drops to zero during
816
/// <see cref="Close()">Close()</see>
819
/// The default implementation closes the object and ref databases.
821
protected internal virtual void DoClose()
823
ObjectDatabase.Close();
827
public override string ToString()
830
if (Directory != null)
832
desc = Directory.GetPath();
836
desc = GetType().Name + "-" + Runtime.IdentityHashCode(this);
838
return "Repository[" + desc + "]";
842
/// Get the name of the reference that
843
/// <code>HEAD</code>
846
/// This is essentially the same as doing:
848
/// return getRef(Constants.HEAD).getTarget().getName()
850
/// Except when HEAD is detached, in which case this method returns the
851
/// current ObjectId in hexadecimal string format.
854
/// name of current branch (for example
855
/// <code>refs/heads/master</code>
857
/// an ObjectId in hex format if the current branch is detached.
859
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
860
public virtual string GetFullBranch()
862
Ref head = GetRef(Constants.HEAD);
867
if (head.IsSymbolic())
869
return head.GetTarget().GetName();
871
if (head.GetObjectId() != null)
873
return head.GetObjectId().Name;
879
/// Get the short name of the current branch that
880
/// <code>HEAD</code>
883
/// This is essentially the same as
884
/// <see cref="GetFullBranch()">GetFullBranch()</see>
887
/// <code>refs/heads/</code>
888
/// is removed from the reference before
889
/// it is returned to the caller.
892
/// name of current branch (for example
893
/// <code>master</code>
895
/// ObjectId in hex format if the current branch is detached.
897
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
898
public virtual string GetBranch()
900
string name = GetFullBranch();
903
return ShortenRefName(name);
909
/// Objects known to exist but not expressed by
910
/// <see cref="GetAllRefs()">GetAllRefs()</see>
913
/// When a repository borrows objects from another repository, it can
914
/// advertise that it safely has that other repository's references, without
915
/// exposing any other details about the other repository. This may help
916
/// a client trying to push changes avoid pushing more than it needs to.
918
/// <returns>unmodifiable collection of other known objects.</returns>
919
public virtual ICollection<ObjectId> GetAdditionalHaves()
921
return Sharpen.Collections.EmptySet<ObjectId>();
924
/// <summary>Get a ref by name.</summary>
925
/// <remarks>Get a ref by name.</remarks>
926
/// <param name="name">
927
/// the name of the ref to lookup. May be a short-hand form, e.g.
928
/// "master" which is is automatically expanded to
929
/// "refs/heads/master" if "refs/heads/master" already exists.
931
/// <returns>the Ref with the given name, or null if it does not exist</returns>
932
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
933
public virtual Ref GetRef(string name)
935
return RefDatabase.GetRef(name);
938
/// <returns>mutable map of all known refs (heads, tags, remotes).</returns>
939
public virtual IDictionary<string, Ref> GetAllRefs()
943
return RefDatabase.GetRefs(NGit.RefDatabase.ALL);
947
return new Dictionary<string, Ref>();
952
/// mutable map of all tags; key is short tag name ("v1.0") and value
953
/// of the entry contains the ref with the full tag name
954
/// ("refs/tags/v1.0").
956
public virtual IDictionary<string, Ref> GetTags()
960
return RefDatabase.GetRefs(Constants.R_TAGS);
964
return new Dictionary<string, Ref>();
968
/// <summary>Peel a possibly unpeeled reference to an annotated tag.</summary>
970
/// Peel a possibly unpeeled reference to an annotated tag.
972
/// If the ref cannot be peeled (as it does not refer to an annotated tag)
973
/// the peeled id stays null, but
974
/// <see cref="Ref.IsPeeled()">Ref.IsPeeled()</see>
977
/// <param name="ref">The ref to peel</param>
979
/// <code>ref</code> if <code>ref.isPeeled()</code> is true; else a
980
/// new Ref object representing the same data as Ref, but isPeeled()
981
/// will be true and getPeeledObjectId will contain the peeled object
984
public virtual Ref Peel(Ref @ref)
988
return RefDatabase.Peel(@ref);
992
// Historical accident; if the reference cannot be peeled due
993
// to some sort of repository access problem we claim that the
994
// same as if the reference was not an annotated tag.
999
/// <returns>a map with all objects referenced by a peeled ref.</returns>
1000
public virtual IDictionary<AnyObjectId, ICollection<Ref>> GetAllRefsByPeeledObjectId
1003
IDictionary<string, Ref> allRefs = GetAllRefs();
1004
IDictionary<AnyObjectId, ICollection<Ref>> ret = new Dictionary<AnyObjectId, ICollection
1005
<Ref>>(allRefs.Count);
1006
foreach (Ref iref in allRefs.Values)
1008
Ref @ref = Peel(iref);
1009
AnyObjectId target = @ref.GetPeeledObjectId();
1012
target = @ref.GetObjectId();
1014
// We assume most Sets here are singletons
1015
ICollection<Ref> oset = ret.Put(target, Sharpen.Collections.Singleton(@ref));
1018
// that was not the case (rare)
1019
if (oset.Count == 1)
1021
// Was a read-only singleton, we must copy to a new Set
1022
oset = new HashSet<Ref>(oset);
1024
ret.Put(target, oset);
1032
/// a representation of the index associated with this
1033
/// <see cref="Repository">Repository</see>
1035
/// <exception cref="System.IO.IOException">if the index can not be read</exception>
1036
/// <exception cref="NGit.Errors.NoWorkTreeException">
1037
/// if this is bare, which implies it has no working directory.
1039
/// <see cref="IsBare()">IsBare()</see>
1042
public virtual GitIndex GetIndex()
1046
throw new NoWorkTreeException();
1050
index = new GitIndex(this);
1055
index.RereadIfNecessary();
1060
/// <returns>the index file location</returns>
1061
/// <exception cref="NGit.Errors.NoWorkTreeException">
1062
/// if this is bare, which implies it has no working directory.
1064
/// <see cref="IsBare()">IsBare()</see>
1067
public virtual FilePath GetIndexFile()
1071
throw new NoWorkTreeException();
1076
/// <summary>Create a new in-core index representation and read an index from disk.</summary>
1078
/// Create a new in-core index representation and read an index from disk.
1080
/// The new index will be read before it is returned to the caller. Read
1081
/// failures are reported as exceptions and therefore prevent the method from
1082
/// returning a partially populated index.
1085
/// a cache representing the contents of the specified index file (if
1086
/// it exists) or an empty cache if the file does not exist.
1088
/// <exception cref="NGit.Errors.NoWorkTreeException">
1089
/// if this is bare, which implies it has no working directory.
1091
/// <see cref="IsBare()">IsBare()</see>
1094
/// <exception cref="System.IO.IOException">the index file is present but could not be read.
1096
/// <exception cref="NGit.Errors.CorruptObjectException">
1097
/// the index file is using a format or extension that this
1098
/// library does not support.
1100
public virtual DirCache ReadDirCache()
1102
return DirCache.Read(GetIndexFile(), FileSystem);
1105
/// <summary>Create a new in-core index representation, lock it, and read from disk.</summary>
1107
/// Create a new in-core index representation, lock it, and read from disk.
1109
/// The new index will be locked and then read before it is returned to the
1110
/// caller. Read failures are reported as exceptions and therefore prevent
1111
/// the method from returning a partially populated index.
1114
/// a cache representing the contents of the specified index file (if
1115
/// it exists) or an empty cache if the file does not exist.
1117
/// <exception cref="NGit.Errors.NoWorkTreeException">
1118
/// if this is bare, which implies it has no working directory.
1120
/// <see cref="IsBare()">IsBare()</see>
1123
/// <exception cref="System.IO.IOException">
1124
/// the index file is present but could not be read, or the lock
1125
/// could not be obtained.
1127
/// <exception cref="NGit.Errors.CorruptObjectException">
1128
/// the index file is using a format or extension that this
1129
/// library does not support.
1131
public virtual DirCache LockDirCache()
1133
return DirCache.Lock(GetIndexFile(), FileSystem);
1136
internal static byte[] GitInternalSlash(byte[] bytes)
1138
if (FilePath.separatorChar == '/')
1142
for (int i = 0; i < bytes.Length; ++i)
1144
if (bytes[i] == FilePath.separatorChar)
1146
bytes[i] = (byte)('/');
1152
/// <returns>an important state</returns>
1153
public virtual RepositoryState GetRepositoryState()
1155
if (IsBare || Directory == null)
1157
return RepositoryState.BARE;
1159
// Pre Git-1.6 logic
1160
if (new FilePath(WorkTree, ".dotest").Exists())
1162
return RepositoryState.REBASING;
1164
if (new FilePath(Directory, ".dotest-merge").Exists())
1166
return RepositoryState.REBASING_INTERACTIVE;
1169
if (new FilePath(Directory, "rebase-apply/rebasing").Exists())
1171
return RepositoryState.REBASING_REBASING;
1173
if (new FilePath(Directory, "rebase-apply/applying").Exists())
1175
return RepositoryState.APPLY;
1177
if (new FilePath(Directory, "rebase-apply").Exists())
1179
return RepositoryState.REBASING;
1181
if (new FilePath(Directory, "rebase-merge/interactive").Exists())
1183
return RepositoryState.REBASING_INTERACTIVE;
1185
if (new FilePath(Directory, "rebase-merge").Exists())
1187
return RepositoryState.REBASING_MERGE;
1190
if (new FilePath(Directory, "MERGE_HEAD").Exists())
1192
// we are merging - now check whether we have unmerged paths
1195
if (!ReadDirCache().HasUnmergedPaths())
1197
// no unmerged paths -> return the MERGING_RESOLVED state
1198
return RepositoryState.MERGING_RESOLVED;
1201
catch (IOException e)
1203
// Can't decide whether unmerged paths exists. Return
1204
// MERGING state to be on the safe side (in state MERGING
1205
// you are not allow to do anything)
1206
Sharpen.Runtime.PrintStackTrace(e);
1208
return RepositoryState.MERGING;
1210
if (new FilePath(Directory, "BISECT_LOG").Exists())
1212
return RepositoryState.BISECTING;
1214
return RepositoryState.SAFE;
1217
/// <summary>Check validity of a ref name.</summary>
1219
/// Check validity of a ref name. It must not contain character that has
1220
/// a special meaning in a Git object reference expression. Some other
1221
/// dangerous characters are also excluded.
1222
/// For portability reasons '\' is excluded
1224
/// <param name="refName"></param>
1225
/// <returns>true if refName is a valid ref name</returns>
1226
public static bool IsValidRefName(string refName)
1228
int len = refName.Length;
1233
if (refName.EndsWith(".lock"))
1239
for (int i = 0; i < len; i++)
1241
char c = refName[i];
1268
if (i == 0 || i == len - 1)
1298
return components > 1;
1301
/// <summary>Strip work dir and return normalized repository path.</summary>
1302
/// <remarks>Strip work dir and return normalized repository path.</remarks>
1303
/// <param name="workDir">Work dir</param>
1304
/// <param name="file">File whose path shall be stripped of its workdir</param>
1306
/// normalized repository relative path or the empty
1307
/// string if the file is not relative to the work directory.
1309
public static string StripWorkDir(FilePath workDir, FilePath file)
1311
string filePath = file.GetPath();
1312
string workDirPath = workDir.GetPath();
1313
if (filePath.Length <= workDirPath.Length || filePath[workDirPath.Length] != FilePath
1314
.separatorChar || !filePath.StartsWith(workDirPath))
1316
FilePath absWd = workDir.IsAbsolute() ? workDir : workDir.GetAbsoluteFile();
1317
FilePath absFile = file.IsAbsolute() ? file : file.GetAbsoluteFile();
1318
if (absWd == workDir && absFile == file)
1320
return string.Empty;
1322
return StripWorkDir(absWd, absFile);
1324
string relName = Sharpen.Runtime.Substring(filePath, workDirPath.Length + 1);
1325
if (FilePath.separatorChar != '/')
1327
relName = relName.Replace(FilePath.separatorChar, '/');
1332
/// <returns>true if this is bare, which implies it has no working directory.</returns>
1333
public virtual bool IsBare
1337
return workTree == null;
1342
/// the root directory of the working tree, where files are checked
1343
/// out for viewing and editing.
1345
/// <exception cref="NGit.Errors.NoWorkTreeException">
1346
/// if this is bare, which implies it has no working directory.
1348
/// <see cref="IsBare()">IsBare()</see>
1351
public virtual FilePath WorkTree
1357
throw new NoWorkTreeException();
1363
/// <summary>Force a scan for changed refs.</summary>
1364
/// <remarks>Force a scan for changed refs.</remarks>
1365
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
1366
public abstract void ScanForRepoChanges();
1368
/// <param name="refName"></param>
1369
/// <returns>a more user friendly ref name</returns>
1370
public static string ShortenRefName(string refName)
1372
if (refName.StartsWith(Constants.R_HEADS))
1374
return Sharpen.Runtime.Substring(refName, Constants.R_HEADS.Length);
1376
if (refName.StartsWith(Constants.R_TAGS))
1378
return Sharpen.Runtime.Substring(refName, Constants.R_TAGS.Length);
1380
if (refName.StartsWith(Constants.R_REMOTES))
1382
return Sharpen.Runtime.Substring(refName, Constants.R_REMOTES.Length);
1387
/// <param name="refName"></param>
1390
/// <see cref="NGit.Storage.File.ReflogReader">NGit.Storage.File.ReflogReader</see>
1391
/// for the supplied refname, or null if the
1392
/// named ref does not exist.
1394
/// <exception cref="System.IO.IOException">the ref could not be accessed.</exception>
1395
public abstract ReflogReader GetReflogReader(string refName);
1397
/// <summary>Return the information stored in the file $GIT_DIR/MERGE_MSG.</summary>
1399
/// Return the information stored in the file $GIT_DIR/MERGE_MSG. In this
1400
/// file operations triggering a merge will store a template for the commit
1401
/// message of the merge commit.
1404
/// a String containing the content of the MERGE_MSG file or
1405
/// <code>null</code>
1406
/// if this file doesn't exist
1408
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
1409
/// <exception cref="NGit.Errors.NoWorkTreeException">
1410
/// if this is bare, which implies it has no working directory.
1412
/// <see cref="IsBare()">IsBare()</see>
1415
public virtual string ReadMergeCommitMsg()
1417
if (IsBare || Directory == null)
1419
throw new NoWorkTreeException();
1421
FilePath mergeMsgFile = new FilePath(Directory, Constants.MERGE_MSG);
1424
return RawParseUtils.Decode(IOUtil.ReadFully(mergeMsgFile));
1426
catch (FileNotFoundException)
1428
// MERGE_MSG file has disappeared in the meantime
1434
/// <summary>Write new content to the file $GIT_DIR/MERGE_MSG.</summary>
1436
/// Write new content to the file $GIT_DIR/MERGE_MSG. In this file operations
1437
/// triggering a merge will store a template for the commit message of the
1438
/// merge commit. If <code>null</code> is specified as message the file will
1441
/// <param name="msg">
1442
/// the message which should be written or <code>null</code> to
1445
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
1446
public virtual void WriteMergeCommitMsg(string msg)
1448
FilePath mergeMsgFile = new FilePath(gitDir, Constants.MERGE_MSG);
1451
FileOutputStream fos = new FileOutputStream(mergeMsgFile);
1454
fos.Write(Sharpen.Runtime.GetBytesForString(msg, Constants.CHARACTER_ENCODING));
1463
FileUtils.Delete(mergeMsgFile);
1467
/// <summary>Return the information stored in the file $GIT_DIR/MERGE_HEAD.</summary>
1469
/// Return the information stored in the file $GIT_DIR/MERGE_HEAD. In this
1470
/// file operations triggering a merge will store the IDs of all heads which
1471
/// should be merged together with HEAD.
1474
/// a list of commits which IDs are listed in the MERGE_HEAD
1476
/// <code>null</code>
1477
/// if this file doesn't exist. Also if the file
1478
/// exists but is empty
1479
/// <code>null</code>
1480
/// will be returned
1482
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
1483
/// <exception cref="NGit.Errors.NoWorkTreeException">
1484
/// if this is bare, which implies it has no working directory.
1486
/// <see cref="IsBare()">IsBare()</see>
1489
public virtual IList<ObjectId> ReadMergeHeads()
1491
if (IsBare || Directory == null)
1493
throw new NoWorkTreeException();
1495
FilePath mergeHeadFile = new FilePath(Directory, Constants.MERGE_HEAD);
1499
raw = IOUtil.ReadFully(mergeHeadFile);
1501
catch (FileNotFoundException)
1505
if (raw.Length == 0)
1509
List<ObjectId> heads = new List<ObjectId>();
1510
for (int p = 0; p < raw.Length; )
1512
heads.AddItem(ObjectId.FromString(raw, p));
1513
p = RawParseUtils.NextLF(raw, p + Constants.OBJECT_ID_STRING_LENGTH);
1518
/// <summary>Write new merge-heads into $GIT_DIR/MERGE_HEAD.</summary>
1520
/// Write new merge-heads into $GIT_DIR/MERGE_HEAD. In this file operations
1521
/// triggering a merge will store the IDs of all heads which should be merged
1522
/// together with HEAD. If <code>null</code> is specified as list of commits
1523
/// the file will be deleted
1525
/// <param name="heads">
1526
/// a list of commits which IDs should be written to
1527
/// $GIT_DIR/MERGE_HEAD or <code>null</code> to delete the file
1529
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
1530
public virtual void WriteMergeHeads(IList<ObjectId> heads)
1532
FilePath mergeHeadFile = new FilePath(gitDir, Constants.MERGE_HEAD);
1535
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(mergeHeadFile
1539
foreach (ObjectId id in heads)
1552
FileUtils.Delete(mergeHeadFile);