Skip to content

Commit 582c1a2

Browse files
committed
refactor: repository remote
- Gather remotes from configurations instead of `git remote -v` since we need to read it - Store private SSH key setting in `Models.Remote` - Use the cached SSH key setting in `Models.Remote` instead of running another `git config` command when fetch/pull/push Signed-off-by: leo <longshuang@msn.cn>
1 parent 3f4f0ac commit 582c1a2

21 files changed

Lines changed: 207 additions & 203 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.Threading.Tasks;
2+
3+
namespace SourceGit.Commands
4+
{
5+
public class DoesBranchExistOnRemote : Command
6+
{
7+
public DoesBranchExistOnRemote(string repo, Models.Remote remote, Models.Branch branch)
8+
{
9+
WorkingDirectory = repo;
10+
SSHKey = remote.PrivateSSHKey;
11+
RaiseError = false;
12+
Args = $"ls-remote {remote.Name} {branch.Name}";
13+
}
14+
15+
public async Task<bool> GetResultAsync()
16+
{
17+
var rs = await ReadToEndAsync();
18+
return rs.IsSuccess && rs.StdOut.Trim().Length > 0;
19+
}
20+
}
21+
}

src/Commands/Fetch.cs

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,40 @@
11
using System.Text;
2-
using System.Threading.Tasks;
32

43
namespace SourceGit.Commands
54
{
65
public class Fetch : Command
76
{
8-
public Fetch(string repo, string remote, bool noTags, bool force)
7+
public Fetch(string repo, Models.Remote remote, bool noTags, bool force)
98
{
10-
_remote = remote;
11-
129
WorkingDirectory = repo;
1310
Context = repo;
11+
SSHKey = remote.PrivateSSHKey;
1412

1513
var builder = new StringBuilder(512);
1614
builder.Append("fetch --progress --verbose ");
1715
builder.Append(noTags ? "--no-tags " : "--tags ");
1816
if (force)
1917
builder.Append("--force ");
20-
builder.Append(remote);
18+
builder.Append(remote.Name);
2119

2220
Args = builder.ToString();
2321
}
2422

25-
public Fetch(string repo, string remote)
23+
public Fetch(string repo, Models.Remote remote)
2624
{
27-
_remote = remote;
28-
2925
WorkingDirectory = repo;
3026
Context = repo;
27+
SSHKey = remote.PrivateSSHKey;
3128
RaiseError = false;
32-
33-
Args = $"fetch --progress --verbose {remote}";
29+
Args = $"fetch --progress --verbose {remote.Name}";
3430
}
3531

36-
public Fetch(string repo, Models.Branch local, Models.Branch remote)
32+
public Fetch(string repo, Models.Remote remote, Models.Branch remoteBranch, Models.Branch local)
3733
{
38-
_remote = remote.Remote;
39-
4034
WorkingDirectory = repo;
4135
Context = repo;
42-
Args = $"fetch --progress --verbose {remote.Remote} {remote.Name}:{local.Name}";
36+
SSHKey = remote.PrivateSSHKey;
37+
Args = $"fetch --progress --verbose {remote.Name} {remoteBranch.Name}:{local.Name}";
4338
}
44-
45-
public async Task<bool> RunAsync()
46-
{
47-
SSHKey = await new Config(WorkingDirectory).GetAsync($"remote.{_remote}.sshkey").ConfigureAwait(false);
48-
return await ExecAsync().ConfigureAwait(false);
49-
}
50-
51-
private readonly string _remote;
5239
}
5340
}

src/Commands/Pull.cs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,26 @@
11
using System.Text;
2-
using System.Threading.Tasks;
32

