~ubuntu-branches/ubuntu/oneiric/monodevelop/oneiric

« back to all changes in this revision

Viewing changes to contrib/NGit/NGit/Repository.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2011-06-27 17:03:13 UTC
  • mto: (1.8.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 54.
  • Revision ID: james.westby@ubuntu.com-20110627170313-6cvz3s19x6e9hqe9
ImportĀ upstreamĀ versionĀ 2.5.92+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
This code is derived from jgit (http://eclipse.org/jgit).
 
3
Copyright owners are documented in jgit's IP log.
 
4
 
 
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
 
9
 
 
10
All rights reserved.
 
11
 
 
12
Redistribution and use in source and binary forms, with or
 
13
without modification, are permitted provided that the following
 
14
conditions are met:
 
15
 
 
16
- Redistributions of source code must retain the above copyright
 
17
  notice, this list of conditions and the following disclaimer.
 
18
 
 
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.
 
23
 
 
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
 
27
  written permission.
 
28
 
 
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.
 
42
*/
 
43
 
 
44
using System;
 
45
using System.Collections.Generic;
 
46
using System.IO;
 
47
using NGit;
 
48
using NGit.Dircache;
 
49
using NGit.Errors;
 
50
using NGit.Events;
 
51
using NGit.Revwalk;
 
52
using NGit.Storage.File;
 
53
using NGit.Treewalk;
 
54
using NGit.Util;
 
55
using Sharpen;
 
56
 
 
57
namespace NGit
 
58
{
 
59
        /// <summary>Represents a Git repository.</summary>
 
60
        /// <remarks>
 
61
        /// Represents a Git repository.
 
62
        /// <p>
 
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).
 
65
        /// <p>
 
66
        /// This class is thread-safe.
 
67
        /// </remarks>
 
68
        public abstract class Repository
 
69
        {
 
70
                private static readonly ListenerList globalListeners = new ListenerList();
 
71
 
 
72
                /// <returns>the global listener list observing all events in this JVM.</returns>
 
73
                public static ListenerList GetGlobalListenerList()
 
74
                {
 
75
                        return globalListeners;
 
76
                }
 
77
 
 
78
                private readonly AtomicInteger useCnt = new AtomicInteger(1);
 
79
 
 
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;
 
83
 
 
84
                /// <summary>File abstraction used to resolve paths.</summary>
 
85
                /// <remarks>File abstraction used to resolve paths.</remarks>
 
86
                private readonly FS fs;
 
87
 
 
88
                private GitIndex index;
 
89
 
 
90
                private readonly ListenerList myListeners = new ListenerList();
 
91
 
 
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;
 
95
 
 
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;
 
99
 
 
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)
 
104
                {
 
105
                        gitDir = options.GetGitDir();
 
106
                        fs = options.GetFS();
 
107
                        workTree = options.GetWorkTree();
 
108
                        indexFile = options.GetIndexFile();
 
109
                }
 
110
 
 
111
                /// <returns>listeners observing only events on this repository.</returns>
 
112
                public virtual ListenerList Listeners
 
113
                {
 
114
                        get
 
115
                        {
 
116
                                return myListeners;
 
117
                        }
 
118
                }
 
119
 
 
120
                /// <summary>Fire an event to all registered listeners.</summary>
 
121
                /// <remarks>
 
122
                /// Fire an event to all registered listeners.
 
123
                /// <p>
 
124
                /// The source repository of the event is automatically set to this
 
125
                /// repository, before the event is delivered to any listeners.
 
126
                /// </remarks>
 
127
                /// <param name="event">the event to deliver.</param>
 
128
                public virtual void FireEvent<_T0>(RepositoryEvent<_T0> @event) where _T0:RepositoryListener
 
129
                {
 
130
                        @event.SetRepository(this);
 
131
                        myListeners.Dispatch(@event);
 
132
                        globalListeners.Dispatch(@event);
 
133
                }
 
134
 
 
135
                /// <summary>Create a new Git repository.</summary>
 
136
                /// <remarks>
 
137
                /// Create a new Git repository.
 
138
                /// <p>
 
139
                /// Repository with working tree is created using this method. This method is
 
140
                /// the same as
 
141
                /// <code>create(false)</code>
 
142
                /// .
 
143
                /// </remarks>
 
144
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
145
                /// <seealso cref="Create(bool)">Create(bool)</seealso>
 
146
                public virtual void Create()
 
147
                {
 
148
                        Create(false);
 
149
                }
 
150
 
 
151
                /// <summary>
 
152
                /// Create a new Git repository initializing the necessary files and
 
153
                /// directories.
 
154
                /// </summary>
 
155
                /// <remarks>
 
156
                /// Create a new Git repository initializing the necessary files and
 
157
                /// directories.
 
158
                /// </remarks>
 
159
                /// <param name="bare">
 
160
                /// if true, a bare repository (a repository without a working
 
161
                /// directory) is created.
 
162
                /// </param>
 
163
                /// <exception cref="System.IO.IOException">in case of IO problem</exception>
 
164
                public abstract void Create(bool bare);
 
165
 
 
166
                /// <returns>local metadata directory; null if repository isn't local.</returns>
 
167
                public virtual FilePath Directory
 
168
                {
 
169
                        get
 
170
                        {
 
171
                                return gitDir;
 
172
                        }
 
173
                }
 
174
 
 
175
                /// <returns>the object database which stores this repository's data.</returns>
 
176
                public abstract NGit.ObjectDatabase ObjectDatabase
 
177
                {
 
178
                        get;
 
179
                }
 
180
 
 
181
                /// <returns>
 
182
                /// a new inserter to create objects in
 
183
                /// <see cref="ObjectDatabase()">ObjectDatabase()</see>
 
184
                /// 
 
185
                /// </returns>
 
186
                public virtual ObjectInserter NewObjectInserter()
 
187
                {
 
188
                        return ObjectDatabase.NewInserter();
 
189
                }
 
190
 
 
191
                /// <returns>
 
192
                /// a new reader to read objects from
 
193
                /// <see cref="ObjectDatabase()">ObjectDatabase()</see>
 
194
                /// 
 
195
                /// </returns>
 
196
                public virtual ObjectReader NewObjectReader()
 
197
                {
 
198
                        return ObjectDatabase.NewReader();
 
199
                }
 
200
 
 
201
                /// <returns>the reference database which stores the reference namespace.</returns>
 
202
                public abstract NGit.RefDatabase RefDatabase
 
203
                {
 
204
                        get;
 
205
                }
 
206
 
 
207
                /// <returns>the configuration of this repository</returns>
 
208
                public abstract StoredConfig GetConfig();
 
209
 
 
210
                /// <returns>the used file system abstraction</returns>
 
211
                public virtual FS FileSystem
 
212
                {
 
213
                        get
 
214
                        {
 
215
                                return fs;
 
216
                        }
 
217
                }
 
218
 
 
219
                /// <param name="objectId"></param>
 
220
                /// <returns>
 
221
                /// true if the specified object is stored in this repo or any of the
 
222
                /// known shared repositories.
 
223
                /// </returns>
 
224
                public virtual bool HasObject(AnyObjectId objectId)
 
225
                {
 
226
                        try
 
227
                        {
 
228
                                return ObjectDatabase.Has(objectId);
 
229
                        }
 
230
                        catch (IOException)
 
231
                        {
 
232
                                // Legacy API, assume error means "no"
 
233
                                return false;
 
234
                        }
 
235
                }
 
236
 
 
237
                /// <summary>Open an object from this repository.</summary>
 
238
                /// <remarks>
 
239
                /// Open an object from this repository.
 
240
                /// <p>
 
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.
 
244
                /// </remarks>
 
245
                /// <param name="objectId">identity of the object to open.</param>
 
246
                /// <returns>
 
247
                /// a
 
248
                /// <see cref="ObjectLoader">ObjectLoader</see>
 
249
                /// for accessing the object.
 
250
                /// </returns>
 
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)
 
254
                {
 
255
                        return ObjectDatabase.Open(objectId);
 
256
                }
 
257
 
 
258
                /// <summary>Open an object from this repository.</summary>
 
259
                /// <remarks>
 
260
                /// Open an object from this repository.
 
261
                /// <p>
 
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.
 
265
                /// </remarks>
 
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.
 
272
                /// </param>
 
273
                /// <returns>
 
274
                /// a
 
275
                /// <see cref="ObjectLoader">ObjectLoader</see>
 
276
                /// for accessing the object.
 
277
                /// </returns>
 
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.
 
282
                /// </exception>
 
283
                /// <exception cref="System.IO.IOException">the object store cannot be accessed.</exception>
 
284
                public virtual ObjectLoader Open(AnyObjectId objectId, int typeHint)
 
285
                {
 
286
                        return ObjectDatabase.Open(objectId, typeHint);
 
287
                }
 
288
 
 
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>
 
292
                /// <returns>
 
293
                /// an update command. The caller must finish populating this command
 
294
                /// and then invoke one of the update methods to actually make a
 
295
                /// change.
 
296
                /// </returns>
 
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.
 
300
                /// </exception>
 
301
                public virtual RefUpdate UpdateRef(string @ref)
 
302
                {
 
303
                        return UpdateRef(@ref, false);
 
304
                }
 
305
 
 
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>
 
310
                /// <returns>
 
311
                /// an update command. The caller must finish populating this command
 
312
                /// and then invoke one of the update methods to actually make a
 
313
                /// change.
 
314
                /// </returns>
 
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.
 
318
                /// </exception>
 
319
                public virtual RefUpdate UpdateRef(string @ref, bool detach)
 
320
                {
 
321
                        return RefDatabase.NewUpdate(@ref, detach);
 
322
                }
 
323
 
 
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)
 
