2
This code is derived from jgit (http://eclipse.org/jgit).
3
Copyright owners are documented in jgit's IP log.
5
This program and the accompanying materials are made available
6
under the terms of the Eclipse Distribution License v1.0 which
7
accompanies this distribution, is reproduced below, and is
8
available at http://www.eclipse.org/org/documents/edl-v10.php
12
Redistribution and use in source and binary forms, with or
13
without modification, are permitted provided that the following
16
- Redistributions of source code must retain the above copyright
17
notice, this list of conditions and the following disclaimer.
19
- Redistributions in binary form must reproduce the above
20
copyright notice, this list of conditions and the following
21
disclaimer in the documentation and/or other materials provided
22
with the distribution.
24
- Neither the name of the Eclipse Foundation, Inc. nor the
25
names of its contributors may be used to endorse or promote
26
products derived from this software without specific prior
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45
using System.Collections;
46
using System.Collections.Generic;
56
/// <summary>A representation of the Git index.</summary>
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
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>
72
/// An index can also contain a tree cache which we ignore for now. We drop the
73
/// tree cache when writing the index.
75
[System.ObsoleteAttribute(@"Use NGit.Dircache.DirCache instead.")]
78
/// <summary>Stage 0 represents merged entries.</summary>
79
/// <remarks>Stage 0 represents merged entries.</remarks>
80
public const int STAGE_0 = 0;
82
private RandomAccessFile cache;
84
private FilePath cacheFile;
88
private bool statDirty;
90
private GitIndex.Header header;
92
private long lastCacheTime;
94
private readonly Repository db;
96
private sealed class _IComparer_122 : IComparer<byte[]>
98
public _IComparer_122()
103
// Stat information updated
104
public int Compare(byte[] o1, byte[] o2)
106
for (int i = 0; i < o1.Length && i < o2.Length; ++i)
108
int c = (o1[i] & unchecked((int)(0xff))) - (o2[i] & unchecked((int)(0xff)));
114
if (o1.Length < o2.Length)
120
if (o1.Length > o2.Length)
129
private IDictionary<byte[], GitIndex.Entry> entries = new SortedDictionary<byte[]
130
, GitIndex.Entry>(new _IComparer_122());
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)
138
this.cacheFile = db.GetIndexFile();
141
/// <returns>true if we have modified the index in memory since reading it from disk</returns>
142
public virtual bool IsChanged()
144
return changed || statDirty;
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()
151
if (cacheFile.Exists() && cacheFile.LastModified() != lastCacheTime)
154
db.FireEvent(new IndexChangedEvent());
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)
166
byte[] key = MakeKey(wd, f);
167
GitIndex.Entry e = entries.Get(key);
170
e = new GitIndex.Entry(this, key, f, 0);
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)
189
byte[] key = MakeKey(wd, f);
190
GitIndex.Entry e = entries.Get(key);
193
e = new GitIndex.Entry(this, key, f, 0, content);
198
e.Update(f, content);
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)
211
byte[] key = MakeKey(wd, f);
212
return Sharpen.Collections.Remove(entries, key) != null;
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()
222
if (!cacheFile.Exists())
229
cache = new RandomAccessFile(cacheFile, "r");
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())
238
throw new IOException(MessageFormat.Format(JGitText.Get().couldNotReadIndexInOneGo
239
, j, buffer.Capacity()));
242
header = new GitIndex.Header(buffer);
244
for (int i = 0; i < header.entries; ++i)
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)
251
entry.stages |= existing.stages;
254
lastCacheTime = cacheFile.LastModified();
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()
268
FilePath tmpIndex = new FilePath(cacheFile.GetAbsoluteFile() + ".tmp");
269
FilePath Lock = new FilePath(cacheFile.GetAbsoluteFile() + ".lock");
270
if (!Lock.CreateNewFile())
272
throw new IOException(JGitText.Get().indexFileIsInUse);
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);
283
newMessageDigest.Update(((byte[])buf.Array()), buf.ArrayOffset(), buf.Limit());
287
for (Iterator i = entries.Values.Iterator(); i.HasNext(); )
289
GitIndex.Entry e = (GitIndex.Entry)i.Next();
292
newMessageDigest.Update(((byte[])buf.Array()), buf.ArrayOffset(), buf.Limit());
297
buf.Put(newMessageDigest.Digest());
301
fileOutputStream.Close();
302
if (cacheFile.Exists())
304
if (db.FileSystem.RetryFailedLockFileCommit())
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++)
312
if (cacheFile.Delete())
319
Sharpen.Thread.Sleep(100);
328
throw new IOException(JGitText.Get().couldNotRenameDeleteOldIndex);
333
if (!cacheFile.Delete())
335
throw new IOException(JGitText.Get().couldNotRenameDeleteOldIndex);
339
if (!tmpIndex.RenameTo(cacheFile))
341
throw new IOException(JGitText.Get().couldNotRenameTemporaryIndexFileToIndex);
345
lastCacheTime = cacheFile.LastModified();
346
db.FireEvent(new IndexChangedEvent());
352
throw new IOException(JGitText.Get().couldNotDeleteLockFileShouldNotHappen);
354
if (tmpIndex.Exists() && !tmpIndex.Delete())
356
throw new IOException(JGitText.Get().couldNotDeleteTemporaryIndexFileShouldNotHappen
362
/// <exception cref="System.IO.IOException"></exception>
363
private void CheckWriteOk()
365
for (Iterator i = entries.Values.Iterator(); i.HasNext(); )
367
GitIndex.Entry e = (GitIndex.Entry)i.Next();
368
if (e.GetStage() != 0)
370
throw new NotSupportedException(JGitText.Get().cannotWorkWithOtherStagesThanZeroRightNow
376
private bool File_canExecute(FilePath f)
378
return db.FileSystem.CanExecute(f);
381
private bool File_setExecute(FilePath f, bool value)
383
return db.FileSystem.SetExecute(f, value);
386
private bool File_hasExecute()
388
return db.FileSystem.SupportsExecute();
391
internal static byte[] MakeKey(FilePath wd, FilePath f)
393
if (!f.GetPath().StartsWith(wd.GetPath()))
395
throw new Error(JGitText.Get().pathIsNotInWorkingDir);
397
string relName = Repository.StripWorkDir(wd, f);
398
return Constants.Encode(relName);
401
internal bool filemode;
403
private bool Config_filemode()
405
// temporary til we can actually set parameters. We need to be able
406
// to change this for testing.
407
if (filemode != null)
411
Config config = db.GetConfig();
412
filemode = Sharpen.Extensions.ValueOf(config.GetBoolean("core", null, "filemode",
417
/// <summary>An index entry</summary>
418
[System.ObsoleteAttribute(@"Use NGit.Dircache.DirCacheEntry .")]
437
internal ObjectId sha1;
441
internal byte[] name;
445
/// <exception cref="System.IO.IOException"></exception>
446
internal Entry(GitIndex _enclosing, byte[] key, FilePath f, int stage)
448
this._enclosing = _enclosing;
449
this.ctime = f.LastModified() * 1000000L;
450
this.mtime = this.ctime;
454
if (this._enclosing.Config_filemode() && this._enclosing.File_canExecute(f))
456
this.mode = FileMode.EXECUTABLE_FILE.GetBits();
460
this.mode = FileMode.REGULAR_FILE.GetBits();
464
this.size = (int)f.Length();
465
ObjectInserter inserter = this._enclosing.db.NewObjectInserter();
468
InputStream @in = new FileInputStream(f);
471
this.sha1 = inserter.Insert(Constants.OBJ_BLOB, f.Length(), @in);
484
this.flags = (short)((stage << 12) | this.name.Length);
486
this.stages = (1 >> this.GetStage());
489
/// <exception cref="System.IO.IOException"></exception>
490
internal Entry(GitIndex _enclosing, byte[] key, FilePath f, int stage, byte[] newContent
493
this._enclosing = _enclosing;
494
this.ctime = f.LastModified() * 1000000L;
495
this.mtime = this.ctime;
499
if (this._enclosing.Config_filemode() && this._enclosing.File_canExecute(f))
501
this.mode = FileMode.EXECUTABLE_FILE.GetBits();
505
this.mode = FileMode.REGULAR_FILE.GetBits();
509
this.size = newContent.Length;
510
ObjectInserter inserter = this._enclosing.db.NewObjectInserter();
513
InputStream @in = new FileInputStream(f);
516
this.sha1 = inserter.Insert(Constants.OBJ_BLOB, newContent);
529
this.flags = (short)((stage << 12) | this.name.Length);
531
this.stages = (1 >> this.GetStage());
534
internal Entry(GitIndex _enclosing, TreeEntry f, int stage)
536
this._enclosing = _enclosing;
542
this.mode = f.GetMode().GetBits();
547
this.size = (int)this._enclosing.db.Open(f.GetId(), Constants.OBJ_BLOB).GetSize();
549
catch (IOException e)
551
Sharpen.Runtime.PrintStackTrace(e);
554
this.sha1 = f.GetId();
555
this.name = Constants.Encode(f.GetFullName());
556
this.flags = (short)((stage << 12) | this.name.Length);
558
this.stages = (1 >> this.GetStage());
561
internal Entry(GitIndex _enclosing, ByteBuffer b)
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];
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))];
580
b.Position(startposition + ((8 + 8 + 4 + 4 + 4 + 4 + 4 + 4 + 20 + 2 + this.name.Length
585
/// Update this index entry with stat and SHA-1 information if it looks
586
/// like the file has been modified in the workdir.
589
/// Update this index entry with stat and SHA-1 information if it looks
590
/// like the file has been modified in the workdir.
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)
597
long lm = f.LastModified() * 1000000L;
598
bool modified = this.mtime != lm;
600
if (this.size != f.Length())
604
if (this._enclosing.Config_filemode())
606
if (this._enclosing.File_canExecute(f) != FileMode.EXECUTABLE_FILE.Equals(this.mode
609
this.mode = FileMode.EXECUTABLE_FILE.GetBits();
615
this.size = (int)f.Length();
616
ObjectInserter oi = this._enclosing.db.NewObjectInserter();
619
InputStream @in = new FileInputStream(f);
622
ObjectId newsha1 = oi.Insert(Constants.OBJ_BLOB, f.Length(), @in);
624
if (!newsha1.Equals(this.sha1))
644
/// Update this index entry with stat and SHA-1 information if it looks
645
/// like the file has been modified in the workdir.
648
/// Update this index entry with stat and SHA-1 information if it looks
649
/// like the file has been modified in the workdir.
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)
657
bool modified = false;
658
this.size = newContent.Length;
659
ObjectInserter oi = this._enclosing.db.NewObjectInserter();
662
ObjectId newsha1 = oi.Insert(Constants.OBJ_BLOB, newContent);
664
if (!newsha1.Equals(this.sha1))
677
internal virtual void Write(ByteBuffer buf)
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);
693
int end = startposition + ((8 + 8 + 4 + 4 + 4 + 4 + 4 + 4 + 20 + 2 + this.name.Length
695
int remain = end - buf.Position();
698
buf.Put(unchecked((byte)0));
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
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.
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)
719
return this.IsModified(wd, false);
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
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.
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.
740
/// <returns>true if content is most likely different.</returns>
741
public virtual bool IsModified(FilePath wd, bool forceContentCheck)
743
if (this.IsAssumedValid())
747
if (this.IsUpdateNeeded())
751
FilePath file = this.GetFile(wd);
752
long length = file.Length();
760
if (length != this.size)
764
// JDK1.6 has file.canExecute
765
// if (file.canExecute() != FileMode.EXECUTABLE_FILE.equals(mode))
767
int exebits = FileMode.EXECUTABLE_FILE.GetBits() ^ FileMode.REGULAR_FILE.GetBits(
769
if (this._enclosing.Config_filemode() && FileMode.EXECUTABLE_FILE.Equals(this.mode
772
if (!this._enclosing.File_canExecute(file) && this._enclosing.File_hasExecute())
779
if (FileMode.REGULAR_FILE.Equals(this.mode & ~exebits))
785
if (this._enclosing.Config_filemode() && this._enclosing.File_canExecute(file) &&
786
this._enclosing.File_hasExecute())
793
if (FileMode.SYMLINK.Equals(this.mode))
799
if (FileMode.TREE.Equals(this.mode))
801
if (!file.IsDirectory())
808
System.Console.Out.WriteLine(MessageFormat.Format(JGitText.Get().doesNotHandleMode
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)
822
lastm = lastm - lastm % 1000;
824
if (lastm != javamtime)
826
if (!forceContentCheck)
832
InputStream @is = new FileInputStream(file);
835
ObjectId newId = new ObjectInserter.Formatter().IdFor(Constants.OBJ_BLOB, file.Length
837
return !newId.Equals(this.sha1);
839
catch (IOException e)
841
Sharpen.Runtime.PrintStackTrace(e);
849
catch (IOException e)
851
// can't happen, but if it does we ignore it
852
Sharpen.Runtime.PrintStackTrace(e);
856
catch (FileNotFoundException e)
858
// should not happen because we already checked this
859
Sharpen.Runtime.PrintStackTrace(e);
866
/// <summary>Returns the stages in which the entry's file is recorded in the index.</summary>
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()).
873
/// <returns>flags denoting stages</returns>
874
/// <seealso cref="GetStage()">GetStage()</seealso>
875
public virtual int GetStages()
881
internal virtual void ForceRecheck()
886
private FilePath GetFile(FilePath wd)
888
return new FilePath(wd, this.GetName());
891
public override string ToString()
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();
900
/// <returns>path name for this entry</returns>
901
public virtual string GetName()
903
return RawParseUtils.Decode(this.name);
906
/// <returns>path name for this entry as byte array, hopefully UTF-8 encoded</returns>
907
public virtual byte[] GetNameUTF8()
912
/// <returns>SHA-1 of the entry managed by this index</returns>
913
public virtual ObjectId GetObjectId()
918
/// <returns>the stage this entry is in</returns>
919
public virtual int GetStage()
921
return (this.flags & unchecked((int)(0x3000))) >> 12;
924
/// <returns>size of disk object</returns>
925
public virtual int GetSize()
930
/// <returns>true if this entry shall be assumed valid</returns>
931
public virtual bool IsAssumedValid()
933
return (this.flags & unchecked((int)(0x8000))) != 0;
936
/// <returns>true if this entry should be checked for changes</returns>
937
public virtual bool IsUpdateNeeded()
939
return (this.flags & unchecked((int)(0x4000))) != 0;
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)
948
this.flags |= unchecked((short)(0x8000));
952
this.flags &= ~unchecked((short)(0x8000));
956
/// <summary>Set whether this entry must be checked</summary>
957
/// <param name="updateNeeded"></param>
958
public virtual void SetUpdateNeeded(bool updateNeeded)
962
this.flags |= unchecked((int)(0x4000));
966
this.flags &= ~unchecked((int)(0x4000));
970
/// <summary>Return raw file mode bits.</summary>
972
/// Return raw file mode bits. See
973
/// <see cref="FileMode">FileMode</see>
975
/// <returns>file mode bits</returns>
976
public virtual int GetModeBits()
981
private readonly GitIndex _enclosing;
984
internal class Header
986
private int signature;
990
internal int entries;
992
/// <exception cref="NGit.Errors.CorruptObjectException"></exception>
993
internal Header(ByteBuffer map)
998
/// <exception cref="NGit.Errors.CorruptObjectException"></exception>
999
private void Read(ByteBuffer buf)
1001
signature = buf.GetInt();
1002
version = buf.GetInt();
1003
entries = buf.GetInt();
1004
if (signature != unchecked((int)(0x44495243)))
1006
throw new CorruptObjectException(MessageFormat.Format(JGitText.Get().indexSignatureIsInvalid
1011
throw new CorruptObjectException(MessageFormat.Format(JGitText.Get().unknownIndexVersionOrCorruptIndex
1016
internal virtual void Write(ByteBuffer buf)
1018
buf.Order(ByteOrder.BIG_ENDIAN);
1019
buf.PutInt(signature);
1020
buf.PutInt(version);
1021
buf.PutInt(entries);
1024
internal Header(IDictionary<byte[],GitIndex.Entry> entryset)
1026
signature = unchecked((int)(0x44495243));
1028
entries = entryset.Count;
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)
1038
ReadTree(string.Empty, t);
1041
/// <exception cref="System.IO.IOException"></exception>
1042
internal virtual void ReadTree(string prefix, Tree t)
1044
TreeEntry[] members = t.Members();
1045
for (int i = 0; i < members.Length; ++i)
1047
TreeEntry te = members[i];
1049
if (prefix.Length > 0)
1051
name = prefix + "/" + te.GetName();
1055
name = te.GetName();
1059
ReadTree(name, (Tree)te);
1063
GitIndex.Entry e = new GitIndex.Entry(this, te, 0);
1064
entries.Put(Constants.Encode(name), e);
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)
1075
byte[] key = Constants.Encode(te.GetFullName());
1076
GitIndex.Entry e = new GitIndex.Entry(this, te, 0);
1077
entries.Put(key, e);
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)
1086
foreach (GitIndex.Entry e in entries.Values)
1088
if (e.GetStage() != 0)
1092
CheckoutEntry(wd, e);
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)
1102
ObjectLoader ol = db.Open(e.sha1, Constants.OBJ_BLOB);
1103
FilePath file = new FilePath(wd, e.GetName());
1105
FileUtils.Mkdirs(file.GetParentFile(), true);
1106
FileOutputStream dst = new FileOutputStream(file);
1115
if (Config_filemode() && File_hasExecute())
1117
if (FileMode.EXECUTABLE_FILE.Equals(e.mode))
1119
if (!File_canExecute(file))
1121
File_setExecute(file, true);
1126
if (File_canExecute(file))
1128
File_setExecute(file, false);
1132
e.mtime = file.LastModified() * 1000000L;
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()
1143
ObjectInserter inserter = db.NewObjectInserter();
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)
1152
if (e.GetStage() != 0)
1156
string[] newName = SplitDirPath(e.GetName());
1157
int c = LongestCommonPath(prevName, newName);
1158
while (c < trees.Count - 1)
1160
current.SetId(inserter.Insert(Constants.OBJ_TREE, current.Format()));
1162
current = trees.IsEmpty() ? null : (Tree)trees.Peek();
1164
while (trees.Count < newName.Length)
1166
if (!current.ExistsTree(newName[trees.Count - 1]))
1168
current = new Tree(current, Constants.Encode(newName[trees.Count - 1]));
1169
current.GetParent().AddEntry(current);
1170
trees.Push(current);
1174
current = (Tree)current.FindTreeMember(newName[trees.Count - 1]);
1175
trees.Push(current);
1178
FileTreeEntry ne = new FileTreeEntry(current, e.sha1, Constants.Encode(newName[newName
1179
.Length - 1]), (e.mode & FileMode.EXECUTABLE_FILE.GetBits()) == FileMode.EXECUTABLE_FILE
1181
current.AddEntry(ne);
1183
while (!trees.IsEmpty())
1185
current.SetId(inserter.Insert(Constants.OBJ_TREE, current.Format()));
1187
if (!trees.IsEmpty())
1189
current = trees.Peek();
1193
return current.GetId();
1201
internal virtual string[] SplitDirPath(string name)
1203
string[] tmp = new string[name.Length / 2 + 1];
1207
while ((p1 = name.IndexOf('/', p0 + 1)) != -1)
1209
tmp[c++] = Sharpen.Runtime.Substring(name, p0 + 1, p1);
1212
tmp[c++] = Sharpen.Runtime.Substring(name, p0 + 1);
1213
string[] ret = new string[c];
1214
for (int i = 0; i < c; ++i)
1221
internal virtual int LongestCommonPath(string[] a, string[] b)
1224
for (i = 0; i < a.Length && i < b.Length; ++i)
1226
if (!a[i].Equals(b[i]))
1235
/// Return the members of the index sorted by the unsigned byte
1236
/// values of the path names.
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.
1246
/// <returns>The index entries sorted</returns>
1247
public virtual GitIndex.Entry[] GetMembers()
1249
return Sharpen.Collections.ToArray(entries.Values, new GitIndex.Entry[entries.Count
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
1259
public virtual GitIndex.Entry GetEntry(string path)
1261
return entries.Get(Repository.GitInternalSlash(Constants.Encode(path)));
1264
/// <returns>The repository holding this index.</returns>
1265
public virtual Repository GetRepository()