43
namespace SourceGit.Commands
54
{
65
public class Pull : Command
76
{
8-
public Pull(string repo, string remote, string branch, bool useRebase)
7+
public Pull(string repo, Models.Remote remote, Models.Branch remoteBranch, bool useRebase)
98
{
10-
_remote = remote;
11-
129
WorkingDirectory = repo;
1310
Context = repo;
11+
SSHKey = remote.PrivateSSHKey;
1412

1513
var builder = new StringBuilder(512);
1614
builder
1715
.Append("pull --verbose --progress --rebase=")
1816
.Append(useRebase ? "true" : "false")
1917
.Append(' ')
20-
.Append(remote)
21-
.Append(' ')
22-
.Append(branch);
18+
.Append(remote.Name);
2319

24-
Args = builder.ToString();
25-
}
20+
if (remoteBranch != null)
21+
builder.Append(' ').Append(remoteBranch.Name);
2622

27-
public async Task<bool> RunAsync()
28-
{
29-
SSHKey = await new Config(WorkingDirectory).GetAsync($"remote.{_remote}.sshkey").ConfigureAwait(false);
30-
return await ExecAsync().ConfigureAwait(false);
23+
Args = builder.ToString();
3124
}
32-
33-
private readonly string _remote;
3425
}
3526
}

src/Commands/Push.cs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
using System.Text;
2-
using System.Threading.Tasks;
32

