summary refs log tree commit diff
path: root/LibGit/GitRepo.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--LibGit/GitRepo.cs141
1 files changed, 141 insertions, 0 deletions
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