330
                {
 
331
                        return RefDatabase.NewRename(fromRef, toRef);
 
332
                }
 
333
 
 
334
                /// <summary>Parse a git revision string and return an object id.</summary>
 
335
                /// <remarks>
 
336
                /// Parse a git revision string and return an object id.
 
337
                /// Combinations of these operators are supported:
 
338
                /// <ul>
 
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>
 
344
                /// ,
 
345
                /// <code>refs/tags</code>
 
346
                /// , or
 
347
                /// <code>refs/remotes</code>
 
348
                /// namespace</li>
 
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
 
353
                /// as
 
354
                /// <code>id^1</code>
 
355
                /// </li>
 
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
 
359
                /// parent.
 
360
                /// <code>id~3</code>
 
361
                /// is equivalent to
 
362
                /// <code>id^1^1^1</code>
 
363
                /// or
 
364
                /// <code>id^^^</code>
 
365
                /// .</li>
 
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>
 
371
                /// </ul>
 
372
                /// <p>
 
373
                /// The following operators are specified by Git conventions, but are not
 
374
                /// supported by this method:
 
375
                /// <ul>
 
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>
 
378
                /// </ul>
 
379
                /// </remarks>
 
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.
 
387
                /// </exception>
 
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.
 
391
                /// </exception>
 
392
                /// <exception cref="NGit.Errors.RevisionSyntaxException">
 
393
                /// the expression is not supported by this implementation, or
 
394
                /// does not meet the standard syntax.
 
395
                /// </exception>
 
396
                /// <exception cref="System.IO.IOException">on serious errors</exception>
 
397
                public virtual ObjectId Resolve(string revstr)
 
398
                {
 
399
                        RevWalk rw = new RevWalk(this);
 
400
                        try
 
401
                        {
 
402
                                return Resolve(rw, revstr);
 
403
                        }
 
404
                        finally
 
405
                        {
 
406
                                rw.Release();
 
407
                        }
 
408
                }
 
409
 
 
410
                /// <exception cref="System.IO.IOException"></exception>
 
411
                private ObjectId Resolve(RevWalk rw, string revstr)
 
