using LibGit.Extensions; namespace LibGit; public class GitPack { public string PackId { get; set; } public GitRepo Repo { get; set; } public int Version { get; set; } public int ObjectCount { get; set; } public List Objects { get; set; } = new List(); public GitPack Read(Stream stream) { stream.Peek(12).HexDump(16); Console.Write(" Header: "); stream.Peek(04).ToArray()[0..].HexDump(4); Console.Write("Version: "); stream.Peek(08).ToArray()[4..].HexDump(4); Console.Write(" ObjCnt: "); stream.Peek(12).ToArray()[8..].HexDump(4); if(!stream.StartsWith("PACK")) throw new Exception("Invalid pack file header"); stream.Skip(4); Version = stream.ReadInt32BE(); ObjectCount = stream.ReadInt32BE(); Console.WriteLine($"Got git v{Version} pack with {ObjectCount} objects"); for (int i = 0; i < ObjectCount; i++) { Objects.Add(new GitPackObject().Read(stream)); } return this; } public GitPack(string packId, GitRepo repo) { PackId = packId; Repo = repo; } } public class GitPackIndex { public int Version { get; set; } public int[] fanOutTable = new int[256]; public GitPackIndex Read(Stream stream) { if(!stream.StartsWith(new byte[]{0xff,0x74,0x4f,0x63})) throw new Exception("Invalid pack index file header or pack is v1"); stream.Skip(4); Version = stream.ReadInt32BE(); Console.WriteLine($"Got git v{Version} pack index"); //fan-out table for (int i = 0; i < 256; i++) { fanOutTable[i] = stream.ReadInt32BE(); } return this; } } public class GitPackObject { private const bool _debug = true; public GitPackObject Read(Stream stream) { stream.Peek(64).HexDump(32); var header = stream.ReadBytes(4).ToArray(); ObjType = (GitObjectType)((header[0] & 0b0111_0000) >> 4); if(ObjType == 0 || (int)ObjType == 5 || (int)ObjType > 7) throw new Exception($"Invalid object type: {(int)ObjType}"); Size = header[0] & 0b0000_1111; Offset = 0; for (int i = 1; i < 4; i++) { Offset <<= 8; Offset |= header[i]; } if ((Size & 0b0000_1000) != 0) { Size <<= 4; Size |= stream.ReadVLQ(); } // ObjType = Type switch // { // 1 => GitObjectType.Commit, // 2 => GitObjectType.Tree, // 3 => GitObjectType.Blob, // 4 => GitObjectType.Tag, // 5 => GitObjectType.Invalid, // 6 => GitObjectType.OffsDelta, // 7 => GitObjectType.RefDelta, // _ => throw new Exception($"Invalid object type {Type}") // }; if(_debug) Console.WriteLine($"pack obj type: {ObjType} ({(int)ObjType}), size: {Size}, offset: {Offset}, sizeBytes: {SizeBytes}"); Console.WriteLine("Data: "); stream.Peek(Size).Take(16).ToArray().HexDump(16); stream.ReadBytes(Size).ZlibDecompress().Take(16).HexDump(16); return this; } public GitObjectType ObjType { get; set; } public int SizeBytes { get; set; } public int Size { get; set; } public int Offset { get; set; } } public enum GitObjectType { Commit = 1, Tree, Blob, Tag, Invalid, // Reserved for future expansion, see https://git-scm.com/docs/pack-format#_object_types OffsDelta, RefDelta }