Skip to content

Commit a2d38cf

Browse files
authored
Add a way to navigate to GitHub/GitLab from the selected commit (#8)
* Add a way to navigate to GitHub/GitLab from the selected commit - Closes #4
1 parent 48324e3 commit a2d38cf

File tree

8 files changed

+118
-17
lines changed

8 files changed

+118
-17
lines changed

Diff for: .github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050
release_name="GitTimelapseView-$tag-${{ matrix.target }}"
5151
5252
# Build everything
53-
dotnet publish GitTimelapseView/GitTimelapseView.csproj --framework net6.0-windows --runtime "${{ matrix.target }}" -c Release -o "$release_name"
53+
dotnet publish GitTimelapseView/GitTimelapseView.csproj --framework net6.0-windows --runtime "${{ matrix.target }}" -c Release -o "$release_name" --self-contained
5454
5555
# Pack files
5656
if [ "${{ matrix.target }}" == "win-x64" ]; then

Diff for: GitTimelapseView.Core/Common/GitHelpers.cs

+48
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,20 @@ public static string RunGitCommand(string gitRootPath, string args, ILogger logg
4949
return gitProcess.StandardOutput.ReadToEnd().Trim();
5050
}
5151

52+
public static string? GetRemotePlatform(string remoteUrl)
53+
{
54+
if (remoteUrl.Contains("github.com", StringComparison.OrdinalIgnoreCase))
55+
{
56+
return "GitHub";
57+
}
58+
else if (remoteUrl.Contains("gitlab", StringComparison.OrdinalIgnoreCase))
59+
{
60+
return "GitLab";
61+
}
62+
63+
return null;
64+
}
65+
5266
internal static IReadOnlyList<string> GetCommitFileLines(this Repository repository, string relativeFilePath, string sha)
5367
{
5468
var commit = repository.Lookup<Commit>(sha);
@@ -79,6 +93,40 @@ internal static IReadOnlyList<string> GetCommitFileLines(this Repository reposit
7993
return relRoot.MakeRelativeUri(fullPath).ToString();
8094
}
8195

96+
internal static string? FindRemoteUrl(this Repository repository)
97+
{
98+
if (repository.Network.Remotes.Any())
99+
{
100+
var remote = repository.Network.Remotes.First();
101+
var url = remote.Url;
102+
if (url.EndsWith(".git", StringComparison.Ordinal))
103+
{
104+
if (url.StartsWith("git", StringComparison.Ordinal))
105+
{
106+
url = url.Replace(":", "/", StringComparison.Ordinal).Replace("git@", "https://", StringComparison.Ordinal);
107+
}
108+
109+
return url.Replace(".git", string.Empty, StringComparison.Ordinal).TrimEnd('/');
110+
}
111+
}
112+
113+
return null;
114+
}
115+
116+
internal static string? GetCommitUrl(string remoteUrl, string sha)
117+
{
118+
if (remoteUrl.Contains("github.com", StringComparison.OrdinalIgnoreCase))
119+
{
120+
return $"{remoteUrl}/commit/{sha}";
121+
}
122+
else if (remoteUrl.Contains("gitlab", StringComparison.OrdinalIgnoreCase))
123+
{
124+
return $"{remoteUrl}/-/commit/{sha}";
125+
}
126+
127+
return null;
128+
}
129+
82130
private static void HandleGitCommandErrors(object? sender, ILogger logger, string? onGitErrorMessage = null)
83131
{
84132
if (sender is Process gitProcess)

Diff for: GitTimelapseView.Core/Models/BlameBlock.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ namespace GitTimelapseView.Core.Models
66
{
77
public class BlameBlock
88
{
9-
public BlameBlock(BlameHunk block, FileRevision fileRevision, IReadOnlyList<string> lines)
9+
public BlameBlock(BlameHunk block, FileRevision fileRevision, IReadOnlyList<string> lines, string? remoteUrl)
1010
{
1111
InitialSignature = block.InitialSignature;
1212
FinalSignature = block.FinalSignature;
13-
InitialCommit = new Commit(block.InitialCommit, fileRevision.FileHistory);
14-
FinalCommit = new Commit(block.FinalCommit, fileRevision.FileHistory);
13+
InitialCommit = new Commit(block.InitialCommit, fileRevision.FileHistory, remoteUrl);
14+
FinalCommit = new Commit(block.FinalCommit, fileRevision.FileHistory, remoteUrl);
1515
StartLine = block.FinalStartLineNumber + 1;
1616
LineCount = block.LineCount;
1717
FileRevision = fileRevision.FileHistory.GetRevisionPerCommit(FinalCommit);

Diff for: GitTimelapseView.Core/Models/Commit.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class Commit
1212
{
1313
private readonly List<FileChange> _fileChanges = new();
1414

15-
public Commit(LibGit2Sharp.Commit commit, FileHistory fileHistory)
15+
public Commit(LibGit2Sharp.Commit commit, FileHistory fileHistory, string? remoteUrl)
1616
{
1717
Message = commit.Message;
1818
MessageShort = commit.MessageShort;
@@ -22,6 +22,10 @@ public Commit(LibGit2Sharp.Commit commit, FileHistory fileHistory)
2222
Committer = commit.Committer;
2323
FileHistory = fileHistory;
2424
Parents = commit.Parents.Select(x => x.Sha).ToArray();
25+
if (remoteUrl != null)
26+
{
27+
WebUrl = GitHelpers.GetCommitUrl(remoteUrl, commit.Sha);
28+
}
2529
}
2630

2731
public string Message { get; init; }
@@ -44,6 +48,8 @@ public Commit(LibGit2Sharp.Commit commit, FileHistory fileHistory)
4448

4549
public string? ContainedInTag { get; private set; }
4650

51+
public string? WebUrl { get; }
52+
4753
public void UpdateInfo(ILogger logger)
4854
{
4955
if (FileChanges.Any())

Diff for: GitTimelapseView.Core/Models/FileHistory.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public void Initialize(ILogger logger)
4949
var commitIds = GetFileCommitIDs(logger).Reverse().ToArray();
5050
using (var repository = new Repository(GitRootPath))
5151
{
52+
var remoteUrl = repository.FindRemoteUrl();
53+
5254
var relativeFilePath = repository.MakeRelativeFilePath(FilePath);
5355
if (relativeFilePath == null)
5456
throw new Exception($"Unable to blame '{FilePath}'. Path is not located in the repository working directory.");
@@ -57,7 +59,7 @@ public void Initialize(ILogger logger)
5759
{
5860
var commitId = commitIds[index];
5961
var commit = repository.Lookup<LibGit2Sharp.Commit>(commitId);
60-
_revisions.Add(new FileRevision(index, new Commit(commit, this), this));
62+
_revisions.Add(new FileRevision(index, new Commit(commit, this, remoteUrl), this));
6163
}
6264
}
6365
}

Diff for: GitTimelapseView.Core/Models/FileRevision.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public void LoadBlocks(ILogger logger)
3434

3535
using (var repository = new Repository(FileHistory.GitRootPath))
3636
{
37+
var remoteUrl = repository.FindRemoteUrl();
38+
3739
var relativeFilePath = repository.MakeRelativeFilePath(FileHistory.FilePath);
3840
if (relativeFilePath == null)
3941
throw new Exception($"Unable to blame '{FileHistory.FilePath}'. Path is not located in the repository working directory.");
@@ -43,7 +45,7 @@ public void LoadBlocks(ILogger logger)
4345

4446
foreach (var block in blocks)
4547
{
46-
Blocks.Add(new BlameBlock(block, this, lines));
48+
Blocks.Add(new BlameBlock(block, this, lines, remoteUrl));
4749
}
4850
}
4951
}

Diff for: GitTimelapseView/Components/CommitInfo/CommitInfo.razor

+27-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
@namespace GitTimelapseView
2+
@using System.Diagnostics
3+
@using GitTimelapseView.Core.Common
24

35
@if (_commit == null)
46
{
@@ -9,28 +11,32 @@ else
911
{
1012
<Card Size="small" class="commit-details" Style="height: 100%">
1113
<TitleTemplate>
12-
<div class="card-title" style="font-weight: bolder;">
14+
<div class="card-title">
1315
<span title=@_commit.Id>
1416
Commit #@_commit.ShortId
1517
</span>
1618
<Tooltip Title=@CopyToClipboardTooltip>
1719
<Button Type="@ButtonType.Text" @onclick="OnButtonClicked" Size="@ButtonSize.Small">
18-
<i class="mdi mdi-clipboard-multiple-outline"></i>
20+
<i class="mdi mdi-clipboard-multiple-outline" />
1921
</Button>
2022
</Tooltip>
21-
<span style="font-size: 12px;">by</span>
23+
<span>by</span>
2224
<span>
2325
<AvatarExt Email=@_commit.Author.Email ShowDisplayName />
2426
</span>
25-
<span style="font-size: 12px;">on @_authoredString</span>
26-
</div>
27+
<span>on @_authoredString</span>
28+
@if(_commit.WebUrl != null && GitHelpers.GetRemotePlatform(_commit.WebUrl) is string platform)
29+
{
30+
<a class="open-remote-url" title=@($"Open commit in {platform}") href="" @onclick="OnWebUrlClicked" @onclick:preventDefault><i class="mdi mdi-open-in-new " />&nbsp;@platform</a>
31+
}
32+
</div>
2733
</TitleTemplate>
2834
<Extra>
2935
@if(!string.IsNullOrEmpty(_commit.ContainedInTag))
3036
{
3137
<Tooltip Title=@($"contained in tag {_commit.ContainedInTag}")>
32-
<span style="opacity:0.4; margin-left: 20px; font-size: 12px;">
33-
<i class="mdi mdi-tag" ></i>
38+
<span class="tag-label" >
39+
<i class="mdi mdi-tag" />
3440
@_commit.ContainedInTag
3541
</span>
3642
</Tooltip>
@@ -39,7 +45,7 @@ else
3945
<CardTabs>
4046
<Tabs @bind-ActiveKey="@_selectedTab" Animated Size="@TabSize.Small" >
4147
<TabPane Tab="Message" Key="message">
42-
<div style="margin:10px; overflow: scroll; max-height: 220px; max-width: 750px; overflow-y: scroll; overflow-x: scroll;white-space: pre-wrap;">
48+
<div class="commit-message">
4349
@_commit.Message
4450
</div>
4551
</TabPane >
@@ -86,5 +92,17 @@ else
8692
{
8793
await new CopyToClipboardAction(TimelapseService.CurrentCommit.Id, "commit id").ExecuteAsync().ConfigureAwait(false);
8894
}
89-
}
95+
}
96+
97+
private void OnWebUrlClicked()
98+
{
99+
if (_commit?.WebUrl != null)
100+
{
101+
Process.Start(new ProcessStartInfo
102+
{
103+
FileName = _commit?.WebUrl,
104+
UseShellExecute = true,
105+
});
106+
}
107+
}
90108
}
+26-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
1-
.card-title{
1+
.card-title {
2+
font-weight: bolder;
3+
font-size: 12px;
4+
}
5+
6+
.open-remote-url {
7+
opacity: 0.4;
8+
margin-left: 10px;
9+
font-size: 12px;
10+
color: var(--gtlv-foreground);
11+
text-decoration: none;
12+
}
13+
14+
.tag-label {
15+
opacity: 0.4;
16+
margin-left: 20px;
17+
font-size: 12px;
18+
}
219

20+
.commit-message {
21+
margin: 10px;
22+
overflow: scroll;
23+
max-height: 220px;
24+
max-width: 750px;
25+
overflow-y: scroll;
26+
overflow-x: scroll;
27+
white-space: pre-wrap;
328
}

0 commit comments

Comments
 (0)