43
namespace SourceGit.Commands
54
{
65
public class Push : Command
76
{
8-
public Push(string repo, string local, string remote, string remoteBranch, bool withTags, bool checkSubmodules, bool track, bool force, bool noVerify)
7+
public Push(string repo, Models.Branch local, Models.Remote remote, Models.Branch remoteBranch, bool withTags, bool checkSubmodules, bool track, bool force, bool noVerify)
98
{
10-
_remote = remote;
11-
129
WorkingDirectory = repo;
1310
Context = repo;
11+
SSHKey = remote.PrivateSSHKey;
1412

1513
var builder = new StringBuilder(1024);
1614
builder.Append("push --progress --verbose ");
@@ -25,32 +23,38 @@ public Push(string repo, string local, string remote, string remoteBranch, bool
2523
if (noVerify)
2624
builder.Append("--no-verify ");
2725

28-
builder.Append(remote).Append(' ').Append(local).Append(':').Append(remoteBranch);
26+
builder.Append(remote.Name).Append(' ').Append(local.Name).Append(':').Append(remoteBranch.Name);
2927
Args = builder.ToString();
3028
}
3129

32-
public Push(string repo, string remote, string refname, bool isDelete)
30+
public Push(string repo, Models.Commit revision, Models.Remote remote, Models.Branch remoteBranch, bool force)
3331
{
34-
_remote = remote;
32+
WorkingDirectory = repo;
33+
Context = repo;
34+
SSHKey = remote.PrivateSSHKey;
35+
36+
var builder = new StringBuilder(1024);
37+
builder.Append("push --progress --verbose ");
38+
if (force)
39+
builder.Append("--force-with-lease ");
3540

41+
builder.Append(remote.Name).Append(' ').Append(revision.SHA).Append(':').Append(remoteBranch.Name);
42+
Args = builder.ToString();
43+
}
44+
45+
public Push(string repo, Models.Remote remote, string refname, bool isDelete)
46+
{
3647
WorkingDirectory = repo;
3748
Context = repo;
49+
SSHKey = remote.PrivateSSHKey;
3850

3951
var builder = new StringBuilder(512);
4052
builder.Append("push ");
4153
if (isDelete)
4254
builder.Append("--delete ");
43-
builder.Append(remote).Append(' ').Append(refname);
55+
builder.Append(remote.Name).Append(' ').Append(refname);
4456

4557
Args = builder.ToString();
4658
}
47-
48-
public async Task<bool> RunAsync()
49-
{
50-
SSHKey = await new Config(WorkingDirectory).GetAsync($"remote.{_remote}.sshkey").ConfigureAwait(false);
51-
return await ExecAsync().ConfigureAwait(false);
52-
}
53-
54-
private readonly string _remote;
5559
}
5660
}

src/Commands/QueryRemotes.cs

Lines changed: 39 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,70 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Text.RegularExpressions;
43
using System.Threading.Tasks;
54

65
namespace SourceGit.Commands
76
{
8-
public partial class QueryRemotes : Command
7+
public class QueryRemotes
98
{
10-
[GeneratedRegex(@"^([\w\.\-]+)\s*(\S+).*$")]
11-
private static partial Regex REG_REMOTE();
12-
139
public QueryRemotes(string repo)
1410
{
15-
WorkingDirectory = repo;
16-
Context = repo;
17-
Args = "remote -v";
11+
_repo = repo;
1812
}
1913

2014
public async Task<List<Models.Remote>> GetResultAsync()
2115
{
22-
var outs = new List<Models.Remote>();
23-
var rs = await ReadToEndAsync().ConfigureAwait(false);
24-
if (!rs.IsSuccess)
25-
return outs;
26-
27-
var config = await new Config(WorkingDirectory).ReadAllAsync().ConfigureAwait(false);
16+
var names = new HashSet<string>();
17+
var urls = new Dictionary<string, string>();
18+
var privateSSHKeys = new Dictionary<string, string>();
2819
var disableAutoFetchRemotes = new HashSet<string>();
20+
21+
var config = await new Config(_repo).ReadAllAsync().ConfigureAwait(false);
2922
foreach (var (k, v) in config)
3023
{
31-
if (k.StartsWith("remote.", StringComparison.Ordinal) &&
32-
k.EndsWith(".disableautofetch", StringComparison.Ordinal) &&
33-
v.Equals("true", StringComparison.OrdinalIgnoreCase))
24+
if (!k.StartsWith("remote.", StringComparison.Ordinal))
25+
continue;
26+
27+
if (k.EndsWith(".url", StringComparison.Ordinal))
28+
{
29+
var name = k.Substring(7, k.Length - 11).Trim('"');
30+
names.Add(name);
31+
urls[name] = v;
32+
}
33+
else if (k.EndsWith(".sshkey", StringComparison.OrdinalIgnoreCase))
3434
{
35-
var remoteName = k.Substring(7, k.Length - 24).Trim('"');
36-
disableAutoFetchRemotes.Add(remoteName);
35+
var name = k.Substring(7, k.Length - 14).Trim('"');
36+
names.Add(name);
37+
privateSSHKeys[name] = v;
38+
}
39+
else if (k.EndsWith(".disableautofetch", StringComparison.OrdinalIgnoreCase) &&
40+
v.Equals("true", StringComparison.OrdinalIgnoreCase))
41+
{
42+
var name = k.Substring(7, k.Length - 24).Trim('"');
43+
disableAutoFetchRemotes.Add(name);
3744
}
3845
}
3946

40-
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
41-
foreach (var line in lines)
47+
var remotes = new List<Models.Remote>();
48+
foreach (var name in names)
4249
{
43-
var match = REG_REMOTE().Match(line);
44-
if (!match.Success)
50+
if (!urls.TryGetValue(name, out var url))
4551
continue;
4652

47-
var remote = new Models.Remote()
53+
var r = new Models.Remote()
4854
{
49-
Name = match.Groups[1].Value,
50-
URL = match.Groups[2].Value,
51-
DisableAutoFetch = disableAutoFetchRemotes.Contains(match.Groups[1].Value)
55+
Name = name,
56+
URL = url,
57+
PrivateSSHKey = privateSSHKeys.TryGetValue(name, out var privateSSHKey) ? privateSSHKey : null,
58+
DisableAutoFetch = disableAutoFetchRemotes.Contains(name)
5259
};
5360

54-
if (outs.Find(x => x.Name == remote.Name) != null)
55-
continue;
56-
57-
if (remote.URL.StartsWith("git@", StringComparison.Ordinal))
58-
{
59-
var hostEnd = remote.URL.IndexOf(':', 4);
60-
if (hostEnd > 4)
61-
{
62-
var host = remote.URL.Substring(4, hostEnd - 4);
63-
Models.HTTPSValidator.Add(host);
64-
}
65-
}
66-
67-
outs.Add(remote);
61+
remotes.Add(r);
6862
}
6963

70-
return outs;
64+
remotes.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
65+
return remotes;
7166
}
67+
68+
private readonly string _repo;
7269
}
7370
}

src/Commands/Remote.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,5 @@ public async Task<bool> SetURLAsync(string name, string url, bool isPush)
4747
Args = "remote set-url" + (isPush ? " --push " : " ") + $"{name} {url}";
4848
return await ExecAsync();
4949
}
50-
51-
public async Task<bool> HasBranchAsync(string remote, string branch)
52-
{
53-
SSHKey = await new Config(WorkingDirectory).GetAsync($"remote.{remote}.sshkey");
54-
Args = $"ls-remote {remote} {branch}";
55-
56-
var rs = await ReadToEndAsync();
57-
return rs.IsSuccess && rs.StdOut.Trim().Length > 0;
58-
}
5950
}
6051
}

