using LibGit.Extensions; using LibGit.Interfaces; namespace LibGit; public class GitRepo { public IRepoSource RepoSource; public string Name { get; set; } // public string Description // { // get => File.ReadAllText(Path.Join(RepoPath, "description")); // set => File.WriteAllText(Path.Join(RepoPath, "description"), value); // } public async Task GetCommit(string commitId) { if (!commitId.Length.Equals(40)) commitId = await ResolveRef(commitId); commitId = commitId.Trim(' ', '\n', '\r', '\t'); string path = Path.Join("objects", commitId[..2], commitId[2..]); var commit = new CommitObject(RepoSource, commitId).ReadFromZlibCompressedObjFile(await RepoSource.GetFileStream(path)); return commit; } public async IAsyncEnumerable GetCommits(string commitId, int limit = Int32.MaxValue) { if (!commitId.Length.Equals(40)) commitId = await ResolveRef(commitId); commitId = commitId.Trim(' ', '\n', '\r', '\t'); var commit = await GetCommit(commitId); int i = 0; while (commit.ParentIds.Count > 0 && i++ < limit) { yield return commit; commit = await GetCommit(commit.ParentIds[0]); } yield return commit; Console.WriteLine($"Reached last commit: {commit.CommitId} ({commit.Message})"); } public async IAsyncEnumerable GetRefs() { var fs = await RepoSource.GetFileStream("info/refs"); while (fs.Remaining() > 0) { yield return new GitRef( commitId: fs.ReadTerminatedField((byte)'\t').AsString(), name: fs.ReadTerminatedField((byte)'\n').AsString(), repo: this ); } } public async IAsyncEnumerable GetPacks() { var fs = await RepoSource.GetFileStream("objects/info/packs"); Console.WriteLine("Found packs file:"); fs.Peek(32).HexDump(32); if (fs.Length <= 1) { Console.WriteLine("WARNING: No packs found!"); yield break; } while (fs.Remaining() > 0 && fs.Peek() != 0x0A) { //example: P pack-24bd1c46d657f74f40629503d8e5083a9ad36a67.pack var line = fs.ReadTerminatedField((byte)'\n').AsString(); if (line.StartsWith("P ")) { new GitPackIndex().Read(await RepoSource.GetFileStream($"objects/pack/{line[2..].Replace(".pack", ".idx")}")); yield return new GitPack( packId: line[2..], repo: this ).Read(await RepoSource.GetFileStream($"objects/pack/{line[2..]}")); } else { Console.WriteLine($"WARNING: Unknown pack line: {line}"); } } } //utilities public async Task ResolveRef(string commitRef) { if (commitRef == "HEAD") { var head = (await RepoSource.GetFileStream("HEAD")).ReadToEnd().AsString(); if (head.StartsWith("ref: ")) { return await ResolveRef(head[5..].Trim(' ', '\n', '\r', '\t')); } else { Console.WriteLine($"Found unknown HEAD style: {head}"); return head; } } if (commitRef.StartsWith("refs/")) { return (await RepoSource.GetFileStream(commitRef)).ReadToEnd().AsString().Trim(' ', '\n', '\r', '\t'); } throw new ArgumentException($"Unknown commit ref: {commitRef}"); } public GitRepo(IRepoSource repoSource) { RepoSource = repoSource; } } public class GitRef { public string Name { get; set; } public string CommitId { get; set; } public GitRepo Repo { get; set; } public GitRef(string name, string commitId, GitRepo repo) { Name = name; CommitId = commitId; Repo = repo; } public async Task GetCommit() { return await Repo.GetCommit(CommitId); } public async IAsyncEnumerable GetCommits(int limit = Int32.MaxValue) { await foreach (var commit in Repo.GetCommits(CommitId, limit)) { yield return commit; } } }