diff --git a/GitRepoViewer/Pages/Counter.razor b/GitRepoViewer/Pages/Counter.razor
new file mode 100644
index 0000000..372905f
--- /dev/null
+++ b/GitRepoViewer/Pages/Counter.razor
@@ -0,0 +1,19 @@
+@page "/counter"
+
+<PageTitle>Counter</PageTitle>
+
+<h1>Counter</h1>
+
+<p role="status">Current count: @currentCount</p>
+
+<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
+
+@code {
+ private int currentCount = 0;
+
+ private void IncrementCount()
+ {
+ currentCount++;
+ }
+
+}
\ No newline at end of file
diff --git a/GitRepoViewer/Pages/FetchData.razor b/GitRepoViewer/Pages/FetchData.razor
new file mode 100644
index 0000000..d39268f
--- /dev/null
+++ b/GitRepoViewer/Pages/FetchData.razor
@@ -0,0 +1,60 @@
+@page "/fetchdata"
+@inject HttpClient Http
+
+<PageTitle>Weather forecast</PageTitle>
+
+<h1>Weather forecast</h1>
+
+<p>This component demonstrates fetching data from the server.</p>
+
+@if (forecasts == null)
+{
+ <p>
+ <em>Loading...</em>
+ </p>
+}
+else
+{
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Date</th>
+ <th>Temp. (C)</th>
+ <th>Temp. (F)</th>
+ <th>Summary</th>
+ </tr>
+ </thead>
+ <tbody>
+ @foreach (var forecast in forecasts)
+ {
+ <tr>
+ <td>@forecast.Date.ToShortDateString()</td>
+ <td>@forecast.TemperatureC</td>
+ <td>@forecast.TemperatureF</td>
+ <td>@forecast.Summary</td>
+ </tr>
+ }
+ </tbody>
+ </table>
+}
+
+@code {
+ private WeatherForecast[]? forecasts;
+
+ protected override async Task OnInitializedAsync()
+ {
+ forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
+ }
+
+ public class WeatherForecast
+ {
+ public DateOnly Date { get; set; }
+
+ public int TemperatureC { get; set; }
+
+ public string? Summary { get; set; }
+
+ public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
+ }
+
+}
\ No newline at end of file
diff --git a/GitRepoViewer/Pages/GitLog.razor b/GitRepoViewer/Pages/GitLog.razor
new file mode 100644
index 0000000..e518bb6
--- /dev/null
+++ b/GitRepoViewer/Pages/GitLog.razor
@@ -0,0 +1,121 @@
+@page "/GitLog"
+@using System.Web
+@using LibGit
+@using LibGit.Extensions
+<h3>GitLog</h3>
+<p>Repo: @Repo</p>
+
+@if (heads.Count > 0)
+{
+ <p>
+ Revision:
+ <details>
+ <summary>@Rev</summary>
+ </details>
+ </p>
+}
+
+@if (commits != null)
+{
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Heads</th>
+ <th>Commit</th>
+ <th>Author</th>
+ <th>Message</th>
+ </tr>
+ </thead>
+ <tbody>
+ @foreach (var commit in commits)
+ {
+ <tr>
+ <td>
+ @foreach (var _ref in heads.Where(x=>x.CommitId == commit.CommitId))
+ {
+ <p>@_ref.Name</p>
+ }
+ </td>
+ <td>@commit.CommitId[..7]</td>
+ <td>@commit.AuthorName</td>
+ <td>@commit.Message</td>
+ </tr>
+ }
+ </tbody>
+ </table>
+}
+
+@code {
+
+ string? Repo { get; set; }
+ string? Rev { get; set; }
+
+ List<CommitObject> commits = new();
+ List<GitRef> heads = new();
+
+ protected override async Task OnInitializedAsync()
+ {
+ var query = new Uri(NavigationManager.Uri).Query;
+ var queryDictionary = HttpUtility.ParseQueryString(query);
+ if((Repo = queryDictionary["repo"]) == null)
+ {
+ return;
+ }
+
+ var repo = new GitRepo(new WebRepoSource(queryDictionary["repo"])
+ {
+ SessionStorage = sessionStorage
+ });
+
+ if((Rev = queryDictionary["rev"]) == null)
+ {
+ Rev = "HEAD";
+ }
+
+ var ss = new SemaphoreSlim(2,2);
+
+ var _heads = repo.GetRefs().GetAsyncEnumerator();
+ while (await _heads.MoveNextAsync())
+ {
+ heads.Add(_heads.Current);
+ var isCached = await ((WebRepoSource)repo.RepoSource).HasObjectCached(_heads.Current.CommitId);
+ Console.WriteLine(_heads.Current.Name+ " - cache miss: " + !isCached);
+ if (!isCached)
+ {
+
+ var _c = _heads.Current.CommitId;
+#pragma warning disable CS4014
+ Task.Run(async () =>
+#pragma warning restore CS4014
+ {
+ await ss.WaitAsync();
+ Console.WriteLine("hi from task");
+ var a = new GitRepo(new WebRepoSource(queryDictionary["repo"])
+ {
+ SessionStorage = sessionStorage
+ }).GetCommits(_c).GetAsyncEnumerator();
+ while (
+ await a.MoveNextAsync()
+ && !await ((WebRepoSource)repo.RepoSource)
+ .HasObjectCached(a.Current.CommitId)
+ ) Console.WriteLine($"Prefetched commit {a.Current.CommitId} with {a.Current.ParentIds.Count()} parents");
+ Console.WriteLine($"Reached already-cached log: {a.Current.CommitId}");
+ ss.Release();
+ });
+ }
+ }
+
+
+ var log = repo.GetCommits(heads.First(x=>x.Name == Rev).CommitId).GetAsyncEnumerator();
+ while (await log.MoveNextAsync())
+ {
+ commits.Add(log.Current);
+ if (commits.Count % 50 == 0)
+ {
+ StateHasChanged();
+ await Task.Delay(1);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/GitRepoViewer/Pages/Index.razor b/GitRepoViewer/Pages/Index.razor
new file mode 100644
index 0000000..66ce12c
--- /dev/null
+++ b/GitRepoViewer/Pages/Index.razor
@@ -0,0 +1,9 @@
+@page "/"
+
+<PageTitle>Index</PageTitle>
+
+<h1>Hello, world!</h1>
+
+Welcome to your new app.
+
+<SurveyPrompt Title="How is Blazor working for you?"/>
\ No newline at end of file
diff --git a/GitRepoViewer/Pages/TestPage.razor b/GitRepoViewer/Pages/TestPage.razor
new file mode 100644
index 0000000..2d29609
--- /dev/null
+++ b/GitRepoViewer/Pages/TestPage.razor
@@ -0,0 +1,225 @@
+@page "/TestPage"
+@using System.Collections.Concurrent
+@using Blazored.SessionStorage
+@using LibGit
+@using LibGit.Extensions
+
+<h3>TestPage</h3>
+
+<p>
+ Repo URL:
+ <InputText @bind-Value="RepoUrl"></InputText>
+</p>
+
+
+@if (!IsValidRepo)
+{
+ <p>Invalid Repo</p>
+ <p>This can happen if the repo isn't reachable, doesn't support dumb-http transport or only provides packs.</p>
+ <p>In the latter case, it might be worth running this script in that directory on the server, if you have permission to:</p>
+ <pre>mv objects/pack/ objects/pack-tmp; for pack in objects/pack-tmp/*.pack; do cat $pack | git unpack-objects; done; mv objects/pack-tmp/ objects/pack</pre>
+}
+else
+{
+ <p>
+
+ @code {
+
+ Dictionary<string, string> knownRefPrefixes = new Dictionary<string, string>()
+ {
+ { "refs/heads/", "Head" },
+ { "refs/pull/", "Pull" },
+ { "refs/tags/", "Tag" },
+ { "refs/remotes/", "Remote" },
+ { "refs/notes/", "Note" },
+ { "refs/stash", "Stash" },
+ { "refs/replace/", "Replace" },
+ { "refs/wip/", "WIP" },
+ { "refs/keep-around/", "Keep Around" },
+ { "refs/merges/", "Merge" },
+ };
+
+ }
+
+
+ @foreach (var (prefix, friendlyName) in knownRefPrefixes.Where(x => Refs.Any(y => y.Key.StartsWith(x.Key))))
+ {
+ <span>@friendlyName:</span>
+ <select @bind="CurrentRef">
+ @foreach (var refName in Refs.Where(x => x.Key.StartsWith(prefix)).Select(x => x.Key.Replace(prefix, "")).OrderBy(x => x))
+ {
+ <option value="@(prefix + refName)">@refName</option>
+ }
+ </select>
+ }
+
+
+ @* Other: *@
+ @* <select @bind="CurrentRef"> *@
+ @* @foreach (var refName in Refs.Where(x => x.Key.StartsWith("refs/pull")).Select(x => x.Key.Replace("refs/pull/", "")).OrderBy(x => x)) *@
+ @* { *@
+ @* <option value="refs/pull/@refName">@refName</option> *@
+ @* } *@
+ @* </select> *@
+ </p>
+
+ <p>@_commits.Count commits</p>
+
+ <table>
+ <thead>--files--</thead>
+ <tbody>
+ <tr>
+
+ </tr>
+ </tbody>
+ </table>
+ <table>
+ @foreach (var commit in _commits.Values.OrderByDescending(x => x.CommitDate))
+ {
+ <tr>
+ @* <td>@commit.Length</td> *@
+ <td style="width: 50%;">@commit.Message</td>
+ <td style="width: 25%;">@commit.CommitterName</td>
+ <td style="width: 25%;">@commit.CommitDate</td>
+ </tr>
+ }
+ </table>
+
+ @if (data != null)
+ {
+ <pre style="max-width: 100%; white-space: pre-wrap;">
+ @if (data is string)
+ {
+ @data
+ }
+ else
+ {
+ @ObjectExtensions.ToJson(data, unsafeContent: true)
+ }
+ </pre>
+ }
+}
+
+@code {
+
+ GitRepo repo { get; set; }
+ readonly ConcurrentDictionary<string, CommitObject> _commits = new();
+ bool IsValidRepo { get; set; } = true;
+ Dictionary<string, string> Refs = new();
+
+ string CurrentRef
+ {
+ get => _currentRef;
+ set
+ {
+ _currentRef = value;
+ _commits.Clear();
+ RefChanged();
+ }
+ }
+
+ string RepoUrl
+ {
+ get => _repoUrl;
+ set
+ {
+ _repoUrl = value;
+ if (!value.StartsWith("http"))
+ {
+ _repoUrl = NavigationManager.BaseUri + value;
+ }
+ _commits.Clear();
+ Refs.Clear();
+ RepoChanged();
+ }
+ }
+
+ dynamic data { get; set; }
+
+ protected override async Task OnInitializedAsync()
+ {
+ RepoUrl = "fosscord-server.git";
+ }
+
+ protected async Task RepoChanged()
+ {
+ Console.WriteLine("Repo changed!");
+ repo = new GitRepo(new WebRepoSource(RepoUrl));
+ ((WebRepoSource)repo.RepoSource).SessionStorage = sessionStorage;
+ var client = new HttpClient();
+ var response = await client.GetAsync(RepoUrl + "/info/refs");
+ if (!response.IsSuccessStatusCode)
+ {
+ IsValidRepo = false;
+ StateHasChanged();
+ return;
+ }
+ IsValidRepo = true;
+ var content = await response.Content.ReadAsStringAsync();
+ data = content;
+ StateHasChanged();
+ var lines = content.Split("\n").ToList();
+ var delay = Math.Max(lines.Count / 100, 50);
+ var index = 0;
+
+ while (lines.Count > 0)
+ {
+ var line = lines[0];
+ lines.Remove(line);
+ if (line == "")
+ {
+ break;
+ }
+ var parts = line.Split("\t");
+ Refs.Add(parts[1], parts[0]);
+ data = new
+ {
+ index,
+ delay,
+ untilNext = delay - index % delay,
+ line,
+ lines,
+ refs = Refs
+ };
+ if (++index % delay == 0)
+ {
+ StateHasChanged();
+ await Task.Delay(1);
+ }
+ }
+ response = await client.GetAsync(RepoUrl + "/HEAD");
+ content = await response.Content.ReadAsStringAsync();
+ if (content.StartsWith("ref: "))
+ CurrentRef = content.Substring(5).Trim();
+ StateHasChanged();
+ }
+
+ protected async Task RefChanged(string refName = null)
+ {
+ string currentRef;
+ data = currentRef = Refs[CurrentRef];
+ Console.WriteLine($"Ref changed: {currentRef}");
+ StateHasChanged();
+ //await LoadObject(currentRef);
+ await LoadGitLogSince(currentRef);
+ }
+
+ protected async Task LoadGitLogSince(string refName)
+ {
+ _commits.Clear();
+ var commit = await repo.GetCommit(refName);
+ _commits.TryAdd(commit.CommitId, commit);
+ while (commit.ParentIds.Count > 0)
+ {
+ Console.WriteLine($"{commit.CommitId[..7]} | {commit.AuthorName.PadRight(16)} | {commit.Message.PadRight(32)[..32]} | {commit.TreeId}");
+ // var tree = await commit.GetTreeAsync();
+ // await PrintTreeRecursive(tree);
+
+ commit = await repo.GetCommit(commit.ParentIds.First());
+ await Task.Delay(1);
+ }
+ }
+
+ private string _repoUrl = "http://localhost:5194/fosscord-server.git";
+ private string _currentRef;
+}
\ No newline at end of file
|