412
                {
 
413
                        char[] rev = revstr.ToCharArray();
 
414
                        RevObject @ref = null;
 
415
                        for (int i = 0; i < rev.Length; ++i)
 
416
                        {
 
417
                                switch (rev[i])
 
418
                                {
 
419
                                        case '^':
 
420
                                        {
 
421
                                                if (@ref == null)
 
422
                                                {
 
423
                                                        @ref = ParseSimple(rw, new string(rev, 0, i));
 
424
                                                        if (@ref == null)
 
425
                                                        {
 
426
                                                                return null;
 
427
                                                        }
 
428
                                                }
 
429
                                                if (i + 1 < rev.Length)
 
430
                                                {
 
431
                                                        switch (rev[i + 1])
 
432
                                                        {
 
433
                                                                case '0':
 
434
                                                                case '1':
 
435
                                                                case '2':
 
436
                                                                case '3':
 
437
                                                                case '4':
 
438
                                                                case '5':
 
439
                                                                case '6':
 
440
                                                                case '7':
 
441
                                                                case '8':
 
442
                                                                case '9':
 
443
                                                                {
 
444
                                                                        int j;
 
445
                                                                        @ref = rw.ParseCommit(@ref);
 
446
                                                                        for (j = i + 1; j < rev.Length; ++j)
 
447
                                                                        {
 
448
                                                                                if (!char.IsDigit(rev[j]))
 
449
                                                                                {
 
450
                                                                                        break;
 
451
                                                                                }
 
452
                                                                        }
 
453
                                                                        string parentnum = new string(rev, i + 1, j - i - 1);
 
454
                                                                        int pnum;
 
455
                                                                        try
 
456
                                                                        {
 
457
                                                                                pnum = System.Convert.ToInt32(parentnum);
 
458
                                                                        }
 
459
                                                                        catch (FormatException)
 
460
                                                                        {
 
461
                                                                                throw new RevisionSyntaxException(JGitText.Get().invalidCommitParentNumber, revstr
 
462
                                                                                        );
 
463
                                                                        }
 
464
                                                                        if (pnum != 0)
 
465
                                                                        {
 
466
                                                                                RevCommit commit = (RevCommit)@ref;
 
467
                                                                                if (pnum > commit.ParentCount)
 
468
                                                                                {
 
469
                                                                                        @ref = null;
 
470
                                                                                }
 
471
                                                                                else
 
472
                                                                                {
 
473
                                                                                        @ref = commit.GetParent(pnum - 1);
 
474
                                                                                }
 
475
                                                                        }
 
476
                                                                        i = j - 1;
 
477
                                                                        break;
 
478
                                                                }
 
479
 
 
480
                                                                case '{':
 
481
                                                                {
 
482
                                                                        int k;
 
483
                                                                        string item = null;
 
484
                                                                        for (k = i + 2; k < rev.Length; ++k)
 
485
                                                                        {
 
486
                                                                                if (rev[k] == '}')
 
487
                                                                                {
 
488
                                                                                        item = new string(rev, i + 2, k - i - 2);
 
489
                                                                                        break;
 
490
                                                                                }
 
491
                                                                        }
 
492
                                                                        i = k;
 
493
                                                                        if (item != null)
 
494
                                                                        {
 
495
                                                                                if (item.Equals("tree"))
 
496
                                                                                {
 
497
                                                                                        @ref = rw.ParseTree(@ref);
 
498
                                                                                }
 
499
                                                                                else
 
500
                                                                                {
 
501
                                                                                        if (item.Equals("commit"))
 
502
                                                                                        {
 
503
                                                                                                @ref = rw.ParseCommit(@ref);
 
504
                                                                                        }
 
505
                                                                                        else
 
506
                                                                                        {
 
507
                                                                                                if (item.Equals("blob"))
 
508
                                                                                                {
 
509
                                                                                                        @ref = rw.Peel(@ref);
 
510
                                                                                                        if (!(@ref is RevBlob))
 
511
                                                                                                        {
 
512
                                                                                                                throw new IncorrectObjectTypeException(@ref, Constants.TYPE_BLOB);
 
513
                                                                                                        }
 
514
                                                                                                }
 
515
                                                                                                else
 
516
                                                                                                {
 
517
                                                                                                        if (item.Equals(string.Empty))
 
518
                                                                                                        {
 
519
                                                                                                                @ref = rw.Peel(@ref);
 
520
                                                                                                        }
 
521
                                                                                                        else
 
522
                                                                                                        {
 
523
                                                                                                                throw new RevisionSyntaxException(revstr);
 
524
                                                                                                        }
 
525
                                                                                                }
 
526
                                                                                        }
 
527
                                                                                }
 
528
                                                                        }
 
529
                                                                        else
 
530
                                                                        {
 
531
                                                                                throw new RevisionSyntaxException(revstr);
 
532
                                                                        }
 
533
                                                                        break;
 
534
                                                                }
 
535
 
 
536
                                                                default:
 
537
                                                                {
 
538
                                                                        @ref = rw.ParseAny(@ref);
 
539
                                                                        if (@ref is RevCommit)
 
540
                                                                        {
 
541
                                                                                RevCommit commit = ((RevCommit)@ref);
 
542
                                                                                if (commit.ParentCount == 0)
 
543
                                                                                {
 
544
                                                                                        @ref = null;
 
545
                                                                                }
 
546
                                                                                else
 
547
                                                                                {
 
548
                                                                                        @ref = commit.GetParent(0);
 
549
                                                                                }
 
550
                                                                        }
 
551
                                                                        else
 
552
                                                                        {
 
553
                                                                                throw new IncorrectObjectTypeException(@ref, Constants.TYPE_COMMIT);
 
554
                                                                        }
 
555
                                                                        break;
 
556
                                                                }
 
557
                                                        }
 
558
                                                }
 
559
                                                else
 
560
                                                {
 
561
                                                        @ref = rw.Peel(@ref);
 
562
                                                        if (@ref is RevCommit)
 
563
                                                        {
 
564
                                                                RevCommit commit = ((RevCommit)@ref);
 
565
                                                                if (commit.ParentCount == 0)
 
566
                                                                {
 
567
                                                                        @ref = null;
 
568
                                                                }
 
569
                                                                else
 
570
                                                                {
 
571
                                                                        @ref = commit.GetParent(0);
 
572
                                                                }
 
573
                                                        }
 
574
                                                        else
 
575
                                                        {
 
576
                                                                throw new IncorrectObjectTypeException(@ref, Constants.TYPE_COMMIT);
 
577
                                                        }
 
578
                                                }
 
579
                                                break;
 
580
                                        }
 
581
 
 
582
                                        case '~':
 
583
                                        {
 
584
                                                if (@ref == null)
 
585
                                                {
 
586
                                                        @ref = ParseSimple(rw, new string(rev, 0, i));
 
587
                                                        if (@ref == null)
 
588
                                                        {
 
589
                                                                return null;
 
590
                                                        }
 
591
                                                }
 
592
                                                @ref = rw.Peel(@ref);
 
593
                                                if (!(@ref is RevCommit))
 
594
                                                {
 
595
                                                        throw new IncorrectObjectTypeException(@ref, Constants.TYPE_COMMIT);
 
596
                                                }
 
597
                                                int l;
 
598
                                                for (l = i + 1; l < rev.Length; ++l)
 
599
                                                {
 
600
                                                        if (!char.IsDigit(rev[l]))
 
601
                                                        {
 
602
                                                                break;
 
603
                                                        }
 
604
                                                }
 
605
                                                string distnum = new string(rev, i + 1, l - i - 1);
 
606
                                                int dist;
 
607
                                                try
 
608
                                                {
 
609
                                                        dist = System.Convert.ToInt32(distnum);
 
610
                                                }
 
611
                                                catch (FormatException)
 
612
                                                {
 
613
                                                        throw new RevisionSyntaxException(JGitText.Get().invalidAncestryLength, revstr);
 
614
                                                }
 
615
                                                while (dist > 0)
 
616
                                                {
 
617
                                                        RevCommit commit = (RevCommit)@ref;
 
618
                                                        if (commit.ParentCount == 0)
 
619
                                                        {
 
620
                                                                @ref = null;
 
621
                                                                break;
 
622
                                                        }
 
623
                                                        commit = commit.GetParent(0);
 
624
                                                        rw.ParseHeaders(commit);
 
625
                                                        @ref = commit;
 
626
                                                        --dist;
 
627
                                                }
 
628
                                                i = l - 1;
 
629
                                                break;
 
630
                                        }
 
631
 
 
632
                                        case '@':
 
633
                                        {
 
634
                                                int m;
 
635
                                                string time = null;
 
636
                                                for (m = i + 2; m < rev.Length; ++m)
 
637
                                                {
 
638
                                                        if (rev[m] == '}')
 
639
                                                        {
 
640
                                                                time = new string(rev, i + 2, m - i - 2);
 
641
                                                                break;
 
642
                                                        }
 
643
                                                }
 
644
                                                if (time != null)
 
645
                                                {
 
646
                                                        throw new RevisionSyntaxException(JGitText.Get().reflogsNotYetSupportedByRevisionParser
 
647
                                                                , revstr);
 
648
                                                }
 
649
                                                i = m - 1;
 
650
                                                break;
 
651
                                        }
 
652
 
 
653
                                        case ':':
 
654
                                        {
 
655
                                                RevTree tree;
 
656
                                                if (@ref == null)
 
657
                                                {
 
658
                                                        // We might not yet have parsed the left hand side.
 
659
                                                        ObjectId id;
 
660
                                                        try
 
661
                                                        {
 
662
                                                                if (i == 0)
 
663
                                                                {
 
664
                                                                        id = Resolve(rw, Constants.HEAD);
 
665
                                                                }
 
666
                                                                else
 
667
                                                                {
 
668
                                                                        id = Resolve(rw, new string(rev, 0, i));
 
669
                                                                }
 
670
                                                        }
 
671
                                                        catch (RevisionSyntaxException)
 
672
                                                        {
 
673
                                                                throw new RevisionSyntaxException(revstr);
 
674
                                                        }
 
675
                                                        if (id == null)
 
676
                                                        {
 
677
                                                                return null;
 
678
                                                        }
 
679
                                                        tree = rw.ParseTree(id);
 
680
                                                }
 
681
                                                else
 
682
                                                {
 
683
                                                        tree = rw.ParseTree(@ref);
 
684
                                                }
 
685
                                                if (i == rev.Length - i)
 
686
                                                {
 
687
                                                        return tree.Copy();
 
688
                                                }
 
689
                                                TreeWalk tw = TreeWalk.ForPath(rw.GetObjectReader(), new string(rev, i + 1, rev.Length
 
690
                                                         - i - 1), tree);
 
691
                                                return tw != null ? tw.GetObjectId(0) : null;
 
692
                                        }
 
693
 
 
694
                                        default:
 
695
                                        {
 
696
                                                if (@ref != null)
 
697
                                                {
 
698
                                                        throw new RevisionSyntaxException(revstr);
 
699
                                                }
 
700
                                                break;
 
701
                                        }
 
702
                                }
 
703
                        }
 
704
                        return @ref != null ? @ref.Copy() : ResolveSimple(revstr);
 
705
                }
 
