diff --git a/LibGit/GitRepo.cs b/LibGit/GitRepo.cs
new file mode 100644
index 0000000..a58bc38
--- /dev/null
+++ b/LibGit/GitRepo.cs
@@ -0,0 +1,141 @@
+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<CommitObject> 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<CommitObject> 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<GitRef> 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<GitPack> 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<string> 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<CommitObject> GetCommit()
+ {
+ return await Repo.GetCommit(CommitId);
+ }
+
+ public async IAsyncEnumerable<CommitObject> GetCommits(int limit = Int32.MaxValue)
+ {
+ await foreach (var commit in Repo.GetCommits(CommitId, limit))
+ {
+ yield return commit;
+ }
+ }
+}
\ No newline at end of file
|