private void LazyLoadTree(IList <TreeEntryModel> entries, Commit commit, string path, double scale) { var tree = string.IsNullOrEmpty(path) ? commit.Tree : commit[path].Target as Tree; var sha = tree.Sha; if (GitCache.Exists(sha, "summary")) { return; } var job = new SingleJob(() => { Task.Delay(1000).Wait(); // let's the page going on using (var repo = new Repository(_repositoryPath)) { var ancestors = repo.Commits.QueryBy(new CommitFilter { Since = commit, SortBy = CommitSortStrategies.Topological }); var summary = CalculateRevisionSummary(entries, ancestors, null); GitCache.Set(sha, "summary", summary, true); } }, scale / 10000); Scheduler.Instance.AddJob(job, string.Format("LazyLoadTree {0}/{1}/{2}", this.Name, commit.Sha, path)); }
public BranchesModel GetBranches() { var head = _repository.Head; if (head.Tip == null) { return(new BranchesModel()); } var sha = CalcBranchesSha(); var aheadBehinds = GitCache.Get <RevisionSummaryCacheItem[]>(sha, "branches"); if (aheadBehinds == null) { aheadBehinds = _repository.Branches .Where(s => s != head && s.Name != "HEAD") .OrderByDescending(s => s.Tip.Author.When) .Select(branch => { var commit = branch.Tip; var divergence = _repository.ObjectDatabase.CalculateHistoryDivergence(commit, head.Tip); return(new RevisionSummaryCacheItem { Ahead = divergence.AheadBy ?? 0, Behind = divergence.BehindBy ?? 0, Name = branch.Name, Sha = commit.Sha, AuthorName = commit.Author.Name, AuthorEmail = commit.Author.Email, AuthorWhen = commit.Author.When, CommitterName = commit.Committer.Name, CommitterEmail = commit.Committer.Email, CommitterWhen = commit.Committer.When, }); }) .ToArray(); GitCache.Set(sha, "branches", aheadBehinds); } var model = new BranchesModel { Commit = ToCommitModel(head.Tip, head.Name), AheadBehinds = aheadBehinds.Select(s => new AheadBehindModel { Ahead = s.Ahead, Behind = s.Behind, Commit = new CommitModel { ReferenceName = s.Name, Author = new Signature(s.AuthorName, s.AuthorEmail, s.AuthorWhen), Committer = new Signature(s.CommitterName, s.CommitterEmail, s.CommitterWhen), }, }).ToArray(), }; return(model); }
public ContributorsModel GetContributors(string path) { string referenceName; var commit = GetCommitByPath(ref path, out referenceName); if (commit == null) { return(null); } var ancestors = _repository.Commits .QueryBy(new CommitFilter { Since = commit }); var contributors = GitCache.Get <ContributorCommitsModel[]>(commit.Sha, "contributors"); if (contributors == null) { contributors = ancestors.GroupBy(s => s.Author.ToString()) .Select(s => new ContributorCommitsModel { Author = s.Key, CommitsCount = s.Count(), }) .OrderByDescending(s => s.CommitsCount) .ThenBy(s => s.Author, new StringLogicalComparer()) .ToArray(); GitCache.Set(commit.Sha, "contributors", contributors); } var statistics = new RepositoryStatisticsModel(); var stats = GitCache.Get <RepositoryStatisticsModel.Statistics>(commit.Sha, "statistics"); int size; if (stats == null) { stats = new RepositoryStatisticsModel.Statistics { Branch = referenceName, Commits = ancestors.Count(), Contributors = ancestors.Select(s => s.Author.ToString()).Distinct().Count(), Files = FilesInCommit(commit, out size), SourceSize = size, }; GitCache.Set(commit.Sha, "statistics", stats); } statistics.Current = stats; if (_repository.Head.Tip != commit) { commit = _repository.Head.Tip; stats = GitCache.Get <RepositoryStatisticsModel.Statistics>(commit.Sha, "statistics"); if (stats == null) { ancestors = _repository.Commits.QueryBy(new CommitFilter { Since = commit }); stats = new RepositoryStatisticsModel.Statistics { Branch = _repository.Head.Name, Commits = ancestors.Count(), Contributors = ancestors.Select(s => s.Author.ToString()).Distinct().Count(), Files = FilesInCommit(commit, out size), SourceSize = size, }; GitCache.Set(commit.Sha, "statistics", stats); } statistics.Default = stats; } var sha = CalcBranchesSha(true); var sizeOfRepo = GitCache.Get <long>(sha, "size"); if (sizeOfRepo == 0) { sizeOfRepo = SizeOfRepository(); GitCache.Set(sha, "size", sizeOfRepo); } statistics.RepositorySize = sizeOfRepo; var model = new ContributorsModel { RepositoryName = Name, Contributors = contributors, Statistics = statistics, }; return(model); }
public TreeModel GetTree(string path) { var isEmptyPath = string.IsNullOrEmpty(path); string referenceName; var commit = GetCommitByPath(ref path, out referenceName); if (commit == null) { if (isEmptyPath) { var branch = _repository.Branches["master"] ?? _repository.Branches.FirstOrDefault(); return(new TreeModel { ReferenceName = branch == null ? "HEAD" : branch.Name, }); } return(null); } var model = new TreeModel { ReferenceName = referenceName, Path = string.IsNullOrEmpty(path) ? "" : path, Commit = new CommitModel { Sha = commit.Sha, Author = commit.Author, Committer = commit.Committer, CommitMessageShort = commit.MessageShort.RepetitionIfEmpty(NoCommitMessage), Parents = commit.Parents.Select(s => s.Sha).ToArray() }, }; var tree = string.IsNullOrEmpty(path) ? commit.Tree : commit[path] == null ? null : commit[path].Target as Tree; if (tree == null) { return(null); } IEnumerable <Commit> ancestors = _repository.Commits .QueryBy(new CommitFilter { Since = commit, SortBy = CommitSortStrategies.Topological }); var scope = GitCache.Get <RepositoryScope>(commit.Sha, "scope"); if (scope == null) { ancestors = ancestors.ToList(); scope = new RepositoryScope { Commits = ancestors.Count(), Contributors = ancestors.Select(s => s.Author.ToString()).Distinct().Count(), }; GitCache.Set(commit.Sha, "scope", scope); } var entries = tree .OrderBy(s => s.TargetType == TreeEntryTargetType.Blob) .ThenBy(s => s.Name, new StringLogicalComparer()) .Select(s => new TreeEntryModel { Name = s.Name, ReferenceName = referenceName, Path = s.Path.Replace('\\', '/'), Commit = new CommitModel { CommitMessageShort = "???", Author = new Signature("???", "???", DateTimeOffset.MinValue), }, Sha = s.Target.Sha, EntryType = s.TargetType, }) .ToList(); var summary = GitCache.Get <RevisionSummaryCacheItem[]>(tree.Sha, "summary"); var missing = summary == null; double scale = scope.Commits * tree.Count; if ((missing || summary.Length == 0) && scale > 20000) { LazyLoadTree(entries, commit, path, scale); } else { summary = CalculateRevisionSummary(entries, ancestors, summary); } if (missing) { GitCache.Set(tree.Sha, "summary", summary ?? new RevisionSummaryCacheItem[0]); } model.Entries = entries; model.Readme = entries.FirstOrDefault(s => s.EntryType == TreeEntryTargetType.Blob && (string.Equals(s.Name, "readme", StringComparison.OrdinalIgnoreCase) //|| string.Equals(s.Name, "readme.txt", StringComparison.OrdinalIgnoreCase) || string.Equals(s.Name, "readme.md", StringComparison.OrdinalIgnoreCase))); if (model.Readme != null) { var data = ((Blob)tree[model.Readme.Name].Target).GetContentStream().ToBytes(); var encoding = FileHelper.DetectEncoding(data, CpToEncoding(commit.Encoding), _i18n.Value); if (encoding == null) { model.Readme.BlobType = BlobType.Binary; } else { model.Readme.BlobType = model.Readme.Name.EndsWith(".md", StringComparison.OrdinalIgnoreCase) ? BlobType.MarkDown : BlobType.Text; model.Readme.TextContent = FileHelper.ReadToEnd(data, encoding); model.Readme.TextBrush = "no-highlight"; } } model.BranchSelector = GetBranchSelectorModel(referenceName, commit.Sha, path); model.PathBar = new PathBarModel { Name = Name, Action = "Tree", Path = path, ReferenceName = referenceName, ReferenceSha = commit.Sha, HideLastSlash = false, }; if (model.IsRoot) { scope.Branches = _repository.Branches.Count(); scope.Tags = _repository.Tags.Count(); model.Scope = scope; } return(model); }
public string GetArchiveFilename(string path, string newline, out string referenceName) { var commit = GetCommitByPath(ref path, out referenceName); if (commit == null) { return(null); } if (referenceName == null) { referenceName = commit.Sha; } var key = "archive"; if (newline != null) { key += newline.GetHashCode().ToString("x"); } bool exist; var filename = GitCache.GetCacheFilename(commit.Sha, key, out exist, true); if (exist) { return(filename); } using (var zipOutputStream = new ZipOutputStream(new FileStream(filename, FileMode.Create))) { var stack = new Stack <Tree>(); stack.Push(commit.Tree); while (stack.Count != 0) { var tree = stack.Pop(); foreach (var entry in tree) { byte[] bytes; switch (entry.TargetType) { case TreeEntryTargetType.Blob: zipOutputStream.PutNextEntry(new ZipEntry(entry.Path)); var blob = (Blob)entry.Target; bytes = blob.GetContentStream().ToBytes(); if (newline == null) { zipOutputStream.Write(bytes, 0, bytes.Length); } else { var encoding = FileHelper.DetectEncoding(bytes, CpToEncoding(commit.Encoding), _i18n.Value); if (encoding == null) { zipOutputStream.Write(bytes, 0, bytes.Length); } else { bytes = FileHelper.ReplaceNewline(bytes, encoding, newline); zipOutputStream.Write(bytes, 0, bytes.Length); } } break; case TreeEntryTargetType.Tree: stack.Push((Tree)entry.Target); break; case TreeEntryTargetType.GitLink: zipOutputStream.PutNextEntry(new ZipEntry(entry.Path + "/.gitsubmodule")); bytes = Encoding.ASCII.GetBytes(entry.Target.Sha); zipOutputStream.Write(bytes, 0, bytes.Length); break; } } } zipOutputStream.SetComment(commit.Sha); } return(filename); }
public BlameModel GetBlame(string path) { string referenceName; var commit = GetCommitByPath(ref path, out referenceName); if (commit == null) { return(null); } var entry = commit[path]; if (entry == null || entry.TargetType != TreeEntryTargetType.Blob) { return(null); } var blob = (Blob)entry.Target; var bytes = blob.GetContentStream().ToBytes(); var encoding = FileHelper.DetectEncoding(bytes, CpToEncoding(commit.Encoding), _i18n.Value); if (encoding == null) { return(null); } var code = FileHelper.ReadToEnd(bytes, encoding); var reader = new StringReader(code); var hunks = GitCache.Get <BlameHunkModel[]>(blob.Sha, "blame"); if (hunks == null) { var blame = _repository.Blame(path, new BlameOptions { StartingAt = commit }); hunks = blame.Select(s => new BlameHunkModel { Code = reader.ReadLines(s.LineCount), StartLine = s.FinalStartLineNumber, EndLine = s.LineCount, MessageShort = s.FinalCommit.MessageShort.RepetitionIfEmpty(NoCommitMessage), Sha = s.FinalCommit.Sha, Author = s.FinalCommit.Author.Name, AuthorEmail = s.FinalCommit.Author.Email, AuthorDate = s.FinalCommit.Author.When, }) .ToArray(); GitCache.Set(blob.Sha, "blame", hunks); } var model = new BlameModel { ReferenceName = referenceName, Sha = commit.Sha, Path = string.IsNullOrEmpty(path) ? "" : path, SizeString = FileHelper.GetSizeString(blob.Size), Brush = FileHelper.GetBrush(path), Hunks = hunks, BranchSelector = GetBranchSelectorModel(referenceName, commit.Sha, path), PathBar = new PathBarModel { Name = Name, Action = "Tree", Path = path, ReferenceName = referenceName, ReferenceSha = commit.Sha, HideLastSlash = true, }, }; return(model); }
public CommitsModel GetCommits(string path, int page = 1, int pagesize = 20) { string referenceName; var commit = GetCommitByPath(ref path, out referenceName); if (commit == null) { return(null); } var tree = commit[path]; if (!string.IsNullOrEmpty(path) && tree == null) { return(null); } var cacheKey = commit.Sha; if (tree != null) { cacheKey += "-" + tree.Target.Sha; } var commits = GitCache.Get <RevisionSummaryCacheItem[]>(cacheKey, "commits"); if (commits == null) { var ancestors = _repository.Commits .QueryBy(new CommitFilter { Since = commit, SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Time }) .PathFilter(path) .ToList(); commits = ancestors.Select(s => new RevisionSummaryCacheItem { Sha = s.Sha, MessageShort = s.MessageShort.RepetitionIfEmpty(NoCommitMessage), AuthorName = s.Author.Name, AuthorEmail = s.Author.Email, AuthorWhen = s.Author.When, CommitterName = s.Committer.Name, CommitterEmail = s.Committer.Email, CommitterWhen = s.Committer.When, }).ToArray(); GitCache.Set(cacheKey, "commits", commits); } var model = new CommitsModel { ReferenceName = referenceName, Sha = commit.Sha, Commits = commits .Skip((page - 1) * pagesize) .Take(pagesize) .Select(s => new CommitModel { CommitMessageShort = s.MessageShort, Sha = s.Sha, Author = new Signature(s.AuthorName, s.AuthorEmail, s.AuthorWhen), Committer = new Signature(s.CommitterName, s.CommitterEmail, s.CommitterWhen), }) .ToList(), CurrentPage = page, ItemCount = commits.Count(), Path = string.IsNullOrEmpty(path) ? "" : path, PathBar = new PathBarModel { Name = Name, Action = "Commits", Path = path, ReferenceName = referenceName, ReferenceSha = commit.Sha, HideLastSlash = path != "" && commit[path].TargetType == TreeEntryTargetType.Blob, }, }; return(model); }
public TreeEntryModel GetBlob(string path) { string referenceName; var commit = GetCommitByPath(ref path, out referenceName); if (commit == null) { return(null); } var entry = commit[path]; if (entry == null || entry.TargetType != TreeEntryTargetType.Blob) { return(null); } var blob = (Blob)entry.Target; var lastCommitSha = GitCache.Get <string>(blob.Sha, "affectedcommit"); if (lastCommitSha == null) { var hs = new HashSet <string>(); var queue = new Queue <Commit>(); queue.Enqueue(commit); hs.Add(commit.Sha); while (queue.Count > 0) { commit = queue.Dequeue(); var has = false; foreach (var parent in commit.Parents) { var tree = parent[path]; if (tree == null) { continue; } var eq = tree.Target.Sha == blob.Sha; if (eq && hs.Add(parent.Sha)) { queue.Enqueue(parent); } has = has || eq; } if (!has) { break; } } lastCommitSha = commit.Sha; GitCache.Set(blob.Sha, "affectedcommit", lastCommitSha); } if (lastCommitSha != commit.Sha) { commit = _repository.Lookup <Commit>(lastCommitSha); } var data = blob.GetContentStream().ToBytes(); var encoding = FileHelper.DetectEncoding(data, CpToEncoding(commit.Encoding), _i18n.Value); var extension = Path.GetExtension(entry.Name).ToLower(); var model = new TreeEntryModel { Name = entry.Name, ReferenceName = referenceName, Sha = commit.Sha, Path = string.IsNullOrEmpty(path) ? "" : path, Commit = new CommitModel { Sha = commit.Sha, Author = commit.Author, Committer = commit.Committer, CommitMessage = commit.Message.RepetitionIfEmpty(NoCommitMessage), CommitMessageShort = commit.MessageShort.RepetitionIfEmpty(NoCommitMessage), Parents = commit.Parents.Select(s => s.Sha).ToArray() }, EntryType = entry.TargetType, RawData = data, SizeString = FileHelper.GetSizeString(data.Length), TextContent = encoding == null ? null : FileHelper.ReadToEnd(data, encoding), TextBrush = FileHelper.BrushMapping.ContainsKey(extension) ? FileHelper.BrushMapping[extension] : "no-highlight", BlobType = encoding == null ? FileHelper.ImageSet.Contains(extension) ? BlobType.Image : BlobType.Binary : extension == ".md" ? BlobType.MarkDown : BlobType.Text, BlobEncoding = encoding, BranchSelector = GetBranchSelectorModel(referenceName, commit.Sha, path), PathBar = new PathBarModel { Name = Name, Action = "Tree", Path = path, ReferenceName = referenceName, ReferenceSha = commit.Sha, HideLastSlash = true, }, }; return(model); }