diff options
Diffstat (limited to 'LibGit/CommitObject.cs')
-rw-r--r-- | LibGit/CommitObject.cs | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/LibGit/CommitObject.cs b/LibGit/CommitObject.cs new file mode 100644 index 0000000..c2107df --- /dev/null +++ b/LibGit/CommitObject.cs @@ -0,0 +1,171 @@ +using System.IO.Compression; +using System.Text.Json.Serialization; +using LibGit.Extensions; +using LibGit.Interfaces; + +namespace LibGit; + +public class CommitObject +{ + [JsonIgnore] + public IRepoSource RepoSource { get; } + public string CommitId { get; } + + public CommitObject(IRepoSource repoSource, string commitId) + { + RepoSource = repoSource; + CommitId = commitId; + } + + private const bool _debug = false; + + public string Length { get; set; } + public string TreeId { get; set; } + public List<string> ParentIds { get; set; } = new(); + + public string Author { get; set; } + + //The Arcane Brony <myrainbowdash949@gmail.com> 1682901812 +0200 + public string Committer { get; set; } + public string GpgSignature { get; set; } + public string Message { get; set; } + public string Description { get; set; } + + public List<CommitObject> Children { get; set; } = new(); + + [JsonIgnore] + public DateTime CommitDate => new DateTime(long.Parse(Committer.Split(" ").TakeLast(2).First())*TimeSpan.TicksPerSecond) + .AddHours(long.Parse(Committer.Split(" ").TakeLast(1).First()[1..3])) + .AddMinutes(long.Parse(Committer.Split(" ").TakeLast(1).First()[3..])) + .AddYears(1970); + + [JsonIgnore] + public DateTime AuthorDate => new DateTime(long.Parse(Author.Split(" ").TakeLast(2).First())*TimeSpan.TicksPerSecond) + .AddHours(long.Parse(Committer.Split(" ").TakeLast(1).First()[1..3])) + .AddMinutes(long.Parse(Committer.Split(" ").TakeLast(1).First()[3..])) + .AddYears(1970); + + [JsonIgnore] + public string AuthorEmail => Author.Split(" <").Last().Split("> ").First(); + + [JsonIgnore] + public string AuthorName => Author.Split(" <").First(); + + [JsonIgnore] + public string CommitterEmail => Committer.Split(" <").Last().Split("> ").First(); + + [JsonIgnore] + public string CommitterName => Committer.Split(" <").First(); + + public async Task<TreeObject> GetTreeAsync() => new TreeObject(RepoSource, TreeId).ReadFromZlibCompressedObjFile(await RepoSource.GetObjectStreamById(TreeId)); + + public CommitObject ReadFromZlibCompressedObjFile(Stream bytes) + { + if (_debug) + { + Console.WriteLine($"Decompressing {this.GetType().Name}"); + Console.WriteLine($"File header:"); + // bytes.Peek(64).HexDump(16); + } + using ZLibStream stream = new ZLibStream(bytes, CompressionMode.Decompress); + using var result = new MemoryStream(); + stream.CopyTo(result); + stream.Flush(); + stream.Close(); + return ReadFromDecompressedObjFile(result); + } + + public CommitObject ReadFromDecompressedObjFile(Stream data) + { + if(_debug) Console.WriteLine("Parsing commit object"); + // var data = new Queue<byte>(bytes.ToArray()); + int iters = 0; + data.Seek(0, SeekOrigin.Begin); + if(_debug)Console.WriteLine($"Iteration {iters}: starting pos: {data.Position}/+{data.Remaining()}/{data.Length}"); + Length = data.ReadNullTerminatedField(asciiPrefix: "commit ").AsString(); + while (data.Position < data.Length) + { + if(_debug) Console.WriteLine($"Iteration {iters} ({data.Position}/+{data.Remaining()}/{data.Length})"); + if (data.StartsWith("tree ")) + TreeId = data.ReadTerminatedField((byte)'\n', asciiPrefix: "tree ").AsString(); + while (data.StartsWith("parent ")) + ParentIds.Add(data.ReadTerminatedField((byte)'\n', asciiPrefix: "parent ").AsString()); + if (data.StartsWith("author ")) + Author = data.ReadTerminatedField((byte)'\n', asciiPrefix: "author ").AsString(); + if (data.StartsWith("committer ")) + Committer = data.ReadTerminatedField((byte)'\n', asciiPrefix: "committer ").AsString();; + if (data.StartsWith("gpgsig ")) + GpgSignature = GetGpgSignature(data); + if(data.Remaining() >= 1 && (data.Peek() == (byte)'\n' || data.Peek() == (byte)'\r')) + data.Skip(1); + if(data.Peek() != (byte)'\n') + Message = GetMessage(data); + while(data.Remaining() >= 1 && (data.Peek() == (byte)'\n' || data.Peek() == (byte)'\r')) + data.Skip(1); + if(data.Remaining() >= 1 && data.Peek() != (byte)'\n') + Description = GetDescription(data); + + if (data.Remaining() > 0) + { + Console.WriteLine($"--Unparsed data after {++iters} iteration(s) of parsing CommitObject--"); + Console.WriteLine(this.ToJson()); + Console.WriteLine("--HexDump of remaining data--"); + data.Peek(data.Remaining()).HexDump(); + //Console.WriteLine($"Unparsed data: {Encoding.UTF8.GetString(data.ToArray())}"); + } + if(iters > 100) throw new Exception("Too many iterations"); + } + + data.Close(); + return this; + } + + //parsing + private string GetMessage(Stream data) + { + if(_debug) Console.WriteLine($"--commit.GetMessage--"); + var message = ""; + while (data.Remaining() > 0 && data.Peek() != (byte)'\n') + { + if(_debug) Console.WriteLine($"Commit.GetMessage -- pos: {data.Position}/+{data.Remaining()}/{data.Length} | next: {(char)data.Peek()} | Message: {message}"); + message += (char)data.ReadByte(); + + } + + // if(data.Count > 0 && data.Peek() == (byte)'\n') + // data.Dequeue(); + return message.Trim(); + } + + private string GetDescription(Stream data) + { + if(_debug) Console.WriteLine($"--commit.GetDescription--"); + var message = ""; + while (data.Remaining() > 0) + { + message += (char)data.ReadByte(); + } + + // if(data.Count > 0 && data.Peek() == (byte)'\n') + // data.Dequeue(); + return message.Trim(); + } + + private string GetGpgSignature(Stream data) + { + if(_debug) Console.WriteLine($"--commit.GetGpgSignature--"); + data.Seek(7, SeekOrigin.Current); + var signature = ""; + while ( + !signature.EndsWith("-----END PGP SIGNATURE-----\n\n") + && !signature.EndsWith("-----END PGP SIGNATURE-----\n \n") + && !signature.EndsWith("-----END SSH SIGNATURE-----\n\n") + ) + { + // Console.Write((char)data.Peek()); + signature += (char)data.ReadByte(); + } + + return signature; + } +} \ No newline at end of file |