706
 
 
707
                private static bool IsHex(char c)
 
708
                {
 
709
                        return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
 
710
                }
 
711
 
 
712
                //
 
713
                //
 
714
                private static bool IsAllHex(string str, int ptr)
 
715
                {
 
716
                        while (ptr < str.Length)
 
717
                        {
 
718
                                if (!IsHex(str[ptr++]))
 
719
                                {
 
720
                                        return false;
 
721
                                }
 
722
                        }
 
723
                        return true;
 
724
                }
 
725
 
 
726
                /// <exception cref="System.IO.IOException"></exception>
 
727
                private RevObject ParseSimple(RevWalk rw, string revstr)
 
728
                {
 
729
                        ObjectId id = ResolveSimple(revstr);
 
730
                        return id != null ? rw.ParseAny(id) : null;
 
731
                }
 
732
 
 
733
                /// <exception cref="System.IO.IOException"></exception>
 
734
                private ObjectId ResolveSimple(string revstr)
 
735
                {
 
736
                        if (ObjectId.IsId(revstr))
 
737
                        {
 
738
                                return ObjectId.FromString(revstr);
 
739
                        }
 
740
                        Ref r = RefDatabase.GetRef(revstr);
 
741
                        if (r != null)
 
742
                        {
 
743
                                return r.GetObjectId();
 
744
                        }
 
745
                        if (AbbreviatedObjectId.IsId(revstr))
 
746
                        {
 
747
                                return ResolveAbbreviation(revstr);
 
748
                        }
 
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))
 
752
                        {
 
753
                                // Possibly output from git describe?
 
754
                                string s = Sharpen.Runtime.Substring(revstr, dashg + 2);
 
755
                                if (AbbreviatedObjectId.IsId(s))
 
756
                                {
 
757
                                        return ResolveAbbreviation(s);
 
758
                                }
 
759
                        }
 
760
                        return null;
 
761
                }
 
762
 
 
763
                /// <exception cref="System.IO.IOException"></exception>
 
764
                /// <exception cref="NGit.Errors.AmbiguousObjectException"></exception>
 
765
                private ObjectId ResolveAbbreviation(string revstr)
 
766
                {
 
767
                        AbbreviatedObjectId id = AbbreviatedObjectId.FromString(revstr);
 
768
                        ObjectReader reader = NewObjectReader();
 
769
                        try
 
770
                        {
 
771
                                ICollection<ObjectId> matches = reader.Resolve(id);
 
772
                                if (matches.Count == 0)
 
773
                                {
 
774
                                        return null;
 
775
                                }
 
776
                                else
 
777
                                {
 
778
                                        if (matches.Count == 1)
 
779
                                        {
 
780
                                                return matches.Iterator().Next();
 
781
                                        }
 
782
                                        else
 
783
                                        {
 
784
                                                throw new AmbiguousObjectException(id, matches);
 
785
                                        }
 
786
                                }
 
787
                        }
 
788
                        finally
 
789
                        {
 
790
                                reader.Release();
 
791
                        }
 
792
                }
 
