/// <summary> /// Recursively add an entire tree into this builder. /// <para /> /// If pathPrefix is "a/b" and the tree contains file "c" then the resulting /// DirCacheEntry will have the path "a/b/c". /// <para /> /// All entries are inserted at stage 0, therefore assuming that the /// application will not insert any other paths with the same pathPrefix. /// </summary> /// <param name="pathPrefix"> /// UTF-8 encoded prefix to mount the tree's entries at. If the /// path does not end with '/' one will be automatically inserted /// as necessary. /// </param> /// <param name="stage">Stage of the entries when adding them.</param> /// <param name="db"> /// Repository the tree(s) will be read from during recursive /// traversal. This must be the same repository that the resulting /// <see cref="DirCache"/> would be written out to (or used in) otherwise /// the caller is simply asking for deferred MissingObjectExceptions. /// </param> /// <param name="tree"> /// The tree to recursively add. This tree's contents will appear /// under <paramref name="pathPrefix"/>. The ObjectId must be that of a /// tree; the caller is responsible for dereferencing a tag or /// commit (if necessary). /// </param> /// <exception cref="IOException"> /// A tree cannot be read to iterate through its entries. /// </exception> public void addTree(byte[] pathPrefix, int stage, Repository db, AnyObjectId tree) { var tw = new TreeWalk.TreeWalk(db); tw.reset(); var curs = new WindowCursor(); try { tw.addTree(new CanonicalTreeParser(pathPrefix, db, tree.ToObjectId(), curs)); } finally { curs.Release(); } tw.Recursive = true; if (!tw.next()) { return; } DirCacheEntry newEntry = ToEntry(stage, tw); BeforeAdd(newEntry); FastAdd(newEntry); while (tw.next()) { FastAdd(ToEntry(stage, tw)); } }
public void testNonRecursiveFiltering() { var ow = new ObjectWriter(db); ObjectId aSth = ow.WriteBlob("a.sth".getBytes()); ObjectId aTxt = ow.WriteBlob("a.txt".getBytes()); DirCache dc = DirCache.read(db); DirCacheBuilder builder = dc.builder(); var aSthEntry = new DirCacheEntry("a.sth"); aSthEntry.setFileMode(FileMode.RegularFile); aSthEntry.setObjectId(aSth); var aTxtEntry = new DirCacheEntry("a.txt"); aTxtEntry.setFileMode(FileMode.RegularFile); aTxtEntry.setObjectId(aTxt); builder.add(aSthEntry); builder.add(aTxtEntry); builder.finish(); ObjectId treeId = dc.writeTree(ow); var tw = new GitSharp.Core.TreeWalk.TreeWalk(db); tw.setFilter(PathSuffixFilter.create(".txt")); tw.addTree(treeId); var paths = new LinkedList<string>(); while (tw.next()) { paths.AddLast(tw.getPathString()); } var expected = new LinkedList<string>(); expected.AddLast("a.txt"); Assert.AreEqual(expected, paths); }
public void testNoSubtree_NoTreeWalk() { DirCache dc = DirCache.read(db); string[] paths = { "a.", "a0b" }; var ents = new DirCacheEntry[paths.Length]; for (int i = 0; i < paths.Length; i++) { ents[i] = new DirCacheEntry(paths[i]); } DirCacheBuilder b = dc.builder(); for (int i = 0; i < ents.Length; i++) { b.add(ents[i]); } b.finish(); var iter = new DirCacheIterator(dc); int pathIdx = 0; for (; !iter.eof(); iter.next(1)) { Assert.AreEqual(pathIdx, iter.Pointer); Assert.AreSame(ents[pathIdx], iter.getDirCacheEntry()); pathIdx++; } Assert.AreEqual(paths.Length, pathIdx); }
private void Resort() { Array.Sort(Entries, 0, EntryCnt, new GenericComparer <DirCacheEntry>(DirCache.EntryComparer)); for (int entryIdx = 1; entryIdx < EntryCnt; entryIdx++) { DirCacheEntry pe = Entries[entryIdx - 1]; DirCacheEntry ce = Entries[entryIdx]; int cr = DirCache.Compare(pe, ce); if (cr == 0) { // Same file path; we can only allow this if the stages // are 1-3 and no 0 exists. // int peStage = pe.getStage(); int ceStage = ce.getStage(); if (peStage == ceStage) { throw Bad(ce, "Duplicate stages not allowed"); } if (peStage == 0 || ceStage == 0) { throw Bad(ce, "Mixed stages not allowed"); } } } _sorted = true; }
private void testLongPath(int len) { string longPath = makeLongPath(len); string shortPath = "~~~ shorter-path"; DirCacheEntry longEnt = new DirCacheEntry(longPath); DirCacheEntry shortEnt = new DirCacheEntry(shortPath); longEnt.setFileMode(FileMode.RegularFile); shortEnt.setFileMode(FileMode.RegularFile); Assert.AreEqual(longPath, longEnt.getPathString()); Assert.AreEqual(shortPath, shortEnt.getPathString()); DirCache dc1 = DirCache.Lock(db); DirCacheBuilder b = dc1.builder(); b.add(longEnt); b.add(shortEnt); Assert.IsTrue(b.commit()); Assert.AreEqual(2, dc1.getEntryCount()); Assert.AreSame(longEnt, dc1.getEntry(0)); Assert.AreSame(shortEnt, dc1.getEntry(1)); DirCache dc2 = DirCache.read(db); Assert.AreEqual(2, dc2.getEntryCount()); Assert.AreNotSame(longEnt, dc2.getEntry(0)); Assert.AreEqual(longPath, dc2.getEntry(0).getPathString()); Assert.AreNotSame(shortEnt, dc2.getEntry(1)); Assert.AreEqual(shortPath, dc2.getEntry(1).getPathString()); }
private void ApplyEdits() { _edits.Sort(EditComparison); int maxIdx = Cache.getEntryCount(); int lastIdx = 0; foreach (PathEdit e in _edits) { int eIdx = Cache.findEntry(e.Path, e.Path.Length); bool missing = eIdx < 0; if (eIdx < 0) { eIdx = -(eIdx + 1); } int cnt = Math.Min(eIdx, maxIdx) - lastIdx; if (cnt > 0) { FastKeep(lastIdx, cnt); } lastIdx = missing ? eIdx : Cache.nextEntry(eIdx); if (e is DeletePath) { continue; } if (e is DeleteTree) { lastIdx = Cache.nextEntry(e.Path, e.Path.Length, eIdx); continue; } DirCacheEntry ent; if (missing) { ent = new DirCacheEntry(e.Path); e.Apply(ent); if (ent.getRawMode() == 0) { throw new ArgumentException("FileMode not set" + " for path " + ent.getPathString()); } } else { ent = Cache.getEntry(eIdx); e.Apply(ent); } FastAdd(ent); } int count = maxIdx - lastIdx; if (count > 0) { FastKeep(lastIdx, count); } }
/// <summary> /// Copy the ObjectId and other meta fields from an existing entry. /// <para /> /// This method copies everything except the path from one entry to another, /// supporting renaming. /// </summary> /// <param name="src"> /// The entry to copy ObjectId and meta fields from. /// </param> public void copyMetaData(DirCacheEntry src) { int pLen = NB.decodeUInt16(_info, _infoOffset + PFlags) & NameMask; Array.Copy(src._info, src._infoOffset, _info, _infoOffset, INFO_LEN); NB.encodeInt16(_info, _infoOffset + PFlags, pLen | NB.decodeUInt16(_info, _infoOffset + PFlags) & ~NameMask); }
private static DirCacheEntry ToEntry(int stage, TreeWalk.TreeWalk tw) { var e = new DirCacheEntry(tw.getRawPath(), stage); var iterator = tw.getTree <AbstractTreeIterator>(0, typeof(AbstractTreeIterator)); e.setFileMode(tw.getFileMode(0)); e.setObjectIdFromRaw(iterator.idBuffer(), iterator.idOffset()); return(e); }
private void WriteTo(Stream os) { MessageDigest foot = Constants.newMessageDigest(); var dos = new DigestOutputStream(os, foot); // Write the header. // var tmp = new byte[128]; Array.Copy(SigDirc, 0, tmp, 0, SigDirc.Length); NB.encodeInt32(tmp, 4, /* version */ 2); NB.encodeInt32(tmp, 8, _entryCnt); dos.Write(tmp, 0, 12); // Write the individual file entries. // if (_lastModified == DateTime.MinValue) { // Write a new index, as no entries require smudging. // for (int i = 0; i < _entryCnt; i++) { _sortedEntries[i].write(dos); } } else { var smudge_s = _lastModified.ToUnixTime(); var smudge_ns = _lastModified.Millisecond * 1000000; // [henon] <--- this could be done with much more precision in C# since DateTime has 100 nanosec ticks for (int i = 0; i < _entryCnt; i++) { DirCacheEntry e = _sortedEntries[i]; if (e.mightBeRacilyClean(smudge_s, smudge_ns)) { e.smudgeRacilyClean(); } e.write(dos); } } if (_cacheTree != null) { var bb = new TemporaryBuffer(); _cacheTree.write(tmp, bb); bb.close(); NB.encodeInt32(tmp, 0, ExtTree); NB.encodeInt32(tmp, 4, (int)bb.Length); dos.Write(tmp, 0, 8); bb.writeTo(dos, null); } var hash = foot.Digest(); os.Write(hash, 0, hash.Length); os.Close(); }
private void WriteTo(Stream os) { MessageDigest foot = Constants.newMessageDigest(); var dos = new DigestOutputStream(os, foot); // Write the header. // var tmp = new byte[128]; Array.Copy(SigDirc, 0, tmp, 0, SigDirc.Length); NB.encodeInt32(tmp, 4, /* version */ 2); NB.encodeInt32(tmp, 8, _entryCnt); dos.Write(tmp, 0, 12); // Write the individual file entries. // if (_lastModified <= 0) { // Write a new index, as no entries require smudging. // for (int i = 0; i < _entryCnt; i++) { _sortedEntries[i].write(dos); } } else { int smudge_s = (int)(_lastModified / 1000); int smudge_ns = ((int)(_lastModified % 1000)) * 1000000; for (int i = 0; i < _entryCnt; i++) { DirCacheEntry e = _sortedEntries[i]; if (e.mightBeRacilyClean(smudge_s, smudge_ns)) { e.smudgeRacilyClean(); } e.write(dos); } } if (_cacheTree != null) { var bb = new LocalFileBuffer(); _cacheTree.write(tmp, bb); bb.close(); NB.encodeInt32(tmp, 0, ExtTree); NB.encodeInt32(tmp, 4, (int)bb.Length); dos.Write(tmp, 0, 8); bb.writeTo(dos, null); } var hash = foot.Digest(); os.Write(hash, 0, hash.Length); os.Close(); }
/// <summary> /// Append one entry into the resulting entry list. /// <para /> /// The entry is placed at the end of the entry list. The caller is /// responsible for making sure the final table is correctly sorted. /// <para /> /// The <seealso cref="Entries"/> table is automatically expanded /// if there is insufficient space for the new addition. /// </summary> /// <param name="newEntry">The new entry to add.</param> protected void FastAdd(DirCacheEntry newEntry) { if (_entries.Length == _entryCnt) { var n = new DirCacheEntry[(_entryCnt + 16) * 3 / 2]; Array.Copy(_entries, 0, n, 0, _entryCnt); _entries = n; } _entries[_entryCnt++] = newEntry; }
/// <summary> /// Update the DirCache with the contents of <seealso cref="Entries"/>. /// <para /> /// This method should be invoked only during an implementation of /// <seealso cref="finish()"/>, and only after <seealso cref="Entries"/> is sorted. /// </summary> protected void Replace() { if (_entryCnt < _entries.Length / 2) { var n = new DirCacheEntry[_entryCnt]; Array.Copy(_entries, 0, n, 0, _entryCnt); _entries = n; } _cache.replace(_entries, _entryCnt); }
/// <summary> /// Append one entry into the resulting entry list. /// <para /> /// The entry is placed at the end of the entry list. If the entry causes the /// list to now be incorrectly sorted a final sorting phase will be /// automatically enabled within <seealso cref="finish()"/>. /// <para /> /// The internal entry table is automatically expanded if there is /// insufficient space for the new addition. /// </summary> /// <param name="newEntry">the new entry to add.</param> public void add(DirCacheEntry newEntry) { if (newEntry.getRawMode() == 0) { throw new ArgumentException("FileMode not set for path " + newEntry.getPathString()); } BeforeAdd(newEntry); FastAdd(newEntry); }
public void testEntriesWithin() { DirCache dc = DirCache.read(db); string[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; DirCacheEntry[] ents = new DirCacheEntry[paths.Length]; for (int i = 0; i < paths.Length; i++) { ents[i] = new DirCacheEntry(paths[i]); ents[i].setFileMode(FileMode.RegularFile); } int aFirst = 1; int aLast = 3; DirCacheBuilder b = dc.builder(); for (int i = 0; i < ents.Length; i++) { b.add(ents[i]); } b.finish(); Assert.AreEqual(paths.Length, dc.getEntryCount()); for (int i = 0; i < ents.Length; i++) { Assert.AreSame(ents[i], dc.getEntry(i)); } DirCacheEntry[] aContents = dc.getEntriesWithin("a"); Assert.IsNotNull(aContents); Assert.AreEqual(aLast - aFirst + 1, aContents.Length); for (int i = aFirst, j = 0; i <= aLast; i++, j++) { Assert.AreSame(ents[i], aContents[j]); } aContents = dc.getEntriesWithin("a/"); Assert.IsNotNull(aContents); Assert.AreEqual(aLast - aFirst + 1, aContents.Length); for (int i = aFirst, j = 0; i <= aLast; i++, j++) { Assert.AreSame(ents[i], aContents[j]); } Assert.IsNotNull(dc.getEntriesWithin("a.")); Assert.AreEqual(0, dc.getEntriesWithin("a.").Length); Assert.IsNotNull(dc.getEntriesWithin("a0b")); Assert.AreEqual(0, dc.getEntriesWithin("a0b.").Length); Assert.IsNotNull(dc.getEntriesWithin("zoo")); Assert.AreEqual(0, dc.getEntriesWithin("zoo.").Length); }
public int nextEntry(byte[] p, int pLen, int nextIdx) { while (nextIdx < _entryCnt) { DirCacheEntry next = _sortedEntries[nextIdx]; if (!DirCacheTree.peq(p, next.Path, pLen)) { break; } nextIdx++; } return(nextIdx); }
public void testCreate_ByStringPathAndStage() { DirCacheEntry e; e = new DirCacheEntry("a", 0); Assert.AreEqual("a", e.getPathString()); Assert.AreEqual(0, e.getStage()); e = new DirCacheEntry("a/b", 1); Assert.AreEqual("a/b", e.getPathString()); Assert.AreEqual(1, e.getStage()); e = new DirCacheEntry("a/c", 2); Assert.AreEqual("a/c", e.getPathString()); Assert.AreEqual(2, e.getStage()); e = new DirCacheEntry("a/d", 3); Assert.AreEqual("a/d", e.getPathString()); Assert.AreEqual(3, e.getStage()); try { new DirCacheEntry("/a", 1); Assert.Fail("Incorrectly created DirCacheEntry"); } catch (ArgumentException err) { Assert.AreEqual("Invalid path: /a", err.Message); } try { new DirCacheEntry("a", -11); Assert.Fail("Incorrectly created DirCacheEntry"); } catch (ArgumentException err) { Assert.AreEqual("Invalid stage -11 for path a", err.Message); } try { new DirCacheEntry("a", 4); Assert.Fail("Incorrectly created DirCacheEntry"); } catch (ArgumentException err) { Assert.AreEqual("Invalid stage 4 for path a", err.Message); } }
/// <summary> /// Add a range of existing entries from the destination cache. /// <para /> /// The entries are placed at the end of the entry list, preserving their /// current order. The caller is responsible for making sure the final table /// is correctly sorted. /// <para /> /// This method copies from the destination cache, which has not yet been /// updated with this editor's new table. So all offsets into the destination /// cache are not affected by any updates that may be currently taking place /// in this editor. /// <para /> /// The <seealso cref="Entries"/> table is automatically expanded if there is /// insufficient space for the new additions. /// </summary> /// <param name="pos">First entry to copy from the destination cache. </param> /// <param name="cnt">Number of entries to copy.</param> protected void FastKeep(int pos, int cnt) { if (_entryCnt + cnt > _entries.Length) { int m1 = (_entryCnt + 16) * 3 / 2; int m2 = _entryCnt + cnt; var n = new DirCacheEntry[Math.Max(m1, m2)]; Array.Copy(_entries, 0, n, 0, _entryCnt); _entries = n; } _cache.toArray(pos, _entries, _entryCnt, cnt); _entryCnt += cnt; }
public void testPathFilterGroup_DoesNotSkipTail() { DirCache dc = DirCache.read(db); var mode = FileMode.RegularFile; string[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; var ents = new DirCacheEntry[paths.Length]; for (int i = 0; i < paths.Length; i++) { ents[i] = new DirCacheEntry(paths[i]); ents[i].setFileMode(mode); } DirCacheBuilder builder = dc.builder(); for (int i = 0; i < ents.Length; i++) { builder.add(ents[i]); } builder.finish(); const int expIdx = 2; DirCacheBuilder b = dc.builder(); var tw = new GitSharp.Core.TreeWalk.TreeWalk(db); tw.reset(); tw.addTree(new DirCacheBuildIterator(b)); tw.Recursive = true; tw.setFilter(PathFilterGroup.createFromStrings(new[] { paths[expIdx] })); Assert.IsTrue(tw.next(), "found " + paths[expIdx]); var c = tw.getTree<DirCacheIterator>(0, typeof(DirCacheIterator)); Assert.IsNotNull(c); Assert.AreEqual(expIdx, c.Pointer); Assert.AreSame(ents[expIdx], c.getDirCacheEntry()); Assert.AreEqual(paths[expIdx], tw.getPathString()); Assert.AreEqual(mode.Bits, tw.getRawMode(0)); Assert.AreSame(mode, tw.getFileMode(0)); b.add(c.getDirCacheEntry()); Assert.IsFalse(tw.next(), "no more entries"); b.finish(); Assert.AreEqual(ents.Length, dc.getEntryCount()); for (int i = 0; i < ents.Length; i++) { Assert.AreSame(ents[i], dc.getEntry(i)); } }
/// <summary> /// Determine the next index position past all entries with the same name. /// <para /> /// As index entries are sorted by path name, then stage number, this method /// advances the supplied position to the first position in the index whose /// path name does not match the path name of the supplied position's entry. /// </summary> /// <param name="position"> /// entry position of the path that should be skipped. /// </param> /// <returns> /// Position of the next entry whose path is after the input. /// </returns> public int nextEntry(int position) { DirCacheEntry last = _sortedEntries[position]; int nextIdx = position + 1; while (nextIdx < _entryCnt) { DirCacheEntry next = _sortedEntries[nextIdx]; if (Compare(last, next) != 0) { break; } last = next; nextIdx++; } return(nextIdx); }
public void testBuildRejectsUnsetFileMode() { DirCache dc = DirCache.newInCore(); DirCacheBuilder b = dc.builder(); Assert.IsNotNull(b); DirCacheEntry e = new DirCacheEntry("a"); Assert.AreEqual(0, e.getRawMode()); try { b.add(e); } catch (ArgumentException err) { Assert.AreEqual("FileMode not set for path a", err.Message); } }
/// <summary> /// Write (if necessary) this tree to the object store. /// </summary> /// <param name="cacheEntry">the complete cache from DirCache.</param> /// <param name="cIdx"> /// first position of <code>cache</code> that is a member of this /// tree. The path of <code>cache[cacheIdx].path</code> for the /// range <code>[0,pathOff-1)</code> matches the complete path of /// this tree, from the root of the repository. </param> /// <param name="pathOffset"> /// number of bytes of <code>cache[cacheIdx].path</code> that /// matches this tree's path. The value at array position /// <code>cache[cacheIdx].path[pathOff-1]</code> is always '/' if /// <code>pathOff</code> is > 0. /// </param> /// <param name="ow"> /// the writer to use when serializing to the store. /// </param> /// <returns>identity of this tree.</returns> /// <exception cref="UnmergedPathException"> /// one or more paths contain higher-order stages (stage > 0), /// which cannot be stored in a tree object. /// </exception> /// <exception cref="IOException"> /// an unexpected error occurred writing to the object store. /// </exception> public ObjectId writeTree(DirCacheEntry[] cacheEntry, int cIdx, int pathOffset, ObjectWriter ow) { if (_id == null) { int endIdx = cIdx + _entrySpan; int size = ComputeSize(cacheEntry, cIdx, pathOffset, ow); var @out = new MemoryStream(size); int childIdx = 0; int entryIdx = cIdx; while (entryIdx < endIdx) { DirCacheEntry e = cacheEntry[entryIdx]; byte[] ep = e.Path; if (childIdx < _childCount) { DirCacheTree st = _children[childIdx]; if (st.contains(ep, pathOffset, ep.Length)) { FileMode.Tree.CopyTo(@out); @out.Write(new[] { (byte)' ' }, 0, 1); @out.Write(st._encodedName, 0, st._encodedName.Length); @out.Write(new[] { (byte)0 }, 0, 1); st._id.copyRawTo(@out); entryIdx += st._entrySpan; childIdx++; continue; } } e.getFileMode().CopyTo(@out); @out.Write(new[] { (byte)' ' }, 0, 1); @out.Write(ep, pathOffset, ep.Length - pathOffset); @out.Write(new byte[] { 0 }, 0, 1); @out.Write(e.idBuffer(), e.idOffset(), Constants.OBJECT_ID_LENGTH); entryIdx++; } _id = ow.WriteCanonicalTree(@out.ToArray()); } return(_id); }
private void ApplyEdits() { _edits.Sort(EditComparison); int maxIdx = Cache.getEntryCount(); int lastIdx = 0; foreach (PathEdit e in _edits) { int eIdx = Cache.findEntry(e.Path, e.Path.Length); bool missing = eIdx < 0; if (eIdx < 0) { eIdx = -(eIdx + 1); } int cnt = Math.Min(eIdx, maxIdx) - lastIdx; if (cnt > 0) { FastKeep(lastIdx, cnt); } lastIdx = missing ? eIdx : Cache.nextEntry(eIdx); if (e is DeletePath) { continue; } if (e is DeleteTree) { lastIdx = Cache.nextEntry(e.Path, e.Path.Length, eIdx); continue; } DirCacheEntry ent = missing ? new DirCacheEntry(e.Path) : Cache.getEntry(eIdx); e.Apply(ent); FastAdd(ent); } int count = maxIdx - lastIdx; if (count > 0) { FastKeep(lastIdx, count); } }
private int ComputeSize(DirCacheEntry[] cache, int cIdx, int pathOffset, ObjectWriter ow) { int endIdx = cIdx + _entrySpan; int childIdx = 0; int entryIdx = cIdx; int size = 0; while (entryIdx < endIdx) { DirCacheEntry e = cache[entryIdx]; if (e.getStage() != 0) { throw new UnmergedPathException(e); } byte[] ep = e.Path; if (childIdx < _childCount) { DirCacheTree st = _children[childIdx]; if (st.contains(ep, pathOffset, ep.Length)) { int stOffset = pathOffset + st.nameLength() + 1; st.writeTree(cache, entryIdx, stOffset, ow); size += FileMode.Tree.copyToLength(); size += st.nameLength(); size += Constants.OBJECT_ID_LENGTH + 2; entryIdx += st._entrySpan; childIdx++; continue; } } FileMode mode = e.getFileMode(); size += mode.copyToLength(); size += ep.Length - pathOffset; size += Constants.OBJECT_ID_LENGTH + 2; entryIdx++; } return(size); }
private void BeforeAdd(DirCacheEntry newEntry) { if (FileMode.Tree.Equals(newEntry.getRawMode())) { throw Bad(newEntry, "Adding subtree not allowed"); } if (_sorted && EntryCnt > 0) { DirCacheEntry lastEntry = Entries[EntryCnt - 1]; int cr = DirCache.Compare(lastEntry, newEntry); if (cr > 0) { // The new entry sorts before the old entry; we are // no longer sorted correctly. We'll need to redo // the sorting before we can close out the build. // _sorted = false; } else if (cr == 0) { // Same file path; we can only insert this if the // stages won't be violated. // int peStage = lastEntry.getStage(); int dceStage = newEntry.getStage(); if (peStage == dceStage) { throw Bad(newEntry, "Duplicate stages not allowed"); } if (peStage == 0 || dceStage == 0) { throw Bad(newEntry, "Mixed stages not allowed"); } if (peStage > dceStage) { _sorted = false; } } } }
private void ParseEntry() { _currentEntry = Cache.getEntry(_pointer); byte[] cep = _currentEntry.Path; if (_nextSubtreePos != Tree.getChildCount()) { DirCacheTree s = Tree.getChild(_nextSubtreePos); if (s.contains(cep, PathOffset, cep.Length)) { // The current position is the first file of this subtree. // Use the subtree instead as the current position. // _currentSubtree = s; _nextSubtreePos++; if (s.isValid()) { s.getObjectId().copyRawTo(SubtreeId, 0); } else { SubtreeId.Fill((byte)0); } Mode = FileMode.Tree.Bits; Path = cep; PathLen = PathOffset + s.nameLength(); return; } } // The current position is a file/symlink/gitlink so we // do not have a subtree located here. // Mode = _currentEntry.getRawMode(); Path = cep; PathLen = cep.Length; _currentSubtree = null; }
/// <summary> /// Recursively get all entries within a subtree. /// </summary> /// <param name="path"> /// The subtree path to get all entries within. /// </param> /// <returns> /// All entries recursively contained within the subtree. /// </returns> public DirCacheEntry[] getEntriesWithin(string path) { if (!path.EndsWith("/")) { path += "/"; } byte[] p = Constants.encode(path); int pLen = p.Length; int eIdx = findEntry(p, pLen); if (eIdx < 0) { eIdx = -(eIdx + 1); } int lastIdx = nextEntry(p, pLen, eIdx); var r = new DirCacheEntry[lastIdx - eIdx]; Array.Copy(_sortedEntries, eIdx, r, 0, r.Length); return(r); }
public void testAdd_InGitSortOrder() { DirCache dc = DirCache.read(db); string[] paths = { "a.", "a.b", "a/b", "a0b" }; DirCacheEntry[] ents = new DirCacheEntry[paths.Length]; for (int i = 0; i < paths.Length; i++) ents[i] = new DirCacheEntry(paths[i]); DirCacheBuilder b = dc.builder(); for (int i = 0; i < ents.Length; i++) b.add(ents[i]); b.finish(); Assert.AreEqual(paths.Length, dc.getEntryCount()); for (int i = 0; i < paths.Length; i++) { Assert.AreSame(ents[i], dc.getEntry(i)); Assert.AreEqual(paths[i], dc.getEntry(i).getPathString()); Assert.AreEqual(i, dc.findEntry(paths[i])); Assert.AreSame(ents[i], dc.getEntry(paths[i])); } }
public void testBuildThenClear() { DirCache dc = DirCache.read(db); string[] paths = { "a.", "a.b", "a/b", "a0b" }; var ents = new DirCacheEntry[paths.Length]; for (int i = 0; i < paths.Length; i++) { ents[i] = new DirCacheEntry(paths[i]); } DirCacheBuilder b = dc.builder(); for (int i = 0; i < ents.Length; i++) { b.add(ents[i]); } b.finish(); Assert.AreEqual(paths.Length, dc.getEntryCount()); dc.clear(); Assert.AreEqual(0, dc.getEntryCount()); }
public void testFindSingleFile() { string path = "a-File-path"; DirCache dc = DirCache.read(db); DirCacheBuilder b = dc.builder(); Assert.IsNotNull(b); DirCacheEntry entOrig = new DirCacheEntry(path); Assert.AreNotSame(path, entOrig.getPathString()); Assert.AreEqual(path, entOrig.getPathString()); b.add(entOrig); b.finish(); Assert.AreEqual(1, dc.getEntryCount()); Assert.AreSame(entOrig, dc.getEntry(0)); Assert.AreEqual(0, dc.findEntry(path)); Assert.AreEqual(-1, dc.findEntry("@@-before")); Assert.AreEqual(0, real(dc.findEntry("@@-before"))); Assert.AreEqual(-2, dc.findEntry("a-zoo")); Assert.AreEqual(1, real(dc.findEntry("a-zoo"))); Assert.AreSame(entOrig, dc.getEntry(path)); }
private static int Compare(byte[] aPath, int aLen, DirCacheEntry b) { return Compare(aPath, aLen, b.Path, b.Path.Length); }
/// <summary> /// Recursively get all entries within a subtree. /// </summary> /// <param name="path"> /// The subtree path to get all entries within. /// </param> /// <returns> /// All entries recursively contained within the subtree. /// </returns> public DirCacheEntry[] getEntriesWithin(string path) { if (!path.EndsWith("/")) { path += "/"; } byte[] p = Constants.encode(path); int pLen = p.Length; int eIdx = findEntry(p, pLen); if (eIdx < 0) { eIdx = -(eIdx + 1); } int lastIdx = nextEntry(p, pLen, eIdx); var r = new DirCacheEntry[lastIdx - eIdx]; Array.Copy(_sortedEntries, eIdx, r, 0, r.Length); return r; }
private void ReadFrom(Stream inStream) { var @in = new StreamReader(inStream); MessageDigest md = Constants.newMessageDigest(); // Read the index header and verify we understand it. // var hdr = new byte[20]; IO.ReadFully(inStream, hdr, 0, 12); md.Update(hdr, 0, 12); if (!IsDIRC(hdr)) { throw new CorruptObjectException("Not a DIRC file."); } int ver = NB.DecodeInt32(hdr, 4); if (ver != 2) { throw new CorruptObjectException("Unknown DIRC version " + ver); } _entryCnt = NB.DecodeInt32(hdr, 8); if (_entryCnt < 0) { throw new CorruptObjectException("DIRC has too many entries."); } // Load the individual file entries. // var infos = new byte[InfoLen * _entryCnt]; _sortedEntries = new DirCacheEntry[_entryCnt]; for (int i = 0; i < _entryCnt; i++) { _sortedEntries[i] = new DirCacheEntry(infos, i * InfoLen, inStream, md); } _lastModified = _liveFile.LastWriteTime; // After the file entries are index extensions, and then a footer. // while (true) { var pos = inStream.Position; IO.ReadFully(inStream, hdr, 0, 20); int nextByte = @in.Read(); if (nextByte < 0 || inStream.Position == inStream.Length) { // No extensions present; the file ended where we expected. // break; } inStream.Seek(pos, SeekOrigin.Begin); switch (NB.DecodeInt32(hdr, 0)) { case ExtTree: var raw = new byte[NB.DecodeInt32(hdr, 4)]; md.Update(hdr, 0, 8); IO.skipFully(inStream, 8); IO.ReadFully(inStream, raw, 0, raw.Length); md.Update(raw, 0, raw.Length); _cacheTree = new DirCacheTree(raw, new MutableInteger(), null); break; default: if (hdr[0] >= (byte)'A' && hdr[0] <= (byte)'Z') { // The extension is optional and is here only as // a performance optimization. Since we do not // understand it, we can safely skip past it. // IO.skipFully(inStream, NB.decodeUInt32(hdr, 4)); } else { // The extension is not an optimization and is // _required_ to understand this index format. // Since we did not trap it above we must abort. // throw new CorruptObjectException("DIRC extension '" + Constants.CHARSET.GetString(hdr.Take(4).ToArray()) + "' not supported by this version."); } break; } } byte[] exp = md.Digest(); if (!exp.SequenceEqual(hdr)) { throw new CorruptObjectException("DIRC checksum mismatch"); } }
private void ReadFrom(Stream inStream) { var @in = new StreamReader(inStream); MessageDigest md = Constants.newMessageDigest(); // Read the index header and verify we understand it. // var hdr = new byte[20]; IO.ReadFully(inStream, hdr, 0, 12); md.Update(hdr, 0, 12); if (!IsDIRC(hdr)) { throw new CorruptObjectException("Not a DIRC file."); } int ver = NB.DecodeInt32(hdr, 4); if (ver != 2) { throw new CorruptObjectException("Unknown DIRC version " + ver); } _entryCnt = NB.DecodeInt32(hdr, 8); if (_entryCnt < 0) { throw new CorruptObjectException("DIRC has too many entries."); } // Load the individual file entries. // var infos = new byte[InfoLen * _entryCnt]; _sortedEntries = new DirCacheEntry[_entryCnt]; for (int i = 0; i < _entryCnt; i++) { _sortedEntries[i] = new DirCacheEntry(infos, i * InfoLen, inStream, md); } _lastModified = _liveFile.lastModified(); // After the file entries are index extensions, and then a footer. // while (true) { var pos = inStream.Position; IO.ReadFully(inStream, hdr, 0, 20); if (inStream.ReadByte() < 0) { // No extensions present; the file ended where we expected. // break; } inStream.Seek(pos, SeekOrigin.Begin); md.Update(hdr, 0, 8); IO.skipFully(inStream, 8); long sz = NB.decodeUInt32(hdr, 4); switch (NB.DecodeInt32(hdr, 0)) { case ExtTree: if (int.MaxValue < sz) { throw new CorruptObjectException("DIRC extension " + formatExtensionName(hdr) + " is too large at " + sz + " bytes."); } byte[] raw = new byte[(int)sz]; IO.ReadFully(inStream, raw, 0, raw.Length); md.Update(raw, 0, raw.Length); _cacheTree = new DirCacheTree(raw, new MutableInteger(), null); break; default: if (hdr[0] >= (byte)'A' && hdr[0] <= (byte)'Z') { // The extension is optional and is here only as // a performance optimization. Since we do not // understand it, we can safely skip past it, after // we include its data in our checksum. // skipOptionalExtension(inStream, md, hdr, sz); } else { // The extension is not an optimization and is // _required_ to understand this index format. // Since we did not trap it above we must abort. // throw new CorruptObjectException("DIRC extension " + formatExtensionName(hdr) + " not supported by this version."); } break; } } byte[] exp = md.Digest(); if (!exp.SequenceEqual(hdr)) { throw new CorruptObjectException("DIRC checksum mismatch"); } }
public override void Apply(DirCacheEntry ent) { throw new NotSupportedException("No apply in delete"); }
/// <summary> /// Apply the update to a single cache entry matching the path. /// <para /> /// After apply is invoked the entry is added to the output table, and /// will be included in the new index. /// </summary> /// <param name="ent"> /// The entry being processed. All fields are zeroed out if /// the path is a new path in the index. /// </param> public abstract void Apply(DirCacheEntry ent);
public void testSingleSubtree() { DirCache dc = DirCache.read(db); string[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; DirCacheEntry[] ents = new DirCacheEntry[paths.Length]; for (int i = 0; i < paths.Length; i++) { ents[i] = new DirCacheEntry(paths[i]); ents[i].setFileMode(FileMode.RegularFile); } int aFirst = 1; int aLast = 3; DirCacheBuilder b = dc.builder(); for (int i = 0; i < ents.Length; i++) b.add(ents[i]); b.finish(); Assert.IsNull(dc.getCacheTree(false)); DirCacheTree root = dc.getCacheTree(true); Assert.IsNotNull(root); Assert.AreSame(root, dc.getCacheTree(true)); Assert.AreEqual(string.Empty, root.getNameString()); Assert.AreEqual(string.Empty, root.getPathString()); Assert.AreEqual(1, root.getChildCount()); Assert.AreEqual(dc.getEntryCount(), root.getEntrySpan()); Assert.IsFalse(root.isValid()); DirCacheTree aTree = root.getChild(0); Assert.IsNotNull(aTree); Assert.AreSame(aTree, root.getChild(0)); Assert.AreEqual("a", aTree.getNameString()); Assert.AreEqual("a/", aTree.getPathString()); Assert.AreEqual(0, aTree.getChildCount()); Assert.AreEqual(aLast - aFirst + 1, aTree.getEntrySpan()); Assert.IsFalse(aTree.isValid()); }
public void testWriteReadTree() { DirCache dc = DirCache.Lock(db); string A = string.Format("a%2000s", "a"); string B = string.Format("b%2000s", "b"); string[] paths = { A + ".", A + "." + B, A + "/" + B, A + "0" + B }; var ents = new DirCacheEntry[paths.Length]; for (int i = 0; i < paths.Length; i++) { ents[i] = new DirCacheEntry(paths[i]); ents[i].setFileMode(FileMode.RegularFile); } DirCacheBuilder b = dc.builder(); for (int i = 0; i < ents.Length; i++) { b.add(ents[i]); } b.commit(); DirCache read = DirCache.read(db); Assert.AreEqual(paths.Length, read.getEntryCount()); Assert.AreEqual(1, read.getCacheTree(true).getChildCount()); }
/// <summary> /// Append one entry into the resulting entry list. /// <para /> /// The entry is placed at the end of the entry list. If the entry causes the /// list to now be incorrectly sorted a final sorting phase will be /// automatically enabled within <seealso cref="finish()"/>. /// <para /> /// The internal entry table is automatically expanded if there is /// insufficient space for the new addition. /// </summary> /// <param name="newEntry">the new entry to add.</param> public void add(DirCacheEntry newEntry) { BeforeAdd(newEntry); FastAdd(newEntry); }
public void testTwoLevelSubtree_Recursive() { DirCache dc = DirCache.read(db); FileMode mode = FileMode.RegularFile; string[] paths = { "a.", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" }; var ents = new DirCacheEntry[paths.Length]; for (int i = 0; i < paths.Length; i++) { ents[i] = new DirCacheEntry(paths[i]); ents[i].setFileMode(mode); } DirCacheBuilder b = dc.builder(); for (int i = 0; i < ents.Length; i++) { b.add(ents[i]); } b.finish(); var tw = new GitSharp.Core.TreeWalk.TreeWalk(db); tw.reset(); tw.addTree(new DirCacheIterator(dc)); tw.Recursive = true; int pathIdx = 0; while (tw.next()) { var c = tw.getTree<DirCacheIterator>(0, typeof(DirCacheIterator)); Assert.IsNotNull(c); Assert.AreEqual(pathIdx, c.Pointer); Assert.AreSame(ents[pathIdx], c.getDirCacheEntry()); Assert.AreEqual(paths[pathIdx], tw.getPathString()); Assert.AreEqual(mode.Bits, tw.getRawMode(0)); Assert.AreSame(mode, tw.getFileMode(0)); pathIdx++; } Assert.AreEqual(paths.Length, pathIdx); }
public void testTwoLevelSubtree_FilterPath() { DirCache dc = DirCache.read(db); FileMode mode = FileMode.RegularFile; string[] paths = { "a.", "a/b", "a/c/e", "a/c/f", "a/d", "a0b" }; var ents = new DirCacheEntry[paths.Length]; for (int i = 0; i < paths.Length; i++) { ents[i] = new DirCacheEntry(paths[i]); ents[i].setFileMode(mode); } DirCacheBuilder b = dc.builder(); for (int i = 0; i < ents.Length; i++) { b.add(ents[i]); } b.finish(); var tw = new GitSharp.Core.TreeWalk.TreeWalk(db); for (int victimIdx = 0; victimIdx < paths.Length; victimIdx++) { tw.reset(); tw.addTree(new DirCacheIterator(dc)); tw.setFilter(PathFilterGroup.createFromStrings(new[] { paths[victimIdx] })); tw.Recursive = tw.getFilter().shouldBeRecursive(); Assert.IsTrue(tw.next()); var c = tw.getTree<DirCacheIterator>(0, typeof(DirCacheIterator)); Assert.IsNotNull(c); Assert.AreEqual(victimIdx, c.Pointer); Assert.AreSame(ents[victimIdx], c.getDirCacheEntry()); Assert.AreEqual(paths[victimIdx], tw.getPathString()); Assert.AreEqual(mode.Bits, tw.getRawMode(0)); Assert.AreSame(mode, tw.getFileMode(0)); Assert.IsFalse(tw.next()); } }
public void testNoSubtree_WithTreeWalk() { DirCache dc = DirCache.read(db); string[] paths = { "a.", "a0b" }; FileMode[] modes = { FileMode.ExecutableFile, FileMode.GitLink }; var ents = new DirCacheEntry[paths.Length]; for (int i = 0; i < paths.Length; i++) { ents[i] = new DirCacheEntry(paths[i]); ents[i].setFileMode(modes[i]); } DirCacheBuilder b = dc.builder(); for (int i = 0; i < ents.Length; i++) { b.add(ents[i]); } b.finish(); var iter = new DirCacheIterator(dc); var tw = new GitSharp.Core.TreeWalk.TreeWalk(db); tw.reset(); tw.addTree(iter); int pathIdx = 0; while (tw.next()) { Assert.AreSame(iter, tw.getTree<DirCacheIterator>(0, typeof(DirCacheIterator))); Assert.AreEqual(pathIdx, iter.Pointer); Assert.AreSame(ents[pathIdx], iter.getDirCacheEntry()); Assert.AreEqual(paths[pathIdx], tw.getPathString()); Assert.AreEqual(modes[pathIdx].Bits, tw.getRawMode(0)); Assert.AreSame(modes[pathIdx], tw.getFileMode(0)); pathIdx++; } Assert.AreEqual(paths.Length, pathIdx); }
private DirCacheEntry MakeEntry(string path, FileMode mode, String content) { var ent = new DirCacheEntry(path); ent.setFileMode(mode); byte[] contentBytes = Constants.encode(content); ent.setObjectId(new ObjectWriter(db).ComputeBlobSha1(contentBytes.Length, new MemoryStream(contentBytes))); return ent; }
/// <summary> /// Create a new update command for an existing entry instance. /// </summary> /// <param name="ent"> /// Entry instance to match path of. Only the path of this /// entry is actually considered during command evaluation. /// </param> protected PathEdit(DirCacheEntry ent) { _path = ent.Path; }
private static int Compare(byte[] aPath, int aLen, DirCacheEntry b) { return(Compare(aPath, aLen, b.Path, b.Path.Length)); }
/// <summary> /// Create a new deletion command for an existing entry instance. /// </summary> /// <param name="ent"> /// Entry instance to remove. Only the path of this entry is /// actually considered during command evaluation. /// </param> public DeletePath(DirCacheEntry ent) : base(ent) { }
/// <summary> /// Create a new unmerged path exception. /// </summary> /// <param name="entry">The first non-zero stage of the unmerged path.</param> /// <param name="inner">Inner Exception.</param> public UnmergedPathException(DirCacheEntry entry, Exception inner) : base("Unmerged path: " + entry.getPathString(), inner) { _entry = entry; }
public void replace(DirCacheEntry[] e, int cnt) { _sortedEntries = e; _entryCnt = cnt; _cacheTree = null; }
public static int Compare(DirCacheEntry a, DirCacheEntry b) { return Compare(a.Path, a.Path.Length, b); }
public void toArray(int i, DirCacheEntry[] dst, int off, int cnt) { Array.Copy(_sortedEntries, i, dst, off, cnt); }
public void testSetFileMode() { DirCacheEntry e = new DirCacheEntry("a"); Assert.AreEqual(0, e.getRawMode()); e.setFileMode(FileMode.RegularFile); Assert.AreSame(FileMode.RegularFile, e.getFileMode()); Assert.AreEqual(FileMode.RegularFile.Bits, e.getRawMode()); e.setFileMode(FileMode.ExecutableFile); Assert.AreSame(FileMode.ExecutableFile, e.getFileMode()); Assert.AreEqual(FileMode.ExecutableFile.Bits, e.getRawMode()); e.setFileMode(FileMode.Symlink); Assert.AreSame(FileMode.Symlink, e.getFileMode()); Assert.AreEqual(FileMode.Symlink.Bits, e.getRawMode()); e.setFileMode(FileMode.GitLink); Assert.AreSame(FileMode.GitLink, e.getFileMode()); Assert.AreEqual(FileMode.GitLink.Bits, e.getRawMode()); try { e.setFileMode(FileMode.Missing); Assert.Fail("incorrectly accepted FileMode.MISSING"); } catch (ArgumentException err) { Assert.AreEqual("Invalid mode 0 for path a", err.Message); } try { e.setFileMode(FileMode.Tree); Assert.Fail("incorrectly accepted FileMode.TREE"); } catch (ArgumentException err) { Assert.AreEqual("Invalid mode " + FileMode.TYPE_TREE + " for path a", err.Message); } }
public void testBuildOneFile_FinishWriteCommit() { string path = "a-File-path"; var mode = GitSharp.Core.FileMode.RegularFile; long lastModified = 1218123387057L; int Length = 1342; DirCacheEntry entOrig; DirCache dc = DirCache.Lock(db); DirCacheBuilder b = dc.builder(); Assert.IsNotNull(b); entOrig = new DirCacheEntry(path); entOrig.setFileMode(mode); entOrig.setLastModified(lastModified); entOrig.setLength(Length); Assert.AreNotSame(path, entOrig.getPathString()); Assert.AreEqual(path, entOrig.getPathString()); Assert.AreEqual(ObjectId.ZeroId, entOrig.getObjectId()); Assert.AreEqual(mode.Bits, entOrig.getRawMode()); Assert.AreEqual(0, entOrig.getStage()); Assert.AreEqual(lastModified, entOrig.getLastModified()); Assert.AreEqual(Length, entOrig.getLength()); Assert.IsFalse(entOrig.isAssumeValid()); b.add(entOrig); b.finish(); Assert.AreEqual(1, dc.getEntryCount()); Assert.AreSame(entOrig, dc.getEntry(0)); dc.write(); Assert.IsTrue(dc.commit()); dc = DirCache.read(db); Assert.AreEqual(1, dc.getEntryCount()); DirCacheEntry entRead = dc.getEntry(0); Assert.AreNotSame(entOrig, entRead); Assert.AreEqual(path, entRead.getPathString()); Assert.AreEqual(ObjectId.ZeroId, entOrig.getObjectId()); Assert.AreEqual(mode.Bits, entOrig.getRawMode()); Assert.AreEqual(0, entOrig.getStage()); Assert.AreEqual(lastModified, entOrig.getLastModified()); Assert.AreEqual(Length, entOrig.getLength()); Assert.IsFalse(entOrig.isAssumeValid()); }
public static int Compare(DirCacheEntry a, DirCacheEntry b) { return(Compare(a.Path, a.Path.Length, b)); }
public void testSingleSubtree_NoRecursion() { DirCache dc = DirCache.read(db); string[] paths = { "a.", "a/b", "a/c", "a/d", "a0b" }; var ents = new DirCacheEntry[paths.Length]; for (int i = 0; i < paths.Length; i++) { ents[i] = new DirCacheEntry(paths[i]); ents[i].setFileMode(FileMode.RegularFile); } DirCacheBuilder b = dc.builder(); for (int i = 0; i < ents.Length; i++) { b.add(ents[i]); } b.finish(); string[] expPaths = { "a.", "a", "a0b" }; FileMode[] expModes = { FileMode.RegularFile, FileMode.Tree, FileMode.RegularFile }; var expPos = new[] { 0, -1, 4 }; var iter = new DirCacheIterator(dc); var tw = new GitSharp.Core.TreeWalk.TreeWalk(db); tw.reset(); tw.addTree(iter); tw.Recursive = false; int pathIdx = 0; while (tw.next()) { Assert.AreSame(iter, tw.getTree<DirCacheIterator>(0, typeof(DirCacheIterator))); Assert.AreEqual(expModes[pathIdx].Bits, tw.getRawMode(0)); Assert.AreSame(expModes[pathIdx], tw.getFileMode(0)); Assert.AreEqual(expPaths[pathIdx], tw.getPathString()); if (expPos[pathIdx] >= 0) { Assert.AreEqual(expPos[pathIdx], iter.Pointer); Assert.AreSame(ents[expPos[pathIdx]], iter.getDirCacheEntry()); } else { Assert.AreSame(FileMode.Tree, tw.getFileMode(0)); } pathIdx++; } Assert.AreEqual(expPaths.Length, pathIdx); }
private static InvalidOperationException Bad(DirCacheEntry a, string msg) { return(new InvalidOperationException(msg + ": " + a.getStage() + " " + a.getPathString())); }