private FileSystemTaskResult AddToProjection(GitIndexEntry data) { if (data.SkipWorktree || data.MergeState == MergeStage.Yours) { data.ParsePath(); this.projection.AddItemFromIndexEntry(data); } else { data.ClearLastParent(); } return(FileSystemTaskResult.Success); }
public void ClearLastParent() { string[] pathParts = new[] { "folder", "one", "file.txt" }; GitIndexEntry indexEntry = this.SetupIndexEntry(string.Join("/", pathParts)); this.TestPathParts(indexEntry, pathParts, hasSameParent: false); string[] pathParts2 = new[] { "folder", "one", "newfile.txt" }; this.ParsePathForIndexEntry(indexEntry, string.Join("/", pathParts2), replaceIndex: 12); this.TestPathParts(indexEntry, pathParts2, hasSameParent: true); indexEntry.LastParent = new FolderData(); indexEntry.ClearLastParent(); indexEntry.HasSameParentAsLastEntry.ShouldBeFalse(); indexEntry.LastParent.ShouldBeNull(); }
private FileSystemTaskResult AddIndexEntryToProjection(GitIndexEntry data) { // Never want to project the common ancestor even if the skip worktree bit is on if ((data.MergeState != MergeStage.CommonAncestor && data.SkipWorktree) || data.MergeState == MergeStage.Yours) { data.BuildingProjection_ParsePath(); this.projection.AddItemFromIndexEntry(data); } else { data.ClearLastParent(); } return(FileSystemTaskResult.Success); }
private FileSystemTaskResult AddToModifiedFiles(GitIndexEntry data) { if (!data.SkipWorktree) { // A git command (e.g. 'git reset --mixed') may have cleared a file's skip worktree bit without // triggering an update to the projection. Ensure this file is in GVFS's modified files database data.ParsePath(); return(this.projection.AddModifiedPath(data.GetFullPath())); } else { data.ClearLastParent(); } return(FileSystemTaskResult.Success); }
/// <summary> /// Takes an action on a GitIndexEntry using the index in indexStream /// </summary> /// <param name="indexStream">Stream for reading a git index file</param> /// <param name="entryAction">Action to take on each GitIndexEntry from the index</param> /// <returns> /// FileSystemTaskResult indicating success or failure of the specified action /// </returns> /// <remarks> /// Only the AddToModifiedFiles method because it updates the modified paths file can result /// in TryIndexAction returning a FileSystemTaskResult other than Success. All other actions result in success (or an exception in the /// case of a corrupt index) /// </remarks> private FileSystemTaskResult ParseIndex( ITracer tracer, Stream indexStream, GitIndexEntry resuableParsedIndexEntry, Func <GitIndexEntry, FileSystemTaskResult> entryAction) { this.indexStream = indexStream; this.indexStream.Position = 0; this.ReadNextPage(); if (this.page[0] != 'D' || this.page[1] != 'I' || this.page[2] != 'R' || this.page[3] != 'C') { throw new InvalidDataException("Incorrect magic signature for index: " + string.Join(string.Empty, this.page.Take(4).Select(c => (char)c))); } this.Skip(4); uint indexVersion = this.ReadFromIndexHeader(); if (indexVersion != 4) { throw new InvalidDataException("Unsupported index version: " + indexVersion); } uint entryCount = this.ReadFromIndexHeader(); // Don't want to flood the logs on large indexes so only log every 500ms const int LoggingTicksThreshold = 5000000; long nextLogTicks = DateTime.UtcNow.Ticks + LoggingTicksThreshold; SortedFolderEntries.InitializePools(tracer, entryCount); LazyUTF8String.InitializePools(tracer, entryCount); resuableParsedIndexEntry.ClearLastParent(); int previousPathLength = 0; bool parseMode = GVFSPlatform.Instance.FileSystem.SupportsFileMode; FileSystemTaskResult result = FileSystemTaskResult.Success; for (int i = 0; i < entryCount; i++) { if (parseMode) { this.Skip(26); // 4-bit object type // valid values in binary are 1000(regular file), 1010(symbolic link) and 1110(gitlink) // 3-bit unused // 9-bit unix permission. Only 0755 and 0644 are valid for regular files. (Legacy repos can also contain 664) // Symbolic links and gitlinks have value 0 in this field. ushort indexFormatTypeAndMode = this.ReadUInt16(); FileTypeAndMode typeAndMode = new FileTypeAndMode(indexFormatTypeAndMode); switch (typeAndMode.Type) { case FileType.Regular: if (typeAndMode.Mode != FileMode755 && typeAndMode.Mode != FileMode644 && typeAndMode.Mode != FileMode664) { throw new InvalidDataException($"Invalid file mode {typeAndMode.GetModeAsOctalString()} found for regular file in index"); } break; case FileType.SymLink: case FileType.GitLink: if (typeAndMode.Mode != 0) { throw new InvalidDataException($"Invalid file mode {typeAndMode.GetModeAsOctalString()} found for link file({typeAndMode.Type:X}) in index"); } break; default: throw new InvalidDataException($"Invalid file type {typeAndMode.Type:X} found in index"); } resuableParsedIndexEntry.TypeAndMode = typeAndMode; this.Skip(12); } else { this.Skip(40); } this.ReadSha(resuableParsedIndexEntry); ushort flags = this.ReadUInt16(); if (flags == 0) { throw new InvalidDataException("Invalid flags found in index"); } resuableParsedIndexEntry.MergeState = (MergeStage)((flags >> 12) & 3); bool isExtended = (flags & ExtendedBit) == ExtendedBit; resuableParsedIndexEntry.PathLength = (ushort)(flags & 0xFFF); resuableParsedIndexEntry.SkipWorktree = false; if (isExtended) { ushort extendedFlags = this.ReadUInt16(); resuableParsedIndexEntry.SkipWorktree = (extendedFlags & SkipWorktreeBit) == SkipWorktreeBit; } int replaceLength = this.ReadReplaceLength(); resuableParsedIndexEntry.ReplaceIndex = previousPathLength - replaceLength; int bytesToRead = resuableParsedIndexEntry.PathLength - resuableParsedIndexEntry.ReplaceIndex + 1; this.ReadPath(resuableParsedIndexEntry, resuableParsedIndexEntry.ReplaceIndex, bytesToRead); previousPathLength = resuableParsedIndexEntry.PathLength; result = entryAction.Invoke(resuableParsedIndexEntry); if (result != FileSystemTaskResult.Success) { return(result); } if (DateTime.UtcNow.Ticks > nextLogTicks) { tracer.RelatedInfo($"{i}/{entryCount} index entries parsed."); nextLogTicks = DateTime.UtcNow.Ticks + LoggingTicksThreshold; } } tracer.RelatedInfo($"Finished parsing {entryCount} index entries."); return(result); }