793
 
 
794
                /// <summary>
 
795
                /// Increment the use counter by one, requiring a matched
 
796
                /// <see cref="Close()">Close()</see>
 
797
                /// .
 
798
                /// </summary>
 
799
                public virtual void IncrementOpen()
 
800
                {
 
801
                        useCnt.IncrementAndGet();
 
802
                }
 
803
 
 
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()
 
807
                {
 
808
                        if (useCnt.DecrementAndGet() == 0)
 
809
                        {
 
810
                                DoClose();
 
811
                        }
 
812
                }
 
813
 
 
814
                /// <summary>
 
815
                /// Invoked when the use count drops to zero during
 
816
                /// <see cref="Close()">Close()</see>
 
817
                /// .
 
818
                /// <p>
 
819
                /// The default implementation closes the object and ref databases.
 
820
                /// </summary>
 
821
                protected internal virtual void DoClose()
 
822
                {
 
823
                        ObjectDatabase.Close();
 
824
                        RefDatabase.Close();
 
825
                }
 
826
 
 
827
                public override string ToString()
 
828
                {
 
829
                        string desc;
 
830
                        if (Directory != null)
 
831
                        {
 
832
                                desc = Directory.GetPath();
 
833
                        }
 
834
                        else
 
835
                        {
 
836
                                desc = GetType().Name + "-" + Runtime.IdentityHashCode(this);
 
837
                        }
 
838
                        return "Repository[" + desc + "]";
 
839
                }
 
840
 
 
841
                /// <summary>
 
842
                /// Get the name of the reference that
 
843
                /// <code>HEAD</code>
 
844
                /// points to.
 
845
                /// <p>
 
846
                /// This is essentially the same as doing:
 
847
                /// <pre>
 
848
                /// return getRef(Constants.HEAD).getTarget().getName()
 
849
                /// </pre>
 
850
                /// Except when HEAD is detached, in which case this method returns the
 
851
                /// current ObjectId in hexadecimal string format.
 
852
                /// </summary>
 
853
                /// <returns>
 
854
                /// name of current branch (for example
 
855
                /// <code>refs/heads/master</code>
 
856
                /// ) or
 
857
                /// an ObjectId in hex format if the current branch is detached.
 
858
                /// </returns>
 
859
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
860
                public virtual string GetFullBranch()
 
861
                {
 
862
                        Ref head = GetRef(Constants.HEAD);
 
863
                        if (head == null)
 
864
                        {
 
865
                                return null;
 
866
                        }
 
867
                        if (head.IsSymbolic())
 
868
                        {
 
869
                                return head.GetTarget().GetName();
 
870
                        }
 
871
                        if (head.GetObjectId() != null)
 
872
                        {
 
873
                                return head.GetObjectId().Name;
 
874
                        }
 
875
                        return null;
 
876
                }
 
877
 
 
878
                /// <summary>
 
879
                /// Get the short name of the current branch that
 
880
                /// <code>HEAD</code>
 
881
                /// points to.
 
882
                /// <p>
 
883
                /// This is essentially the same as
 
884
                /// <see cref="GetFullBranch()">GetFullBranch()</see>
 
885
                /// , except the
 
886
                /// leading prefix
 
887
                /// <code>refs/heads/</code>
 
888
                /// is removed from the reference before
 
889
                /// it is returned to the caller.
 
890
                /// </summary>
 
891
                /// <returns>
 
892
                /// name of current branch (for example
 
893
                /// <code>master</code>
 
894
                /// ), or an
 
895
                /// ObjectId in hex format if the current branch is detached.
 
896
                /// </returns>
 
897
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
898
                public virtual string GetBranch()
 
899
                {
 
900
                        string name = GetFullBranch();
 
901
                        if (name != null)
 
902
                        {
 
903
                                return ShortenRefName(name);
 
904
                        }
 
905
                        return name;
 
906
                }
 
907
 
 
908
                /// <summary>
 
909
                /// Objects known to exist but not expressed by
 
910
                /// <see cref="GetAllRefs()">GetAllRefs()</see>
 
911
                /// .
 
912
                /// <p>
 
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.
 
917
                /// </summary>
 
918
                /// <returns>unmodifiable collection of other known objects.</returns>
 
919
                public virtual ICollection<ObjectId> GetAdditionalHaves()
 
920
                {
 
921
                        return Sharpen.Collections.EmptySet<ObjectId>();
 
922
                }
 
923
 
 
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.
 
930
                /// </param>
 
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)
 
934
                {
 
935
                        return RefDatabase.GetRef(name);
 
936
                }
 
937
 
 
938
                /// <returns>mutable map of all known refs (heads, tags, remotes).</returns>
 
939
                public virtual IDictionary<string, Ref> GetAllRefs()
 
940
                {
 
941
                        try
 
942
                        {
 
943
                                return RefDatabase.GetRefs(NGit.RefDatabase.ALL);
 
944
                        }
 
945
                        catch (IOException)
 
946
                        {
 
947
                                return new Dictionary<string, Ref>();
 
948
                        }
 
949
                }
 
950
 
 
951
                /// <returns>
 
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").
 
955
                /// </returns>
 
956
                public virtual IDictionary<string, Ref> GetTags()
 
957
                {
 
958
                        try
 
959
                        {
 
960
                                return RefDatabase.GetRefs(Constants.R_TAGS);
 
961
                        }
 
962
                        catch (IOException)
 
963
                        {
 
964
                                return new Dictionary<string, Ref>();
 
965
                        }
 
966
                }
 
967
 
 
968
                /// <summary>Peel a possibly unpeeled reference to an annotated tag.</summary>
 
969
                /// <remarks>
 
970
                /// Peel a possibly unpeeled reference to an annotated tag.
 
971
                /// <p>
 
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>
 
975
                /// will be true.
 
976
                /// </remarks>
 
977
                /// <param name="ref">The ref to peel</param>
 
978
                /// <returns>
 
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
 
982
                /// (or null).
 
983
                /// </returns>
 
984
                public virtual Ref Peel(Ref @ref)
 
985
                {
 
986
                        try
 
987
                        {
 
988
                                return RefDatabase.Peel(@ref);
 
989
                        }
 
990
                        catch (IOException)
 
991
                        {
 
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.
 
995
                                return @ref;
 
996
                        }
 
997
                }
 
998
 
 
999
                /// <returns>a map with all objects referenced by a peeled ref.</returns>
 
1000
                public virtual IDictionary<AnyObjectId, ICollection<Ref>> GetAllRefsByPeeledObjectId
 