src/Models/Remote.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public partial class Remote
3131

3232
public string Name { get; set; }
3333
public string URL { get; set; }
34+
public string PrivateSSHKey { get; set; }
3435
public bool DisableAutoFetch { get; set; }
3536

3637
public static bool IsSSH(string url)

src/ViewModels/AddRemote.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,19 @@ public override async Task<bool> Sure()
107107

108108
if (succ)
109109
{
110+
var remote = new Models.Remote()
111+
{
112+
Name = _name,
113+
PrivateSSHKey = _useSSH ? _sshkey : null,
114+
};
115+
110116
await new Commands.Config(_repo.FullPath)
111117
.Use(log)
112118
.SetAsync($"remote.{_name}.sshkey", _useSSH ? SSHKey : null);
113119

114-
await new Commands.Fetch(_repo.FullPath, _name, FetchWithoutTags, false)
120+
await new Commands.Fetch(_repo.FullPath, remote, FetchWithoutTags, false)
115121
.Use(log)
116-
.RunAsync();
122+
.ExecAsync();
117123
}
118124

119125
log.Complete();

src/ViewModels/CreateTag.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,13 @@ public override async Task<bool> Sure()
101101
else
102102
succ = await cmd.AddAsync(_basedOn);
103103

104-
if (succ && remotes != null)
104+
if (succ && remotes is { Count: > 0 })
105105
{
106+
var fullname = $"refs/tags/{_tagName}";
106107
foreach (var remote in remotes)
107-
await new Commands.Push(_repo.FullPath, remote.Name, $"refs/tags/{_tagName}", false)
108+
await new Commands.Push(_repo.FullPath, remote, fullname, false)
108109
.Use(log)
109-
.RunAsync();
110+
.ExecAsync();
110111
}
111112

112113
log.Complete();

src/ViewModels/DeleteBranch.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,20 @@ public override async Task<bool> Sure()
9494

9595
private async Task<bool> DeleteRemoteBranchAsync(Models.Branch branch, CommandLog log)
9696
{
97-
var exists = await new Commands.Remote(_repo.FullPath)
98-
.HasBranchAsync(branch.Remote, branch.Name)
99-
.ConfigureAwait(false);
97+
var exists = false;
98+
var remote = _repo.Remotes.Find(x => x.Name.Equals(branch.Remote, StringComparison.Ordinal));
99+
if (remote != null)
100+
{
101+
exists = await new Commands.DoesBranchExistOnRemote(_repo.FullPath, remote, branch)
102+
.Use(log)
103+
.GetResultAsync()
104+
.ConfigureAwait(false);
105+
}
100106

101107
if (exists)
102-
return await new Commands.Push(_repo.FullPath, branch.Remote, $"refs/heads/{branch.Name}", true)
108+
return await new Commands.Push(_repo.FullPath, remote, $"refs/heads/{branch.Name}", true)
103109
.Use(log)
104-
.RunAsync()
110+
.ExecAsync()
105111
.ConfigureAwait(false);
106112
else
107113
return await new Commands.Branch(_repo.FullPath, branch.Name)

0 commit comments

Comments
 (0)