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

« back to all changes in this revision

Viewing changes to contrib/NGit/NGit/GitIndex.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;
 
46
using System.Collections.Generic;
 
47
using System.IO;
 
48
using NGit;
 
49
using NGit.Errors;
 
50
using NGit.Events;
 
51
using NGit.Util;
 
52
using Sharpen;
 
53
 
 
54
namespace NGit
 
55
{
 
56
        /// <summary>A representation of the Git index.</summary>
 
57
        /// <remarks>
 
58
        /// A representation of the Git index.
 
59
        /// The index points to the objects currently checked out or in the process of
 
60
        /// being prepared for committing or objects involved in an unfinished merge.
 
61
        /// The abstract format is:<br/> path stage flags statdata SHA-1
 
62
        /// <ul>
 
63
        /// <li>Path is the relative path in the workdir</li>
 
64
        /// <li>stage is 0 (normally), but when
 
65
        /// merging 1 is the common ancestor version, 2 is 'our' version and 3 is 'their'
 
66
        /// version. A fully resolved merge only contains stage 0.</li>
 
67
        /// <li>flags is the object type and information of validity</li>
 
68
        /// <li>statdata is the size of this object and some other file system specifics,
 
69
        /// some of it ignored by JGit</li>
 
70
        /// <li>SHA-1 represents the content of the references object</li>
 
71
        /// </ul>
 
72
        /// An index can also contain a tree cache which we ignore for now. We drop the
 
73
        /// tree cache when writing the index.
 
74
        /// </remarks>
 
75
        [System.ObsoleteAttribute(@"Use NGit.Dircache.DirCache instead.")]
 
76
        public class GitIndex
 
77
        {
 
78
                /// <summary>Stage 0 represents merged entries.</summary>
 
79
                /// <remarks>Stage 0 represents merged entries.</remarks>
 
80
                public const int STAGE_0 = 0;
 
81
 
 
82
                private RandomAccessFile cache;
 
83
 
 
84
                private FilePath cacheFile;
 
85
 
 
86
                private bool changed;
 
87
 
 
88
                private bool statDirty;
 
89
 
 
90
                private GitIndex.Header header;
 
91
 
 
92
                private long lastCacheTime;
 
93
 
 
94
                private readonly Repository db;
 
95
 
 
96
                private sealed class _IComparer_122 : IComparer<byte[]>
 
97
                {
 
98
                        public _IComparer_122()
 
99
                        {
 
100
                        }
 
101
 
 
102
                        // Index is modified
 
103
                        // Stat information updated
 
104
                        public int Compare(byte[] o1, byte[] o2)
 
105
                        {
 
106
                                for (int i = 0; i < o1.Length && i < o2.Length; ++i)
 
107
                                {
 
108
                                        int c = (o1[i] & unchecked((int)(0xff))) - (o2[i] & unchecked((int)(0xff)));
 
109
                                        if (c != 0)
 
110
                                        {
 
111
                                                return c;
 
112
                                        }
 
113
                                }
 
114
                                if (o1.Length < o2.Length)
 
115
                                {
 
116
                                        return -1;
 
117
                                }
 
118
                                else
 
119
                                {
 
120
                                        if (o1.Length > o2.Length)
 
121
                                        {
 
122
                                                return 1;
 
123
                                        }
 
124
                                }
 
125
                                return 0;
 
126
                        }
 
127
                }
 
128
 
 
129
                private IDictionary<byte[], GitIndex.Entry> entries = new SortedDictionary<byte[]
 
130
                        , GitIndex.Entry>(new _IComparer_122());
 
131
 
 
132
                /// <summary>Construct a Git index representation.</summary>
 
133
                /// <remarks>Construct a Git index representation.</remarks>
 
134
                /// <param name="db"></param>
 
135
                public GitIndex(Repository db)
 
136
                {
 
137
                        this.db = db;
 
138
                        this.cacheFile = db.GetIndexFile();
 
139
                }
 
140
 
 
141
                /// <returns>true if we have modified the index in memory since reading it from disk</returns>
 
142
                public virtual bool IsChanged()
 
143
                {
 
144
                        return changed || statDirty;
 
145
                }
 
146
 
 
147
                /// <summary>Reread index data from disk if the index file has been changed</summary>
 
148
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
149
                public virtual void RereadIfNecessary()
 
150
                {
 
151
                        if (cacheFile.Exists() && cacheFile.LastModified() != lastCacheTime)
 
152
                        {
 
153
                                Read();
 
154
                                db.FireEvent(new IndexChangedEvent());
 
155
                        }
 
156
                }
 
157
 
 
158
                /// <summary>Add the content of a file to the index.</summary>
 
159
                /// <remarks>Add the content of a file to the index.</remarks>
 
160
                /// <param name="wd">workdir</param>
 
161
                /// <param name="f">the file</param>
 
162
                /// <returns>a new or updated index entry for the path represented by f</returns>
 
163
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
164
                public virtual GitIndex.Entry Add(FilePath wd, FilePath f)
 
165
                {
 
166
                        byte[] key = MakeKey(wd, f);
 
167
                        GitIndex.Entry e = entries.Get(key);
 
168
                        if (e == null)
 
169
                        {
 
170
                                e = new GitIndex.Entry(this, key, f, 0);
 
171
                                entries.Put(key, e);
 
172
                        }
 
173
                        else
 
174
                        {
 
175
                                e.Update(f);
 
176
                        }
 
177
                        return e;
 
178
                }
 
179
 
 
180
                /// <summary>Add the content of a file to the index.</summary>
 
181
                /// <remarks>Add the content of a file to the index.</remarks>
 
182
                /// <param name="wd">workdir</param>
 
183
                /// <param name="f">the file</param>
 
184
                /// <param name="content">content of the file</param>
 
185
                /// <returns>a new or updated index entry for the path represented by f</returns>
 
186
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
187
                public virtual GitIndex.Entry Add(FilePath wd, FilePath f, byte[] content)
 
188
                {
 
189
                        byte[] key = MakeKey(wd, f);
 
190
                        GitIndex.Entry e = entries.Get(key);
 
191
                        if (e == null)
 
192
                        {
 
193
                                e = new GitIndex.Entry(this, key, f, 0, content);
 
194
                                entries.Put(key, e);
 
195
                        }
 
196
                        else
 
197
                        {
 
198
                                e.Update(f, content);
 
199
                        }
 
200
                        return e;
 
201
                }
 
202
 
 
203
                /// <summary>Remove a path from the index.</summary>
 
204
                /// <remarks>Remove a path from the index.</remarks>
 
205
                /// <param name="wd">workdir</param>
 
206
                /// <param name="f">the file whose path shall be removed.</param>
 
207
                /// <returns>true if such a path was found (and thus removed)</returns>
 
208
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
209
                public virtual bool Remove(FilePath wd, FilePath f)
 
210
                {
 
211
                        byte[] key = MakeKey(wd, f);
 
212
                        return Sharpen.Collections.Remove(entries, key) != null;
 
213
                }
 
214
 
 
215
                /// <summary>Read the cache file into memory.</summary>
 
216
                /// <remarks>Read the cache file into memory.</remarks>
 
217
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
218
                public virtual void Read()
 
219
                {
 
220
                        changed = false;
 
221
                        statDirty = false;
 
222
                        if (!cacheFile.Exists())
 
223
                        {
 
224
                                header = null;
 
225
                                entries.Clear();
 
226
                                lastCacheTime = 0;
 
227
                                return;
 
228
                        }
 
229
                        cache = new RandomAccessFile(cacheFile, "r");
 
230
                        try
 
231
                        {
 
232
                                FileChannel channel = cache.GetChannel();
 
233
                                ByteBuffer buffer = ByteBuffer.AllocateDirect((int)cacheFile.Length());
 
234
                                buffer.Order(ByteOrder.BIG_ENDIAN);
 
235
                                int j = channel.Read(buffer);
 
236
                                if (j != buffer.Capacity())
 
237
                                {
 
238
                                        throw new IOException(MessageFormat.Format(JGitText.Get().couldNotReadIndexInOneGo
 
239
                                                , j, buffer.Capacity()));
 
240
                                }
 
241
                                buffer.Flip();
 
242
                                header = new GitIndex.Header(buffer);
 
243
                                entries.Clear();
 
244
                                for (int i = 0; i < header.entries; ++i)
 
245
                                {
 
246
                                        GitIndex.Entry entry = new GitIndex.Entry(this, buffer);
 
247
                                        GitIndex.Entry existing = entries.Get(entry.name);
 
248
                                        entries.Put(entry.name, entry);
 
249
                                        if (existing != null)
 
250
                                        {
 
251
                                                entry.stages |= existing.stages;
 
252
                                        }
 
253
                                }
 
254
                                lastCacheTime = cacheFile.LastModified();
 
255
                        }
 
256
                        finally
 
257
                        {
 
258
                                cache.Close();
 
259
                        }
 
260
                }
 
261
 
 
262
                /// <summary>Write content of index to disk.</summary>
 
263
                /// <remarks>Write content of index to disk.</remarks>
 
264
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
265
                public virtual void Write()
 
266
                {
 
267
                        CheckWriteOk();
 
268
                        FilePath tmpIndex = new FilePath(cacheFile.GetAbsoluteFile() + ".tmp");
 
269
                        FilePath Lock = new FilePath(cacheFile.GetAbsoluteFile() + ".lock");
 
270
                        if (!Lock.CreateNewFile())
 
271
                        {
 
272
                                throw new IOException(JGitText.Get().indexFileIsInUse);
 
273
                        }
 
274
                        try
 
275
                        {
 
276
                                FileOutputStream fileOutputStream = new FileOutputStream(tmpIndex);
 
277
                                FileChannel fc = fileOutputStream.GetChannel();
 
278
                                ByteBuffer buf = ByteBuffer.Allocate(4096);
 
279
                                MessageDigest newMessageDigest = Constants.NewMessageDigest();
 
280
                                header = new GitIndex.Header(entries);
 
281
                                header.Write(buf);
 
282
                                buf.Flip();
 
283
                                newMessageDigest.Update(((byte[])buf.Array()), buf.ArrayOffset(), buf.Limit());
 
284
                                fc.Write(buf);
 
285
                                buf.Flip();
 
286
                                buf.Clear();
 
287
                                for (Iterator i = entries.Values.Iterator(); i.HasNext(); )
 
288
                                {
 
289
                                        GitIndex.Entry e = (GitIndex.Entry)i.Next();
 
290
                                        e.Write(buf);
 
291
                                        buf.Flip();
 
292
                                        newMessageDigest.Update(((byte[])buf.Array()), buf.ArrayOffset(), buf.Limit());
 
293
                                        fc.Write(buf);
 
294
                                        buf.Flip();
 
295
                                        buf.Clear();
 
296
                                }
 
297
                                buf.Put(newMessageDigest.Digest());
 
298
                                buf.Flip();
 
299
                                fc.Write(buf);
 
300
                                fc.Close();
 
301
                                fileOutputStream.Close();
 
302
                                if (cacheFile.Exists())
 
303
                                {
 
304
                                        if (db.FileSystem.RetryFailedLockFileCommit())
 
305
                                        {
 
306
                                                // file deletion fails on windows if another
 
307
                                                // thread is reading the file concurrently
 
308
                                                // So let's try 10 times...
 
309
                                                bool deleted = false;
 
310
                                                for (int i_1 = 0; i_1 < 10; i_1++)
 
311
                                                {
 
312
                                                        if (cacheFile.Delete())
 
313
                                                        {
 
314
                                                                deleted = true;
 
315
                                                                break;
 
316
                                                        }
 
317
                                                        try
 
318
                                                        {
 
319
                                                                Sharpen.Thread.Sleep(100);
 
320
                                                        }
 
321
                                                        catch (Exception)
 
322
                                                        {
 
323
                                                        }
 
324
                                                }
 
325
                                                // ignore
 
326
                                                if (!deleted)
 
327
                                                {
 
328
                                                        throw new IOException(JGitText.Get().couldNotRenameDeleteOldIndex);
 
329
                                                }
 
330
                                        }
 
331
                                        else
 
332
                                        {
 
333
                                                if (!cacheFile.Delete())
 
334
                                                {
 
335
                                                        throw new IOException(JGitText.Get().couldNotRenameDeleteOldIndex);
 
336
                                                }
 
337
                                        }
 
338
                                }
 
339
                                if (!tmpIndex.RenameTo(cacheFile))
 
340
                                {
 
341
                                        throw new IOException(JGitText.Get().couldNotRenameTemporaryIndexFileToIndex);
 
342
                                }
 
343
                                changed = false;
 
344
                                statDirty = false;
 
345
                                lastCacheTime = cacheFile.LastModified();
 
346
                                db.FireEvent(new IndexChangedEvent());
 
347
                        }
 
348
                        finally
 
349
                        {
 
350
                                if (!Lock.Delete())
 
351
                                {
 
352
                                        throw new IOException(JGitText.Get().couldNotDeleteLockFileShouldNotHappen);
 
353
                                }
 
354
                                if (tmpIndex.Exists() && !tmpIndex.Delete())
 
355
                                {
 
356
                                        throw new IOException(JGitText.Get().couldNotDeleteTemporaryIndexFileShouldNotHappen
 
357
                                                );
 
358
                                }
 
359
                        }
 
360
                }
 
361
 
 
362
                /// <exception cref="System.IO.IOException"></exception>
 
363
                private void CheckWriteOk()
 
364
                {
 
365
                        for (Iterator i = entries.Values.Iterator(); i.HasNext(); )
 
366
                        {
 
367
                                GitIndex.Entry e = (GitIndex.Entry)i.Next();
 
368
                                if (e.GetStage() != 0)
 
369
                                {
 
370
                                        throw new NotSupportedException(JGitText.Get().cannotWorkWithOtherStagesThanZeroRightNow
 
371
                                                );
 
372
                                }
 
373
                        }
 
374
                }
 
375
 
 
376
                private bool File_canExecute(FilePath f)
 
377
                {
 
378
                        return db.FileSystem.CanExecute(f);
 
379
                }
 
380
 
 
381
                private bool File_setExecute(FilePath f, bool value)
 
382
                {
 
383
                        return db.FileSystem.SetExecute(f, value);
 
384
                }
 
385
 
 
386
                private bool File_hasExecute()
 
387
                {
 
388
                        return db.FileSystem.SupportsExecute();
 
389
                }
 
390
 
 
391
                internal static byte[] MakeKey(FilePath wd, FilePath f)
 
392
                {
 
393
                        if (!f.GetPath().StartsWith(wd.GetPath()))
 
394
                        {
 
395
                                throw new Error(JGitText.Get().pathIsNotInWorkingDir);
 
396
                        }
 
397
                        string relName = Repository.StripWorkDir(wd, f);
 
398
                        return Constants.Encode(relName);
 
399
                }
 
400
 
 
401
                internal bool filemode;
 
402
 
 
403
                private bool Config_filemode()
 
404
                {
 
405
                        // temporary til we can actually set parameters. We need to be able
 
406
                        // to change this for testing.
 
407
                        if (filemode != null)
 
408
                        {
 
409
                                return filemode;
 
410
                        }
 
411
                        Config config = db.GetConfig();
 
412
                        filemode = Sharpen.Extensions.ValueOf(config.GetBoolean("core", null, "filemode", 
 
413
                                true));
 
414
                        return filemode;
 
415
                }
 
416
 
 
417
                /// <summary>An index entry</summary>
 
418
                [System.ObsoleteAttribute(@"Use NGit.Dircache.DirCacheEntry .")]
 
419
                public class Entry
 
420
                {
 
421
                        internal long ctime;
 
422
 
 
423
                        internal long mtime;
 
424
 
 
425
                        private int dev;
 
426
 
 
427
                        private int ino;
 
428
 
 
429
                        internal int mode;
 
430
 
 
431
                        private int uid;
 
432
 
 
433
                        private int gid;
 
434
 
 
435
                        private int size;
 
436
 
 
437
                        internal ObjectId sha1;
 
438
 
 
439
                        private short flags;
 
440
 
 
441
                        internal byte[] name;
 
442
 
 
443
                        internal int stages;
 
444
 
 
445
                        /// <exception cref="System.IO.IOException"></exception>
 
446
                        internal Entry(GitIndex _enclosing, byte[] key, FilePath f, int stage)
 
447
                        {
 
448
                                this._enclosing = _enclosing;
 
449
                                this.ctime = f.LastModified() * 1000000L;
 
450
                                this.mtime = this.ctime;
 
451
                                // we use same here
 
452
                                this.dev = -1;
 
453
                                this.ino = -1;
 
454
                                if (this._enclosing.Config_filemode() && this._enclosing.File_canExecute(f))
 
455
                                {
 
456
                                        this.mode = FileMode.EXECUTABLE_FILE.GetBits();
 
457
                                }
 
458
                                else
 
459
                                {
 
460
                                        this.mode = FileMode.REGULAR_FILE.GetBits();
 
461
                                }
 
462
                                this.uid = -1;
 
463
                                this.gid = -1;
 
464
                                this.size = (int)f.Length();
 
465
                                ObjectInserter inserter = this._enclosing.db.NewObjectInserter();
 
466
                                try
 
467
                                {
 
468
                                        InputStream @in = new FileInputStream(f);
 
469
                                        try
 
470
                                        {
 
471
                                                this.sha1 = inserter.Insert(Constants.OBJ_BLOB, f.Length(), @in);
 
472
                                        }
 
473
                                        finally
 
474
                                        {
 
475
                                                @in.Close();
 
476
                                        }
 
477
                                        inserter.Flush();
 
478
                                }
 
479
                                finally
 
480
                                {
 
481
                                        inserter.Release();
 
482
                                }
 
483
                                this.name = key;
 
484
                                this.flags = (short)((stage << 12) | this.name.Length);
 
485
                                // TODO: fix flags
 
486
                                this.stages = (1 >> this.GetStage());
 
487
                        }
 
488
 
 
489
                        /// <exception cref="System.IO.IOException"></exception>
 
490
                        internal Entry(GitIndex _enclosing, byte[] key, FilePath f, int stage, byte[] newContent
 
491
                                )
 
492
                        {
 
493
                                this._enclosing = _enclosing;
 
494
                                this.ctime = f.LastModified() * 1000000L;
 
495
                                this.mtime = this.ctime;
 
496
                                // we use same here
 
497
                                this.dev = -1;
 
498
                                this.ino = -1;
 
499
                                if (this._enclosing.Config_filemode() && this._enclosing.File_canExecute(f))
 
500
                                {
 
501
                                        this.mode = FileMode.EXECUTABLE_FILE.GetBits();
 
502
                                }
 
503
                                else
 
504
                                {
 
505
                                        this.mode = FileMode.REGULAR_FILE.GetBits();
 
506
                                }
 
507
                                this.uid = -1;
 
508
                                this.gid = -1;
 
509
                                this.size = newContent.Length;
 
510
                                ObjectInserter inserter = this._enclosing.db.NewObjectInserter();
 
511
                                try
 
512
                                {
 
513
                                        InputStream @in = new FileInputStream(f);
 
514
                                        try
 
515
                                        {
 
516
                                                this.sha1 = inserter.Insert(Constants.OBJ_BLOB, newContent);
 
517
                                        }
 
518
                                        finally
 
519
                                        {
 
520
                                                @in.Close();
 
521
                                        }
 
522
                                        inserter.Flush();
 
523
                                }
 
524
                                finally
 
525
                                {
 
526
                                        inserter.Release();
 
527
                                }
 
528
                                this.name = key;
 
529
                                this.flags = (short)((stage << 12) | this.name.Length);
 
530
                                // TODO: fix flags
 
531
                                this.stages = (1 >> this.GetStage());
 
532
                        }
 
533
 
 
534
                        internal Entry(GitIndex _enclosing, TreeEntry f, int stage)
 
535
                        {
 
536
                                this._enclosing = _enclosing;
 
537
                                this.ctime = -1;
 
538
                                // hmm
 
539
                                this.mtime = -1;
 
540
                                this.dev = -1;
 
541
                                this.ino = -1;
 
542
                                this.mode = f.GetMode().GetBits();
 
543
                                this.uid = -1;
 
544
                                this.gid = -1;
 
545
                                try
 
546
                                {
 
547
                                        this.size = (int)this._enclosing.db.Open(f.GetId(), Constants.OBJ_BLOB).GetSize();
 
548
                                }
 
549
                                catch (IOException e)
 
550
                                {
 
551
                                        Sharpen.Runtime.PrintStackTrace(e);
 
552
                                        this.size = -1;
 
553
                                }
 
554
                                this.sha1 = f.GetId();
 
555
                                this.name = Constants.Encode(f.GetFullName());
 
556
                                this.flags = (short)((stage << 12) | this.name.Length);
 
557
                                // TODO: fix flags
 
558
                                this.stages = (1 >> this.GetStage());
 
559
                        }
 
560
 
 
561
                        internal Entry(GitIndex _enclosing, ByteBuffer b)
 
562
                        {
 
563
                                this._enclosing = _enclosing;
 
564
                                int startposition = b.Position();
 
565
                                this.ctime = b.GetInt() * 1000000000L + (b.GetInt() % 1000000000L);
 
566
                                this.mtime = b.GetInt() * 1000000000L + (b.GetInt() % 1000000000L);
 
567
                                this.dev = b.GetInt();
 
568
                                this.ino = b.GetInt();
 
569
                                this.mode = b.GetInt();
 
570
                                this.uid = b.GetInt();
 
571
                                this.gid = b.GetInt();
 
572
                                this.size = b.GetInt();
 
573
                                byte[] sha1bytes = new byte[Constants.OBJECT_ID_LENGTH];
 
574
                                b.Get(sha1bytes);
 
575
                                this.sha1 = ObjectId.FromRaw(sha1bytes);
 
576
                                this.flags = b.GetShort();
 
577
                                this.stages = (1 << this.GetStage());
 
578
                                this.name = new byte[this.flags & unchecked((int)(0xFFF))];
 
579
                                b.Get(this.name);
 
580
                                b.Position(startposition + ((8 + 8 + 4 + 4 + 4 + 4 + 4 + 4 + 20 + 2 + this.name.Length
 
581
                                         + 8) & ~7));
 
582
                        }
 
583
 
 
584
                        /// <summary>
 
585
                        /// Update this index entry with stat and SHA-1 information if it looks
 
586
                        /// like the file has been modified in the workdir.
 
587
                        /// </summary>
 
588
                        /// <remarks>
 
589
                        /// Update this index entry with stat and SHA-1 information if it looks
 
590
                        /// like the file has been modified in the workdir.
 
591
                        /// </remarks>
 
592
                        /// <param name="f">file in work dir</param>
 
593
                        /// <returns>true if a change occurred</returns>
 
594
                        /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
595
                        public virtual bool Update(FilePath f)
 
596
                        {
 
597
                                long lm = f.LastModified() * 1000000L;
 
598
                                bool modified = this.mtime != lm;
 
599
                                this.mtime = lm;
 
600
                                if (this.size != f.Length())
 
601
                                {
 
602
                                        modified = true;
 
603
                                }
 
604
                                if (this._enclosing.Config_filemode())
 
605
                                {
 
606
                                        if (this._enclosing.File_canExecute(f) != FileMode.EXECUTABLE_FILE.Equals(this.mode
 
607
                                                ))
 
608
                                        {
 
609
                                                this.mode = FileMode.EXECUTABLE_FILE.GetBits();
 
610
                                                modified = true;
 
611
                                        }
 
612
                                }
 
613
                                if (modified)
 
614
                                {
 
615
                                        this.size = (int)f.Length();
 
616
                                        ObjectInserter oi = this._enclosing.db.NewObjectInserter();
 
617
                                        try
 
618
                                        {
 
619
                                                InputStream @in = new FileInputStream(f);
 
620
                                                try
 
621
                                                {
 
622
                                                        ObjectId newsha1 = oi.Insert(Constants.OBJ_BLOB, f.Length(), @in);
 
623
                                                        oi.Flush();
 
624
                                                        if (!newsha1.Equals(this.sha1))
 
625
                                                        {
 
626
                                                                modified = true;
 
627
                                                        }
 
628
                                                        this.sha1 = newsha1;
 
629
                                                }
 
630
                                                finally
 
631
                                                {
 
632
                                                        @in.Close();
 
633
                                                }
 
634
                                        }
 
635
                                        finally
 
636
                                        {
 
637
                                                oi.Release();
 
638
                                        }
 
639
                                }
 
640
                                return modified;
 
641
                        }
 
642
 
 
643
                        /// <summary>
 
644
                        /// Update this index entry with stat and SHA-1 information if it looks
 
645
                        /// like the file has been modified in the workdir.
 
646
                        /// </summary>
 
647
                        /// <remarks>
 
648
                        /// Update this index entry with stat and SHA-1 information if it looks
 
649
                        /// like the file has been modified in the workdir.
 
650
                        /// </remarks>
 
651
                        /// <param name="f">file in work dir</param>
 
652
                        /// <param name="newContent">the new content of the file</param>
 
653
                        /// <returns>true if a change occurred</returns>
 
654
                        /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
655
                        public virtual bool Update(FilePath f, byte[] newContent)
 
656
                        {
 
657
                                bool modified = false;
 
658
                                this.size = newContent.Length;
 
659
                                ObjectInserter oi = this._enclosing.db.NewObjectInserter();
 
660
                                try
 
661
                                {
 
662
                                        ObjectId newsha1 = oi.Insert(Constants.OBJ_BLOB, newContent);
 
663
                                        oi.Flush();
 
664
                                        if (!newsha1.Equals(this.sha1))
 
665
                                        {
 
666
                                                modified = true;
 
667
                                        }
 
668
                                        this.sha1 = newsha1;
 
669
                                }
 
670
                                finally
 
671
                                {
 
672
                                        oi.Release();
 
673
                                }
 
674
                                return modified;
 
675
                        }
 
676
 
 
677
                        internal virtual void Write(ByteBuffer buf)
 
678
                        {
 
679
                                int startposition = buf.Position();
 
680
                                buf.PutInt((int)(this.ctime / 1000000000L));
 
681
                                buf.PutInt((int)(this.ctime % 1000000000L));
 
682
                                buf.PutInt((int)(this.mtime / 1000000000L));
 
683
                                buf.PutInt((int)(this.mtime % 1000000000L));
 
684
                                buf.PutInt(this.dev);
 
685
                                buf.PutInt(this.ino);
 
686
                                buf.PutInt(this.mode);
 
687
                                buf.PutInt(this.uid);
 
688
                                buf.PutInt(this.gid);
 
689
                                buf.PutInt(this.size);
 
690
                                this.sha1.CopyRawTo(buf);
 
691
                                buf.PutShort(this.flags);
 
692
                                buf.Put(this.name);
 
693
                                int end = startposition + ((8 + 8 + 4 + 4 + 4 + 4 + 4 + 4 + 20 + 2 + this.name.Length
 
694
                                         + 8) & ~7);
 
695
                                int remain = end - buf.Position();
 
696
                                while (remain-- > 0)
 
697
                                {
 
698
                                        buf.Put(unchecked((byte)0));
 
699
                                }
 
700
                        }
 
701
 
 
702
                        /// <summary>
 
703
                        /// Check if an entry's content is different from the cache,
 
704
                        /// File status information is used and status is same we
 
705
                        /// consider the file identical to the state in the working
 
706
                        /// directory.
 
707
                        /// </summary>
 
708
                        /// <remarks>
 
709
                        /// Check if an entry's content is different from the cache,
 
710
                        /// File status information is used and status is same we
 
711
                        /// consider the file identical to the state in the working
 
712
                        /// directory. Native git uses more stat fields than we
 
713
                        /// have accessible in Java.
 
714
                        /// </remarks>
 
715
                        /// <param name="wd">working directory to compare content with</param>
 
716
                        /// <returns>true if content is most likely different.</returns>
 
717
                        public virtual bool IsModified(FilePath wd)
 
718
                        {
 
719
                                return this.IsModified(wd, false);
 
720
                        }
 
721
 
 
722
                        /// <summary>
 
723
                        /// Check if an entry's content is different from the cache,
 
724
                        /// File status information is used and status is same we
 
725
                        /// consider the file identical to the state in the working
 
726
                        /// directory.
 
727
                        /// </summary>
 
728
                        /// <remarks>
 
729
                        /// Check if an entry's content is different from the cache,
 
730
                        /// File status information is used and status is same we
 
731
                        /// consider the file identical to the state in the working
 
732
                        /// directory. Native git uses more stat fields than we
 
733
                        /// have accessible in Java.
 
734
                        /// </remarks>
 
735
                        /// <param name="wd">working directory to compare content with</param>
 
736
                        /// <param name="forceContentCheck">
 
737
                        /// True if the actual file content
 
738
                        /// should be checked if modification time differs.
 
739
                        /// </param>
 
740
                        /// <returns>true if content is most likely different.</returns>
 
741
                        public virtual bool IsModified(FilePath wd, bool forceContentCheck)
 
742
                        {
 
743
                                if (this.IsAssumedValid())
 
744
                                {
 
745
                                        return false;
 
746
                                }
 
747
                                if (this.IsUpdateNeeded())
 
748
                                {
 
749
                                        return true;
 
750
                                }
 
751
                                FilePath file = this.GetFile(wd);
 
752
                                long length = file.Length();
 
753
                                if (length == 0)
 
754
                                {
 
755
                                        if (!file.Exists())
 
756
                                        {
 
757
                                                return true;
 
758
                                        }
 
759
                                }
 
760
                                if (length != this.size)
 
761
                                {
 
762
                                        return true;
 
763
                                }
 
764
                                // JDK1.6 has file.canExecute
 
765
                                // if (file.canExecute() != FileMode.EXECUTABLE_FILE.equals(mode))
 
766
                                // return true;
 
767
                                int exebits = FileMode.EXECUTABLE_FILE.GetBits() ^ FileMode.REGULAR_FILE.GetBits(
 
768
                                        );
 
769
                                if (this._enclosing.Config_filemode() && FileMode.EXECUTABLE_FILE.Equals(this.mode
 
770
                                        ))
 
771
                                {
 
772
                                        if (!this._enclosing.File_canExecute(file) && this._enclosing.File_hasExecute())
 
773
                                        {
 
774
                                                return true;
 
775
                                        }
 
776
                                }
 
777
                                else
 
778
                                {
 
779
                                        if (FileMode.REGULAR_FILE.Equals(this.mode & ~exebits))
 
780
                                        {
 
781
                                                if (!file.IsFile())
 
782
                                                {
 
783
                                                        return true;
 
784
                                                }
 
785
                                                if (this._enclosing.Config_filemode() && this._enclosing.File_canExecute(file) &&
 
786
                                                         this._enclosing.File_hasExecute())
 
787
                                                {
 
788
                                                        return true;
 
789
                                                }
 
790
                                        }
 
791
                                        else
 
792
                                        {
 
793
                                                if (FileMode.SYMLINK.Equals(this.mode))
 
794
                                                {
 
795
                                                        return true;
 
796
                                                }
 
797
                                                else
 
798
                                                {
 
799
                                                        if (FileMode.TREE.Equals(this.mode))
 
800
                                                        {
 
801
                                                                if (!file.IsDirectory())
 
802
                                                                {
 
803
                                                                        return true;
 
804
                                                                }
 
805
                                                        }
 
806
                                                        else
 
807
                                                        {
 
808
                                                                System.Console.Out.WriteLine(MessageFormat.Format(JGitText.Get().doesNotHandleMode
 
809
                                                                        , this.mode, file));
 
810
                                                                return true;
 
811
                                                        }
 
812
                                                }
 
813
                                        }
 
814
                                }
 
815
                                // Git under windows only stores seconds so we round the timestamp
 
816
                                // Java gives us if it looks like the timestamp in index is seconds
 
817
                                // only. Otherwise we compare the timestamp at millisecond prevision.
 
818
                                long javamtime = this.mtime / 1000000L;
 
819
                                long lastm = file.LastModified();
 
820
                                if (javamtime % 1000 == 0)
 
821
                                {
 
822
                                        lastm = lastm - lastm % 1000;
 
823
                                }
 
824
                                if (lastm != javamtime)
 
825
                                {
 
826
                                        if (!forceContentCheck)
 
827
                                        {
 
828
                                                return true;
 
829
                                        }
 
830
                                        try
 
831
                                        {
 
832
                                                InputStream @is = new FileInputStream(file);
 
833
                                                try
 
834
                                                {
 
835
                                                        ObjectId newId = new ObjectInserter.Formatter().IdFor(Constants.OBJ_BLOB, file.Length
 
836
                                                                (), @is);
 
837
                                                        return !newId.Equals(this.sha1);
 
838
                                                }
 
839
                                                catch (IOException e)
 
840
                                                {
 
841
                                                        Sharpen.Runtime.PrintStackTrace(e);
 
842
                                                }
 
843
                                                finally
 
844
                                                {
 
845
                                                        try
 
846
                                                        {
 
847
                                                                @is.Close();
 
848
                                                        }
 
849
                                                        catch (IOException e)
 
850
                                                        {
 
851
                                                                // can't happen, but if it does we ignore it
 
852
                                                                Sharpen.Runtime.PrintStackTrace(e);
 
853
                                                        }
 
854
                                                }
 
855
                                        }
 
856
                                        catch (FileNotFoundException e)
 
857
                                        {
 
858
                                                // should not happen because we already checked this
 
859
                                                Sharpen.Runtime.PrintStackTrace(e);
 
860
                                                throw new Error(e);
 
861
                                        }
 
862
                                }
 
863
                                return false;
 
864
                        }
 
865
 
 
866
                        /// <summary>Returns the stages in which the entry's file is recorded in the index.</summary>
 
867
                        /// <remarks>
 
868
                        /// Returns the stages in which the entry's file is recorded in the index.
 
869
                        /// The stages are bit-encoded: bit N is set if the file is present
 
870
                        /// in stage N. In particular, the N-th bit will be set if this entry
 
871
                        /// itself is in stage N (see getStage()).
 
872
                        /// </remarks>
 
873
                        /// <returns>flags denoting stages</returns>
 
874
                        /// <seealso cref="GetStage()">GetStage()</seealso>
 
875
                        public virtual int GetStages()
 
876
                        {
 
877
                                return this.stages;
 
878
                        }
 
879
 
 
880
                        // for testing
 
881
                        internal virtual void ForceRecheck()
 
882
                        {
 
883
                                this.mtime = -1;
 
884
                        }
 
885
 
 
886
                        private FilePath GetFile(FilePath wd)
 
887
                        {
 
888
                                return new FilePath(wd, this.GetName());
 
889
                        }
 
890
 
 
891
                        public override string ToString()
 
892
                        {
 
893
                                return this.GetName() + "/SHA-1(" + this.sha1.Name + ")/M:" + Sharpen.Extensions.CreateDate
 
894
                                        (this.ctime / 1000000L) + "/C:" + Sharpen.Extensions.CreateDate(this.mtime / 1000000L
 
895
                                        ) + "/d" + this.dev + "/i" + this.ino + "/m" + Sharpen.Extensions.ToString(this.
 
896
                                        mode, 8) + "/u" + this.uid + "/g" + this.gid + "/s" + this.size + "/f" + this.flags
 
897
                                         + "/@" + this.GetStage();
 
898
                        }
 
899
 
 
900
                        /// <returns>path name for this entry</returns>
 
901
                        public virtual string GetName()
 
902
                        {
 
903
                                return RawParseUtils.Decode(this.name);
 
904
                        }
 
905
 
 
906
                        /// <returns>path name for this entry as byte array, hopefully UTF-8 encoded</returns>
 
907
                        public virtual byte[] GetNameUTF8()
 
908
                        {
 
909
                                return this.name;
 
910
                        }
 
911
 
 
912
                        /// <returns>SHA-1 of the entry managed by this index</returns>
 
913
                        public virtual ObjectId GetObjectId()
 
914
                        {
 
915
                                return this.sha1;
 
916
                        }
 
917
 
 
918
                        /// <returns>the stage this entry is in</returns>
 
919
                        public virtual int GetStage()
 
920
                        {
 
921
                                return (this.flags & unchecked((int)(0x3000))) >> 12;
 
922
                        }
 
923
 
 
924
                        /// <returns>size of disk object</returns>
 
925
                        public virtual int GetSize()
 
926
                        {
 
927
                                return this.size;
 
928
                        }
 
929
 
 
930
                        /// <returns>true if this entry shall be assumed valid</returns>
 
931
                        public virtual bool IsAssumedValid()
 
932
                        {
 
933
                                return (this.flags & unchecked((int)(0x8000))) != 0;
 
934
                        }
 
935
 
 
936
                        /// <returns>true if this entry should be checked for changes</returns>
 
937
                        public virtual bool IsUpdateNeeded()
 
938
                        {
 
939
                                return (this.flags & unchecked((int)(0x4000))) != 0;
 
940
                        }
 
941
 
 
942
                        /// <summary>Set whether to always assume this entry valid</summary>
 
943
                        /// <param name="assumeValid">true to ignore changes</param>
 
944
                        public virtual void SetAssumeValid(bool assumeValid)
 
945
                        {
 
946
                                if (assumeValid)
 
947
                                {
 
948
                                        this.flags |= unchecked((short)(0x8000));
 
949
                                }
 
950
                                else
 
951
                                {
 
952
                                        this.flags &= ~unchecked((short)(0x8000));
 
953
                                }
 
954
                        }
 
955
 
 
956
                        /// <summary>Set whether this entry must be checked</summary>
 
957
                        /// <param name="updateNeeded"></param>
 
958
                        public virtual void SetUpdateNeeded(bool updateNeeded)
 
959
                        {
 
960
                                if (updateNeeded)
 
961
                                {
 
962
                                        this.flags |= unchecked((int)(0x4000));
 
963
                                }
 
964
                                else
 
965
                                {
 
966
                                        this.flags &= ~unchecked((int)(0x4000));
 
967
                                }
 
968
                        }
 
969
 
 
970
                        /// <summary>Return raw file mode bits.</summary>
 
971
                        /// <remarks>
 
972
                        /// Return raw file mode bits. See
 
973
                        /// <see cref="FileMode">FileMode</see>
 
974
                        /// </remarks>
 
975
                        /// <returns>file mode bits</returns>
 
976
                        public virtual int GetModeBits()
 
977
                        {
 
978
                                return this.mode;
 
979
                        }
 
980
 
 
981
                        private readonly GitIndex _enclosing;
 
982
                }
 
983
 
 
984
                internal class Header
 
985
                {
 
986
                        private int signature;
 
987
 
 
988
                        private int version;
 
989
 
 
990
                        internal int entries;
 
991
 
 
992
                        /// <exception cref="NGit.Errors.CorruptObjectException"></exception>
 
993
                        internal Header(ByteBuffer map)
 
994
                        {
 
995
                                Read(map);
 
996
                        }
 
997
 
 
998
                        /// <exception cref="NGit.Errors.CorruptObjectException"></exception>
 
999
                        private void Read(ByteBuffer buf)
 
1000
                        {
 
1001
                                signature = buf.GetInt();
 
1002
                                version = buf.GetInt();
 
1003
                                entries = buf.GetInt();
 
1004
                                if (signature != unchecked((int)(0x44495243)))
 
1005
                                {
 
1006
                                        throw new CorruptObjectException(MessageFormat.Format(JGitText.Get().indexSignatureIsInvalid
 
1007
                                                , signature));
 
1008
                                }
 
1009
                                if (version != 2)
 
1010
                                {
 
1011
                                        throw new CorruptObjectException(MessageFormat.Format(JGitText.Get().unknownIndexVersionOrCorruptIndex
 
1012
                                                , version));
 
1013
                                }
 
1014
                        }
 
1015
 
 
1016
                        internal virtual void Write(ByteBuffer buf)
 
1017
                        {
 
1018
                                buf.Order(ByteOrder.BIG_ENDIAN);
 
1019
                                buf.PutInt(signature);
 
1020
                                buf.PutInt(version);
 
1021
                                buf.PutInt(entries);
 
1022
                        }
 
1023
 
 
1024
                        internal Header(IDictionary<byte[],GitIndex.Entry> entryset)
 
1025
                        {
 
1026
                                signature = unchecked((int)(0x44495243));
 
1027
                                version = 2;
 
1028
                                entries = entryset.Count;
 
1029
                        }
 
1030
                }
 
1031
 
 
1032
                /// <summary>Read a Tree recursively into the index</summary>
 
1033
                /// <param name="t">The tree to read</param>
 
1034
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
1035
                public virtual void ReadTree(Tree t)
 
1036
                {
 
1037
                        entries.Clear();
 
1038
                        ReadTree(string.Empty, t);
 
1039
                }
 
1040
 
 
1041
                /// <exception cref="System.IO.IOException"></exception>
 
1042
                internal virtual void ReadTree(string prefix, Tree t)
 
1043
                {
 
1044
                        TreeEntry[] members = t.Members();
 
1045
                        for (int i = 0; i < members.Length; ++i)
 
1046
                        {
 
1047
                                TreeEntry te = members[i];
 
1048
                                string name;
 
1049
                                if (prefix.Length > 0)
 
1050
                                {
 
1051
                                        name = prefix + "/" + te.GetName();
 
1052
                                }
 
1053
                                else
 
1054
                                {
 
1055
                                        name = te.GetName();
 
1056
                                }
 
1057
                                if (te is Tree)
 
1058
                                {
 
1059
                                        ReadTree(name, (Tree)te);
 
1060
                                }
 
1061
                                else
 
1062
                                {
 
1063
                                        GitIndex.Entry e = new GitIndex.Entry(this, te, 0);
 
1064
                                        entries.Put(Constants.Encode(name), e);
 
1065
                                }
 
1066
                        }
 
1067
                }
 
1068
 
 
1069
                /// <summary>Add tree entry to index</summary>
 
1070
                /// <param name="te">tree entry</param>
 
1071
                /// <returns>new or modified index entry</returns>
 
1072
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
1073
                public virtual GitIndex.Entry AddEntry(TreeEntry te)
 
1074
                {
 
1075
                        byte[] key = Constants.Encode(te.GetFullName());
 
1076
                        GitIndex.Entry e = new GitIndex.Entry(this, te, 0);
 
1077
                        entries.Put(key, e);
 
1078
                        return e;
 
1079
                }
 
1080
 
 
1081
                /// <summary>Check out content of the content represented by the index</summary>
 
1082
                /// <param name="wd">workdir</param>
 
1083
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
1084
                public virtual void Checkout(FilePath wd)
 
1085
                {
 
1086
                        foreach (GitIndex.Entry e in entries.Values)
 
1087
                        {
 
1088
                                if (e.GetStage() != 0)
 
1089
                                {
 
1090
                                        continue;
 
1091
                                }
 
1092
                                CheckoutEntry(wd, e);
 
1093
                        }
 
1094
                }
 
1095
 
 
1096
                /// <summary>Check out content of the specified index entry</summary>
 
1097
                /// <param name="wd">workdir</param>
 
1098
                /// <param name="e">index entry</param>
 
1099
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
1100
                public virtual void CheckoutEntry(FilePath wd, GitIndex.Entry e)
 
1101
                {
 
1102
                        ObjectLoader ol = db.Open(e.sha1, Constants.OBJ_BLOB);
 
1103
                        FilePath file = new FilePath(wd, e.GetName());
 
1104
                        file.Delete();
 
1105
                        FileUtils.Mkdirs(file.GetParentFile(), true);
 
1106
                        FileOutputStream dst = new FileOutputStream(file);
 
1107
                        try
 
1108
                        {
 
1109
                                ol.CopyTo(dst);
 
1110
                        }
 
1111
                        finally
 
1112
                        {
 
1113
                                dst.Close();
 
1114
                        }
 
1115
                        if (Config_filemode() && File_hasExecute())
 
1116
                        {
 
1117
                                if (FileMode.EXECUTABLE_FILE.Equals(e.mode))
 
1118
                                {
 
1119
                                        if (!File_canExecute(file))
 
1120
                                        {
 
1121
                                                File_setExecute(file, true);
 
1122
                                        }
 
1123
                                }
 
1124
                                else
 
1125
                                {
 
1126
                                        if (File_canExecute(file))
 
1127
                                        {
 
1128
                                                File_setExecute(file, false);
 
1129
                                        }
 
1130
                                }
 
1131
                        }
 
1132
                        e.mtime = file.LastModified() * 1000000L;
 
1133
                        e.ctime = e.mtime;
 
1134
                }
 
1135
 
 
1136
                /// <summary>Construct and write tree out of index.</summary>
 
1137
                /// <remarks>Construct and write tree out of index.</remarks>
 
1138
                /// <returns>SHA-1 of the constructed tree</returns>
 
1139
                /// <exception cref="System.IO.IOException">System.IO.IOException</exception>
 
1140
                public virtual ObjectId WriteTree()
 
1141
                {
 
1142
                        CheckWriteOk();
 
1143
                        ObjectInserter inserter = db.NewObjectInserter();
 
1144
                        try
 
1145
                        {
 
1146
                                Tree current = new Tree(db);
 
1147
                                Stack<Tree> trees = new Stack<Tree>();
 
1148
                                trees.Push(current);
 
1149
                                string[] prevName = new string[0];
 
1150
                                foreach (GitIndex.Entry e in entries.Values)
 
1151
                                {
 
1152
                                        if (e.GetStage() != 0)
 
1153
                                        {
 
1154
                                                continue;
 
1155
                                        }
 
1156
                                        string[] newName = SplitDirPath(e.GetName());
 
1157
                                        int c = LongestCommonPath(prevName, newName);
 
1158
                                        while (c < trees.Count - 1)
 
1159
                                        {
 
1160
                                                current.SetId(inserter.Insert(Constants.OBJ_TREE, current.Format()));
 
1161
                                                trees.Pop();
 
1162
                                                current = trees.IsEmpty() ? null : (Tree)trees.Peek();
 
1163
                                        }
 
1164
                                        while (trees.Count < newName.Length)
 
1165
                                        {
 
1166
                                                if (!current.ExistsTree(newName[trees.Count - 1]))
 
1167
                                                {
 
1168
                                                        current = new Tree(current, Constants.Encode(newName[trees.Count - 1]));
 
1169
                                                        current.GetParent().AddEntry(current);
 
1170
                                                        trees.Push(current);
 
1171
                                                }
 
1172
                                                else
 
1173
                                                {
 
1174
                                                        current = (Tree)current.FindTreeMember(newName[trees.Count - 1]);
 
1175
                                                        trees.Push(current);
 
1176
                                                }
 
1177
                                        }
 
1178
                                        FileTreeEntry ne = new FileTreeEntry(current, e.sha1, Constants.Encode(newName[newName
 
1179
                                                .Length - 1]), (e.mode & FileMode.EXECUTABLE_FILE.GetBits()) == FileMode.EXECUTABLE_FILE
 
1180
                                                .GetBits());
 
1181
                                        current.AddEntry(ne);
 
1182
                                }
 
1183
                                while (!trees.IsEmpty())
 
1184
                                {
 
1185
                                        current.SetId(inserter.Insert(Constants.OBJ_TREE, current.Format()));
 
1186
                                        trees.Pop();
 
1187
                                        if (!trees.IsEmpty())
 
1188
                                        {
 
1189
                                                current = trees.Peek();
 
1190
                                        }
 
1191
                                }
 
1192
                                inserter.Flush();
 
1193
                                return current.GetId();
 
1194
                        }
 
1195
                        finally
 
1196
                        {
 
1197
                                inserter.Release();
 
1198
                        }
 
1199
                }
 
1200
 
 
1201
                internal virtual string[] SplitDirPath(string name)
 
1202
                {
 
1203
                        string[] tmp = new string[name.Length / 2 + 1];
 
1204
                        int p0 = -1;
 
1205
                        int p1;
 
1206
                        int c = 0;
 
1207
                        while ((p1 = name.IndexOf('/', p0 + 1)) != -1)
 
1208
                        {
 
1209
                                tmp[c++] = Sharpen.Runtime.Substring(name, p0 + 1, p1);
 
1210
                                p0 = p1;
 
1211
                        }
 
1212
                        tmp[c++] = Sharpen.Runtime.Substring(name, p0 + 1);
 
1213
                        string[] ret = new string[c];
 
1214
                        for (int i = 0; i < c; ++i)
 
1215
                        {
 
1216
                                ret[i] = tmp[i];
 
1217
                        }
 
1218
                        return ret;
 
1219
                }
 
1220
 
 
1221
                internal virtual int LongestCommonPath(string[] a, string[] b)
 
1222
                {
 
1223
                        int i;
 
1224
                        for (i = 0; i < a.Length && i < b.Length; ++i)
 
1225
                        {
 
1226
                                if (!a[i].Equals(b[i]))
 
1227
                                {
 
1228
                                        return i;
 
1229
                                }
 
1230
                        }
 
1231
                        return i;
 
1232
                }
 
1233
 
 
1234
                /// <summary>
 
1235
                /// Return the members of the index sorted by the unsigned byte
 
1236
                /// values of the path names.
 
1237
                /// </summary>
 
1238
                /// <remarks>
 
1239
                /// Return the members of the index sorted by the unsigned byte
 
1240
                /// values of the path names.
 
1241
                /// Small beware: Unaccounted for are unmerged entries. You may want
 
1242
                /// to abort if members with stage != 0 are found if you are doing
 
1243
                /// any updating operations. All stages will be found after one another
 
1244
                /// here later. Currently only one stage per name is returned.
 
1245
                /// </remarks>
 
1246
                /// <returns>The index entries sorted</returns>
 
1247
                public virtual GitIndex.Entry[] GetMembers()
 
1248
                {
 
1249
                        return Sharpen.Collections.ToArray(entries.Values, new GitIndex.Entry[entries.Count
 
1250
                                ]);
 
1251
                }
 
1252
 
 
1253
                /// <summary>Look up an entry with the specified path.</summary>
 
1254
                /// <remarks>Look up an entry with the specified path.</remarks>
 
1255
                /// <param name="path"></param>
 
1256
                /// <returns>index entry for the path or null if not in index.</returns>
 
1257
                /// <exception cref="Sharpen.UnsupportedEncodingException">Sharpen.UnsupportedEncodingException
 
1258
                ///     </exception>
 
1259
                public virtual GitIndex.Entry GetEntry(string path)
 
1260
                {
 
1261
                        return entries.Get(Repository.GitInternalSlash(Constants.Encode(path)));
 
1262
                }
 
1263
 
 
1264
                /// <returns>The repository holding this index.</returns>
 
1265
                public virtual Repository GetRepository()
 
1266
                {
 
1267
                        return db;
 
1268
                }
 
1269
        }
 
1270
}