1001
                        ()
 
1002
                {
 
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)
 
1007
                        {
 
1008
                                Ref @ref = Peel(iref);
 
1009
                                AnyObjectId target = @ref.GetPeeledObjectId();
 
1010
                                if (target == null)
 
1011
                                {
 
1012
                                        target = @ref.GetObjectId();
 
1013
                                }
 
1014
                                // We assume most Sets here are singletons
 
1015
                                ICollection<Ref> oset = ret.Put(target, Sharpen.Collections.Singleton(@ref));
 
1016
                                if (oset != null)
 
1017
                                {
 
1018
                                        // that was not the case (rare)
 
1019
                                        if (oset.Count == 1)
 
1020
                                        {
 
1021
                                                // Was a read-only singleton, we must copy to a new Set
 
1022
                                                oset = new HashSet<Ref>(oset);
 
1023
                                        }
 
1024
                                        ret.Put(target, oset);
 
1025
                                        oset.AddItem(@ref);
 
1026
                                }
 
1027
                        }
 
1028
                        return ret;
 
1029
                }
 
1030
 
 
1031
                /// <returns>
 
1032
                /// a representation of the index associated with this
 
1033
                /// <see cref="Repository">Repository</see>
 
1034
                /// </returns>
 
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.
 
1038
                /// See
 
1039
                /// <see cref="IsBare()">IsBare()</see>
 
1040
                /// .
 
1041
                /// </exception>
 
1042
                public virtual GitIndex GetIndex()
 
1043
                {
 
1044
                        if (IsBare)
 
1045
                        {
 
1046
                                throw new NoWorkTreeException();
 
1047
                        }
 
1048
                        if (index == null)
 
1049
                        {
 
1050
                                index = new GitIndex(this);
 
1051
                                index.Read();
 
1052
                        }
 
1053
                        else
 
1054
                        {
 
1055
                                index.RereadIfNecessary();
 
1056
                        }
 
1057
                        return index;
 
1058
                }
 
1059
 
 
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.
 
1063
                /// See
 
1064
                /// <see cref="IsBare()">IsBare()</see>
 
1065
                /// .
 
1066
                /// </exception>
 
1067
                public virtual FilePath GetIndexFile()
 
1068
                {
 
1069
                        if (IsBare)
 
1070
                        {
 
1071
                                throw new NoWorkTreeException();
 
1072
                        }
 
1073
                        return indexFile;
 
1074
                }
 
1075
 
 
1076
                /// <summary>Create a new in-core index representation and read an index from disk.</summary>
 
1077
                /// <remarks>
 
1078
                /// Create a new in-core index representation and read an index from disk.
 
1079
                /// <p>
 
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.
 
1083
                /// </remarks>
 
1084
                /// <returns>
 
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.
 
1087
                /// </returns>
 
1088
                /// <exception cref="NGit.Errors.NoWorkTreeException">
 
1089
                /// if this is bare, which implies it has no working directory.
 
1090
                /// See
 
1091
                /// <see cref="IsBare()">IsBare()</see>
 
1092
                /// .
 
1093
                /// </exception>
 
1094
                /// <exception cref="System.IO.IOException">the index file is present but could not be read.
 
1095
                ///     </exception>
 
1096
                /// <exception cref="NGit.Errors.CorruptObjectException">
 
1097
                /// the index file is using a format or extension that this
 
1098
                /// library does not support.
 
1099
                /// </exception>
 
1100
                public virtual DirCache ReadDirCache()
 
1101
                {
 
1102
                        return DirCache.Read(GetIndexFile(), FileSystem);
 
1103
                }
 
1104
 
 
1105
                /// <summary>Create a new in-core index representation, lock it, and read from disk.</summary>
 
1106
                /// <remarks>
 
1107
                /// Create a new in-core index representation, lock it, and read from disk.
 
1108
                /// <p>
 
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.
 
1112
                /// </remarks>
 
1113
                /// <returns>
 
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.
 
1116
                /// </returns>
 
1117
                /// <exception cref="NGit.Errors.NoWorkTreeException">
 
1118
                /// if this is bare, which implies it has no working directory.
 
1119
                /// See
 
1120
                /// <see cref="IsBare()">IsBare()</see>
 
1121
                /// .
 
1122
                /// </exception>
 
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.
 
1126
                /// </exception>
 
1127
                /// <exception cref="NGit.Errors.CorruptObjectException">
 
1128
                /// the index file is using a format or extension that this
 
1129
                /// library does not support.
 
1130
                /// </exception>
 
1131
                public virtual DirCache LockDirCache()
 
1132
                {
 
1133
                        return DirCache.Lock(GetIndexFile(), FileSystem);
 
1134
                }
 
1135
 
 
1136
                internal static byte[] GitInternalSlash(byte[] bytes)
 
1137
                {
 
1138
                        if (FilePath.separatorChar == '/')
 
1139
                        {
 
1140
                                return bytes;
 
1141
                        }
 
1142
                        for (int i = 0; i < bytes.Length; ++i)
 
1143
                        {
 
1144
                                if (bytes[i] == FilePath.separatorChar)
 
1145
                                {
 
1146
                                        bytes[i] = (byte)('/');
 
1147
                                }
 
1148
                        }
 
1149
                        return bytes;
 
1150
                }
 
1151
 
 
1152
                /// <returns>an important state</returns>
 
1153
                public virtual RepositoryState GetRepositoryState()
 
