summary refs log tree commit diff
path: root/LibGit/CommitObject.cs
diff options
context:
space:
mode:
Diffstat (limited to 'LibGit/CommitObject.cs')
-rw-r--r--LibGit/CommitObject.cs171
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