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
|