summary refs log tree commit diff
path: root/LibGit/GitRepo.cs
blob: a58bc38bd22ac690460bfc4887f29eed2838951d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
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;
        }
    }
}