public static ObjectHash WriteFixedTree(string vcsPath, Tree tree) { var resultingTreeLines = new List <Tree.TreeLine>(); bool fixRequired = false; foreach (var treeLine in tree.Lines) { if (!treeLine.IsDirectory()) { resultingTreeLines.Add(treeLine); continue; } var childTree = GitObjectFactory.ReadTree(vcsPath, treeLine.Hash); var fixedTreeHash = WriteFixedTree(vcsPath, childTree); resultingTreeLines.Add(new Tree.TreeLine(treeLine.TextBytes, fixedTreeHash)); if (fixedTreeHash != childTree.Hash) { fixRequired = true; } } if (fixRequired || Tree.HasDuplicateLines(resultingTreeLines)) { tree = Tree.GetFixedTree(resultingTreeLines); HashContent.WriteObject(vcsPath, tree); } return(tree.Hash); }
private static ConcurrentStack <Commit> ReadCommitsFromRefs(string vcsPath) { var refs = Refs.ReadAll(vcsPath); var addedCommits = new ConcurrentDictionary <ObjectHash, bool>(); var result = new ConcurrentStack <Commit>(); Parallel.ForEach(refs, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, @ref => { var gitObject = @ref is TagRef tag ? GitObjectFactory.ReadGitObject(vcsPath, new ObjectHash(tag.CommitHash)) : GitObjectFactory.ReadGitObject(vcsPath, new ObjectHash(@ref.Hash)); while (gitObject is Tag tagObject) { gitObject = GitObjectFactory.ReadGitObject(vcsPath, new ObjectHash(tagObject.Object)); } // Tags pointing to trees are ignored if (gitObject.Type == GitObjectType.Commit && addedCommits.TryAdd(gitObject.Hash, true)) { result.Push((Commit)gitObject); } }); return(result); }
private static bool HasDefectiveTree(string vcsPath, Commit commit) { if (SeenTrees.TryGetValue(commit.TreeHash, out bool isDefective)) { return(isDefective); } var tree = GitObjectFactory.ReadTree(vcsPath, commit.TreeHash); return(IsDefectiveTree(vcsPath, tree)); }
public static IEnumerable <Commit> CommitsRandomOrder(string vcsPath) { var commitsAlreadySeen = new HashSet <ObjectHash>(); var commits = ReadCommitsFromRefs(vcsPath); while (commits.TryPop(out var commit)) { if (!commitsAlreadySeen.Add(commit.Hash)) { continue; } yield return(commit); foreach (var parent in commit.Parents.Where(parent => !commitsAlreadySeen.Contains(parent))) { commits.Push(GitObjectFactory.ReadCommit(vcsPath, parent)); } } }
private static ObjectHash RewriteRef(string vcsPath, string hash, string refName, Dictionary <ObjectHash, ObjectHash> rewrittenCommits) { var gitObject = GitObjectFactory.ReadGitObject(vcsPath, new ObjectHash(hash)); if (gitObject.Type == GitObjectType.Commit) { var path = Path.Combine(vcsPath, refName); var correctedHash = GetRewrittenCommitHash(new ObjectHash(hash), rewrittenCommits); Directory.CreateDirectory(Path.GetDirectoryName(path)); File.WriteAllText(path, correctedHash.ToString()); return(correctedHash); } if (gitObject.Type == GitObjectType.Tag) { var tag = (Tag)gitObject; if (tag.PointsToTree) { // Do not touch tags pointing to trees right now as this is not properly implemented yet return(tag.Hash); } var rewrittenObjectHash = tag.PointsToTag ? RewriteRef(vcsPath, tag.Object, "", rewrittenCommits) : GetRewrittenCommitHash(new ObjectHash(tag.Object), rewrittenCommits); // points to commit var rewrittenTag = tag.WithNewObject(rewrittenObjectHash.ToString()); HashContent.WriteObject(vcsPath, rewrittenTag); var path = Path.Combine(vcsPath, "refs/tags", rewrittenTag.TagName); File.WriteAllText(path, rewrittenTag.Hash.ToString()); return(rewrittenTag.Hash); } throw new NotImplementedException(); }
public static IEnumerable <Commit> CommitsInOrder(string vcsPath) { var commits = ReadCommitsFromRefs(vcsPath); var parentsSeen = new HashSet <ObjectHash>(); var commitsProcessed = new HashSet <ObjectHash>(); while (commits.TryPop(out var commit)) { if (commitsProcessed.Contains(commit.Hash)) { parentsSeen.Remove(commit.Hash); } else { if (!parentsSeen.Add(commit.Hash) || !commit.HasParents) { commitsProcessed.Add(commit.Hash); yield return(commit); } else { commits.Push(commit); foreach (var parent in commit.Parents) { if (!commitsProcessed.Contains(parent)) { var parentCommit = GitObjectFactory.ReadCommit(vcsPath, parent); if (parentCommit == null) { throw new Exception("Commit not found: " + parent); } commits.Push(parentCommit); } } } } } }
public static bool IsDefectiveTree(string vcsPath, Tree tree) { if (SeenTrees.TryGetValue(tree.Hash, out bool isDefective)) { return(isDefective); } if (Tree.HasDuplicateLines(tree.Lines)) { SeenTrees.TryAdd(tree.Hash, true); return(true); } var childTrees = tree.GetDirectories(); foreach (var childTree in childTrees) { if (SeenTrees.TryGetValue(childTree.Hash, out isDefective)) { if (isDefective) { return(true); } continue; } var childTreeObject = (Tree)GitObjectFactory.ReadGitObject(vcsPath, childTree.Hash); if (IsDefectiveTree(vcsPath, childTreeObject)) { return(true); } } SeenTrees.TryAdd(tree.Hash, false); return(false); }
static Dictionary <ObjectHash, ObjectHash> FixDefectiveCommits(string vcsPath, List <ObjectHash> defectiveCommits) { var rewrittenCommitHashes = new Dictionary <ObjectHash, ObjectHash>(); foreach (var commit in CommitWalker.CommitsInOrder(vcsPath)) { if (rewrittenCommitHashes.ContainsKey(commit.Hash)) { continue; } // Rewrite this commit byte[] newCommitBytes; if (defectiveCommits.Contains(commit.Hash)) { var fixedTreeHash = WriteFixedTree(vcsPath, GitObjectFactory.ReadTree(vcsPath, commit.TreeHash)); newCommitBytes = Commit.GetSerializedCommitWithChangedTreeAndParents(commit, fixedTreeHash, CorrectParents(commit.Parents, rewrittenCommitHashes).ToList()); } else { newCommitBytes = Commit.GetSerializedCommitWithChangedTreeAndParents(commit, commit.TreeHash, CorrectParents(commit.Parents, rewrittenCommitHashes).ToList()); } var fileObjectBytes = GitObjectFactory.GetBytesWithHeader(GitObjectType.Commit, newCommitBytes); var newCommitHash = new ObjectHash(Hash.Create(fileObjectBytes)); if (newCommitHash != commit.Hash && !rewrittenCommitHashes.ContainsKey(commit.Hash)) { HashContent.WriteFile(vcsPath, fileObjectBytes, newCommitHash.ToString()); rewrittenCommitHashes.Add(commit.Hash, newCommitHash); } } return(rewrittenCommitHashes); }