1154
                {
 
1155
                        if (IsBare || Directory == null)
 
1156
                        {
 
1157
                                return RepositoryState.BARE;
 
1158
                        }
 
1159
                        // Pre Git-1.6 logic
 
1160
                        if (new FilePath(WorkTree, ".dotest").Exists())
 
1161
                        {
 
1162
                                return RepositoryState.REBASING;
 
1163
                        }
 
1164
                        if (new FilePath(Directory, ".dotest-merge").Exists())
 
1165
                        {
 
1166
                                return RepositoryState.REBASING_INTERACTIVE;
 
1167
                        }
 
1168
                        // From 1.6 onwards
 
1169
                        if (new FilePath(Directory, "rebase-apply/rebasing").Exists())
 
1170
                        {
 
1171
                                return RepositoryState.REBASING_REBASING;
 
1172
                        }
 
1173
                        if (new FilePath(Directory, "rebase-apply/applying").Exists())
 
1174
                        {
 
1175
                                return RepositoryState.APPLY;
 
1176
                        }
 
1177
                        if (new FilePath(Directory, "rebase-apply").Exists())
 
1178
                        {
 
1179
                                return RepositoryState.REBASING;
 
1180
                        }
 
1181
                        if (new FilePath(Directory, "rebase-merge/interactive").Exists())
 
1182
                        {
 
1183
                                return RepositoryState.REBASING_INTERACTIVE;
 
1184
                        }
 
1185
                        if (new FilePath(Directory, "rebase-merge").Exists())
 
1186
                        {
 
1187
                                return RepositoryState.REBASING_MERGE;
 
1188
                        }
 
1189
                        // Both versions
 
1190
                        if (new FilePath(Directory, "MERGE_HEAD").Exists())
 
1191
                        {
 
1192
                                // we are merging - now check whether we have unmerged paths
 
1193
                                try
 
1194
                                {
 
1195
                                        if (!ReadDirCache().HasUnmergedPaths())
 
1196
                                        {
 
1197
                                                // no unmerged paths -> return the MERGING_RESOLVED state
 
1198
                                                return RepositoryState.MERGING_RESOLVED;
 
1199
                                        }
 
1200
                                }
 
1201
                                catch (IOException e)
 
1202
                                {
 
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);
 
1207
                                }
 
1208
                                return RepositoryState.MERGING;
 
1209
                        }
 
1210
                        if (new FilePath(Directory, "BISECT_LOG").Exists())
 
1211
                        {
 
1212
                                return RepositoryState.BISECTING;
 
1213
                        }
 
1214
                        return RepositoryState.SAFE;
 
1215
                }
 
1216
 
 
1217
                /// <summary>Check validity of a ref name.</summary>
 
1218
                /// <remarks>
 
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
 
1223
                /// </remarks>
 
1224
                /// <param name="refName"></param>
 
1225
                /// <returns>true if refName is a valid ref name</returns>
 
1226
                public static bool IsValidRefName(string refName)
 
1227
                {
 
1228
                        int len = refName.Length;
 
1229
                        if (len == 0)
 
1230
                        {
 
1231
                                return false;
 
1232
                        }
 
1233
                        if (refName.EndsWith(".lock"))
 
1234
                        {
 
1235
                                return false;
 
1236
                        }
 
1237
                        int components = 1;
 
1238
                        char p = '\0';
 
1239
                        for (int i = 0; i < len; i++)
 
1240
                        {
 
1241
                                char c = refName[i];
 
1242
                                if (c <= ' ')
 
1243
                                {
 
1244
                                        return false;
 
1245
                                }
 
1246
                                switch (c)
 
1247
                                {
 
1248
                                        case '.':
 
1249
                                        {
 
1250
                                                switch (p)
 
1251
                                                {
 
1252
                                                        case '\0':
 
1253
                                                        case '/':
 
1254
                                                        case '.':
 
1255
                                                        {
 
1256
                                                                return false;
 
1257
                                                        }
 
1258
                                                }
 
1259
                                                if (i == len - 1)
 
1260
                                                {
 
1261
                                                        return false;
 
1262
                                                }
 
1263
                                                break;
 
1264
                                        }
 
1265
 
 
1266
                                        case '/':
 
1267
                                        {
 
1268
                                                if (i == 0 || i == len - 1)
 
1269
                                                {
 
1270
                                                        return false;
 
1271
                                                }
 
1272
                                                components++;
 
1273
                                                break;
 
1274
                                        }
 
1275
 
 
1276
                                        case '{':
 
1277
                                        {
 
1278
                                                if (p == '@')
 
1279
                                                {
 
1280
                                                        return false;
 
1281
                                                }
 
1282
                                                break;
 
1283
                                        }
 
1284
 
 
1285
                                        case '~':
 
1286
                                        case '^':
 
1287
                                        case ':':
 
1288
                                        case '?':
 
1289
                                        case '[':
 
1290
                                        case '*':
 
1291
                                        case '\\':
 
1292
                                        {
 
1293
                                                return false;
 
1294
                                        }
 
1295
                                }
 
1296
                                p = c;
 
1297
                        }
 
1298
                        return components > 1;
 
1299
                }
 
1300
 
 
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>
 
1305
                /// <returns>
 
1306
                /// normalized repository relative path or the empty
 
1307
                /// string if the file is not relative to the work directory.
 
1308
                /// </returns>
 
1309
                public static string StripWorkDir(FilePath workDir, FilePath file)
 
1310
                {
 
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))
 
1315
                        {
 
1316
                                FilePath absWd = workDir.IsAbsolute() ? workDir : workDir.GetAbsoluteFile();
 
1317
                                FilePath absFile = file.IsAbsolute() ? file : file.GetAbsoluteFile();
 
1318
                                if (absWd == workDir && absFile == file)
 
1319
                                {
 
1320
                                        return string.Empty;
 
1321
                                }
 
1322
                                return StripWorkDir(absWd, absFile);
 
1323
                        }
 
1324
                        string relName = Sharpen.Runtime.Substring(filePath, workDirPath.Length + 1);
 
1325
                        if (FilePath.separatorChar != '/')
 
1326
                        {
 
1327
                                relName = relName.Replace(FilePath.separatorChar, '/');
 
1328
                        }
 
1329
                        return relName;
 
1330
                }
 
1331
 
 
1332
                /// <returns>true if this is bare, which implies it has no working directory.</returns>
 
1333
                public virtual bool IsBare
 
1334
                {
 
1335
                        get
 
1336
                        {
 
1337
                                return workTree == null;
 
1338
                        }
 
1339
                }
 
1340
 
 
1341
                /// <returns>
 
1342
                /// the root directory of the working tree, where files are checked
 
1343
                /// out for viewing and editing.
 
1344
                /// </returns>
 
1345
                /// <exception cref="NGit.Errors.NoWorkTreeException">
 
1346
                /// if this is bare, which implies it has no working directory.
 
1347
                /// See
 
1348
                /// <see cref="IsBare()">IsBare()</see>
 
1349
                /// .
 
1350
                /// </exception>
 
1351
                public virtual FilePath WorkTree
 
1352
                {
 
1353
                        get
 
1354
                        {
 
1355
                                if (IsBare)
 
1356
                                {
 
1357
                                        throw new NoWorkTreeException();
 
1358
                                }
 
1359
                                return workTree;
 
1360
                        }
 
1361
                }
 
1362
 
 
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();
 
1367
 
 
1368
                /// <param name="refName"></param>
 
1369
                /// <returns>a more user friendly ref name</returns>
 
1370
                public static string ShortenRefName(string refName)
 
1371
                {
 
1372
                        if (refName.StartsWith(Constants.R_HEADS))
 
1373
                        {
 
1374
                                return Sharpen.Runtime.Substring(refName, Constants.R_HEADS.Length);
 
1375
                        }
 
1376
                        if (refName.StartsWith(Constants.R_TAGS))
 
1377
                        {
 
1378
                                return Sharpen.Runtime.Substring(refName, Constants.R_TAGS.Length);
 
1379
                        }
 
1380
                        if (refName.StartsWith(Constants.R_REMOTES))
 
1381
                        {
 
1382
                                return Sharpen.Runtime.Substring(refName, Constants.R_REMOTES.Length);
 
1383
                        }
 
1384
                        return refName;
 
1385
                }
 
1386
 
 
1387
                /// <param name="refName"></param>
 
1388
                /// <returns>
 
1389
                /// a
 
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.
 
1393
                /// </returns>
 
1394
                /// <exception cref="System.IO.IOException">the ref could not be accessed.</exception>
 
1395
                public abstract ReflogReader GetReflogReader(string refName);
 
1396
 
 
1397
                /// <summary>Return the information stored in the file $GIT_DIR/MERGE_MSG.</summary>
 
1398
                /// <remarks>
 
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.
 
1402
                /// </remarks>
 
1403
                /// <returns>
 
1404
                /// a String containing the content of the MERGE_MSG file or
 
1405
                /// <code>null</code>
 
1406
                /// if this file doesn't exist
 
1407
                /// </returns>
 
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.
 
1411
                /// See
 
1412
                /// <see cref="IsBare()">IsBare()</see>
 
1413
                /// .
 
1414
                /// </exception>
 
1415
                public virtual string ReadMergeCommitMsg()
 
1416
                {
 
1417
                        if (IsBare || Directory == null)
 
1418
                        {
 
1419
                                throw new NoWorkTreeException();
 
1420
                        }
 
1421
                        FilePath mergeMsgFile = new FilePath(Directory, Constants.MERGE_MSG);
 
1422
                        try
 
1423
                        {
 
1424
                                return RawParseUtils.Decode(IOUtil.ReadFully(mergeMsgFile));
 
1425
                        }
 
1426
                        catch (FileNotFoundException)
 
1427
                        {
 
1428
                                // MERGE_MSG file has disappeared in the meantime
 
1429
                                // ignore it
 
1430
                                return null;
 
1431
                        }
 
1432
                }
 
1433
 
 
1434
                /// <summary>Write new content to the file $GIT_DIR/MERGE_MSG.</summary>
 
1435
                /// <remarks>
 
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
 
1439
                /// be deleted
 
1440
                /// </remarks>
 
1441
                /// <param name="msg">
 
1442
                /// the message which should be written or <code>null</code> to
 
1443
                /// delete the file
 
1444
                /// </param>
 
1445
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
1446
                public virtual void WriteMergeCommitMsg(string msg)
 
1447
                {
 
1448
                        FilePath mergeMsgFile = new FilePath(gitDir, Constants.MERGE_MSG);
 
1449
                        if (msg != null)
 
1450
                        {
 
1451
                                FileOutputStream fos = new FileOutputStream(mergeMsgFile);
 
1452
                                try
 
1453
                                {
 
1454
                                        fos.Write(Sharpen.Runtime.GetBytesForString(msg, Constants.CHARACTER_ENCODING));
 
1455
                                }
 
1456
                                finally
 
1457
                                {
 
1458
                                        fos.Close();
 
1459
                                }
 
1460
                        }
 
1461
                        else
 
1462
                        {
 
1463
                                FileUtils.Delete(mergeMsgFile);
 
1464
                        }
 
1465
                }
 
1466
 
 
1467
                /// <summary>Return the information stored in the file $GIT_DIR/MERGE_HEAD.</summary>
 
1468
                /// <remarks>
 
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.
 
1472
                /// </remarks>
 
1473
                /// <returns>
 
1474
                /// a list of commits which IDs are listed in the MERGE_HEAD
 
1475
                /// file or
 
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
 
1481
                /// </returns>
 
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.
 
1485
                /// See
 
1486
                /// <see cref="IsBare()">IsBare()</see>
 
1487
                /// .
 
1488
                /// </exception>
 
1489
                public virtual IList<ObjectId> ReadMergeHeads()
 
1490
                {
 
1491
                        if (IsBare || Directory == null)
 
1492
                        {
 
1493
                                throw new NoWorkTreeException();
 
1494
                        }
 
1495
                        FilePath mergeHeadFile = new FilePath(Directory, Constants.MERGE_HEAD);
 
1496
                        byte[] raw;
 
1497
                        try
 
1498
                        {
 
1499
                                raw = IOUtil.ReadFully(mergeHeadFile);
 
1500
                        }
 
1501
                        catch (FileNotFoundException)
 
1502
                        {
 
1503
                                return null;
 
1504
                        }
 
1505
                        if (raw.Length == 0)
 
1506
                        {
 
1507
                                return null;
 
1508
                        }
 
1509
                        List<ObjectId> heads = new List<ObjectId>();
 
1510
                        for (int p = 0; p < raw.Length; )
 
1511
                        {
 
1512
                                heads.AddItem(ObjectId.FromString(raw, p));
 
1513
                                p = RawParseUtils.NextLF(raw, p + Constants.OBJECT_ID_STRING_LENGTH);
 
1514
                        }
 
1515
                        return heads;
 
1516
                }
 
1517
 
 
1518
                /// <summary>Write new merge-heads into $GIT_DIR/MERGE_HEAD.</summary>
 
1519
                /// <remarks>
 
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
 
1524
                /// </remarks>
 
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
 
1528
                /// </param>
 
1529
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
1530
                public virtual void WriteMergeHeads(IList<ObjectId> heads)
 
1531
                {
 
1532
                        FilePath mergeHeadFile = new FilePath(gitDir, Constants.MERGE_HEAD);
 
1533
                        if (heads != null)
 
1534
                        {
 
1535
                                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(mergeHeadFile
 
1536
                                        ));
 
1537
                                try
 
1538
                                {
 
1539
                                        foreach (ObjectId id in heads)
 
1540
                                        {
 
1541
                                                id.CopyTo(bos);
 
1542
                                                bos.Write('\n');
 
1543
                                        }
 
1544
                                }
 
1545
                                finally
 
1546
                                {
 
1547
                                        bos.Close();
 
1548
                                }
 
1549
                        }
 
1550
                        else
 
1551
                        {
 
1552
                                FileUtils.Delete(mergeHeadFile);
 
1553
                        }
 
1554
                }
 
1555
        }
 
1556
}