Skip to content

Commit ef05694

Browse files
committed
feature: supports to create pull request with selected branch (or its upstream) (#1854)
Signed-off-by: leo <longshuang@msn.cn>
1 parent 97948e1 commit ef05694

File tree

7 files changed

+100
-10
lines changed

7 files changed

+100
-10
lines changed

src/Models/CommitLink.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ public static List<CommitLink> Get(List<Remote> remotes)
2222
{
2323
if (remote.TryGetVisitURL(out var link))
2424
{
25-
if (link.EndsWith(".git"))
26-
link = link.Substring(0, link.Length - 4);
27-
2825
var uri = new Uri(link, UriKind.Absolute);
2926
var host = uri.Host;
3027
var route = uri.AbsolutePath.TrimStart('/');

src/Models/Remote.cs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.IO;
33
using System.Text.RegularExpressions;
4+
using System.Web;
45

56
namespace SourceGit.Models
67
{
@@ -65,7 +66,6 @@ public bool TryGetVisitURL(out string url)
6566

6667
if (URL.StartsWith("http", StringComparison.Ordinal))
6768
{
68-
// Try to remove the user before host and `.git` extension.
6969
var uri = new Uri(URL.EndsWith(".git", StringComparison.Ordinal) ? URL.Substring(0, URL.Length - 4) : URL);
7070
if (uri.Port != 80 && uri.Port != 443)
7171
url = $"{uri.Scheme}://{uri.Host}:{uri.Port}{uri.LocalPath}";
@@ -84,5 +84,56 @@ public bool TryGetVisitURL(out string url)
8484

8585
return false;
8686
}
87+
88+
public bool TryGetCreatePullRequestURL(out string url, string mergeBranch)
89+
{
90+
url = null;
91+
92+
if (!TryGetVisitURL(out var baseURL))
93+
return false;
94+
95+
var uri = new Uri(baseURL);
96+
var host = uri.Host;
97+
var route = uri.AbsolutePath.TrimStart('/');
98+
var encodedBranch = HttpUtility.UrlEncode(mergeBranch);
99+
100+
if (host.Contains("github.com", StringComparison.Ordinal))
101+
{
102+
url = $"{baseURL}/compare/{encodedBranch}?expand=1";
103+
return true;
104+
}
105+
106+
if (host.Contains("gitlab", StringComparison.Ordinal))
107+
{
108+
url = $"{baseURL}/-/merge_requests/new?merge_request%5Bsource_branch%5D={encodedBranch}";
109+
return true;
110+
}
111+
112+
if (host.Equals("gitee.com", StringComparison.Ordinal))
113+
{
114+
url = $"{baseURL}/pulls/new?source={encodedBranch}";
115+
return true;
116+
}
117+
118+
if (host.Equals("bitbucket.org", StringComparison.Ordinal))
119+
{
120+
url = $"{baseURL}/pull-requests/new?source={encodedBranch}";
121+
return true;
122+
}
123+
124+
if (host.Equals("gitea.org", StringComparison.Ordinal))
125+
{
126+
url = $"{baseURL}/compare/{encodedBranch}";
127+
return true;
128+
}
129+
130+
if (host.Contains("azure.com", StringComparison.Ordinal))
131+
{
132+
url = $"{baseURL}/pullrequestcreate?sourceRef={encodedBranch}";
133+
return true;
134+
}
135+
136+
return false;
137+
}
87138
}
88139
}

src/Resources/Icons.axaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<StreamGeometry x:Key="Icons.Compare">M645 448l64 64 220-221L704 64l-64 64 115 115H128v90h628zM375 576l-64-64-220 224L314 960l64-64-116-115H896v-90H262z</StreamGeometry>
2727
<StreamGeometry x:Key="Icons.Conflict">M608 0q48 0 88 23t63 63 23 87v70h55q35 0 67 14t57 38 38 57 14 67V831q0 34-14 66t-38 57-57 38-67 13H426q-34 0-66-13t-57-38-38-57-14-66v-70h-56q-34 0-66-14t-57-38-38-57-13-67V174q0-47 23-87T109 23 196 0h412m175 244H426q-46 0-86 22T278 328t-26 85v348H608q47 0 86-22t63-62 25-85l1-348m-269 318q18 0 31 13t13 31-13 31-31 13-31-13-13-31 13-31 31-13m0-212q13 0 22 9t11 22v125q0 14-9 23t-22 10-23-7-11-22l-1-126q0-13 10-23t23-10z</StreamGeometry>
2828
<StreamGeometry x:Key="Icons.Copy">M896 811l-128 0c-23 0-43-19-43-43 0-23 19-43 43-43l107 0c13 0 21-9 21-21L896 107c0-13-9-21-21-21L448 85c-13 0-21 9-21 21l0 21c0 23-19 43-43 43-23 0-43-19-43-43L341 85c0-47 38-85 85-85l469 0c47 0 85 38 85 85l0 640C981 772 943 811 896 811zM683 299l0 640c0 47-38 85-85 85L128 1024c-47 0-85-38-85-85L43 299c0-47 38-85 85-85l469 0C644 213 683 252 683 299zM576 299 149 299c-13 0-21 9-21 21l0 597c0 13 9 21 21 21l427 0c13 0 21-9 21-21L597 320C597 307 589 299 576 299z</StreamGeometry>
29+
<StreamGeometry x:Key="Icons.CreatePR">M339 297c17-23 25-51 25-83 2-42-12-79-43-108S255 62 215 65c-32 0-60 8-84 25s-42 39-54 67-15 56-10 86 19 55 40 76c21 21 47 35 76 41v303c-30 6-55 20-76 41-21 21-35 47-40 76-5 30-2 58 10 86s30 50 54 67 52 25 83 25 59-8 84-25c25-17 45-39 57-67 6-19 10-39 10-61 0-30-8-57-25-83-21-34-52-55-92-64v-299l25-6c28-13 50-32 67-57zm-45 471c8 15 12 30 11 46-1 16-6 31-16 46-10 15-23 25-40 32s-34 8-51 5-32-11-46-24-22-28-24-45c-6-28-1-53 18-75s41-33 68-33c17 0 32 4 46 13 14 8 25 20 33 35zM167 288c-15-11-26-24-33-41-7-17-10-34-6-51 3-17 11-32 24-45s28-21 46-25 36-3 53 5c17 7 30 18 40 32 10 14 15 29 16 46 1 17-3 33-11 48-8 15-20 27-33 35-14 8-29 13-46 13s-33-5-48-16zm615 45c2-19-1-38-10-57-11-28-29-50-54-67s-53-25-83-25h-111l76-76-45-41-127 127v41l127 127 45-41-76-76h111c23 0 44 8 62 25 18 17 27 38 27 64v89h57V332zm0 449H960v-61H782V542h-61v178H543v61h178v178h61V781z</StreamGeometry>
2930
<StreamGeometry x:Key="Icons.Cut">M280 145l243 341 0-0 45 63-0 0 79 110a143 143 0 11-36 75l-88-123-92 126c1 4 1 9 1 13l0 5a143 143 0 11-36-95l82-113L221 188l60-43zm473 541a70 70 0 100 140 70 70 0 000-140zm-463 0a70 70 0 100 140 70 70 0 000-140zM772 145l59 43-232 319-45-63L772 145z</StreamGeometry>
3031
<StreamGeometry x:Key="Icons.Detached">M128 183C128 154 154 128 183 128h521c30 0 55 26 55 55v38c0 17-17 34-34 34s-34-17-34-34v-26H196v495h26c17 0 34 17 34 34s-17 34-34 34h-38c-30 0-55-26-55-55V183zM380 896h-34c-26 0-47-21-47-47v-90h68V828h64V896H380c4 0 0 0 0 0zM759 828V896h90c26 0 47-21 47-47v-90h-68V828h-68zM828 435H896V346c0-26-21-47-47-47h-90v68H828v68zM435 299v68H367V439H299V346C299 320 320 299 346 299h90zM367 649H299v-107h68v107zM546 367V299h107v68h-107zM828 546H896v107h-68v-107zM649 828V896h-107v-68h107zM730 508v188c0 17-17 34-34 34h-188c-17 0-34-17-34-34s17-34 34-34h102l-124-124c-13-13-13-34 0-47 13-13 34-13 47 0l124 124V512c0-17 17-34 34-34 21-4 38 9 38 30z</StreamGeometry>
3132
<StreamGeometry x:Key="Icons.Detail">M889 0H135c-32 0-59 26-59 59v906c0 32 26 59 59 59h753c32 0 59-26 59-59v-906c1-33-26-59-58-59zm-165 177c31 0 56 25 56 56s-25 56-56 56-56-25-56-56 25-56 56-56zm-212 0c31 0 56 25 56 56S543 288 512 288s-56-25-56-56S481 177 512 177zm-212 0c31 0 56 25 56 56s-25 56-56 56-56-25-56-56 25-56 56-56zm209 606H285c-25 0-44-20-44-44 0-25 20-44 44-44h224c25 0 44 20 44 44 0 24-20 44-44 44zm230-212H285c-25 0-44-20-44-44 0-25 20-44 44-44h453c25 0 44 20 44 44 1 24-20 44-44 44z</StreamGeometry>

src/Resources/Locales/en_US.axaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
<x:String x:Key="Text.BranchCM.CompareWithCurrent" xml:space="preserve">Compare with ${0}$</x:String>
5858
<x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">Compare with Worktree</x:String>
5959
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">Copy Branch Name</x:String>
60+
<x:String x:Key="Text.BranchCM.CreatePR" xml:space="preserve">Create PR...</x:String>
61+
<x:String x:Key="Text.BranchCM.CreatePRForUpstream" xml:space="preserve">Create PR for upstream ${0}$...</x:String>
6062
<x:String x:Key="Text.BranchCM.CustomAction" xml:space="preserve">Custom Action</x:String>
6163
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">Delete ${0}$...</x:String>
6264
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">Delete selected {0} branches</x:String>

src/Resources/Locales/zh_CN.axaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
<x:String x:Key="Text.BranchCM.CompareWithCurrent" xml:space="preserve">与当前 ${0}$ 比较</x:String>
6262
<x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">与本地工作树比较</x:String>
6363
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">复制分支名</x:String>
64+
<x:String x:Key="Text.BranchCM.CreatePR" xml:space="preserve">创建合并请求 ...</x:String>
65+
<x:String x:Key="Text.BranchCM.CreatePRForUpstream" xml:space="preserve">为上游分支 ${0}$ 创建合并请求 ...</x:String>
6466
<x:String x:Key="Text.BranchCM.CustomAction" xml:space="preserve">自定义操作</x:String>
6567
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">删除 ${0}$...</x:String>
6668
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">删除选中的 {0} 个分支</x:String>

src/Resources/Locales/zh_TW.axaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
<x:String x:Key="Text.BranchCM.CompareWithCurrent" xml:space="preserve">與目前 ${0}$ 比較</x:String>
6262
<x:String x:Key="Text.BranchCM.CompareWithWorktree" xml:space="preserve">與本機工作區比較</x:String>
6363
<x:String x:Key="Text.BranchCM.CopyName" xml:space="preserve">複製分支名稱</x:String>
64+
<x:String x:Key="Text.BranchCM.CreatePR" xml:space="preserve">建立拉取請求...</x:String>
65+
<x:String x:Key="Text.BranchCM.CreatePRForUpstream" xml:space="preserve">為上游分支 ${0}$ 建立拉取請求...</x:String>
6466
<x:String x:Key="Text.BranchCM.CustomAction" xml:space="preserve">自訂動作</x:String>
6567
<x:String x:Key="Text.BranchCM.Delete" xml:space="preserve">刪除 ${0}$...</x:String>
6668
<x:String x:Key="Text.BranchCM.DeleteMultiBranches" xml:space="preserve">刪除所選的 {0} 個分支</x:String>

src/Views/BranchTree.axaml.cs

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,24 @@ private ContextMenu CreateContextMenuForLocalBranch(ViewModels.Repository repo,
842842
menu.Items.Add(new MenuItem() { Header = "-" });
843843
menu.Items.Add(createBranch);
844844
menu.Items.Add(createTag);
845+
846+
if (upstream != null)
847+
{
848+
var remote = repo.Remotes.Find(x => x.Name.Equals(upstream.Remote, StringComparison.Ordinal));
849+
if (remote != null && remote.TryGetCreatePullRequestURL(out var prURL, upstream.Name))
850+
{
851+
var createPR = new MenuItem();
852+
createPR.Header = App.Text("BranchCM.CreatePRForUpstream", upstream.FriendlyName);
853+
createPR.Icon = App.CreateMenuIcon("Icons.CreatePR");
854+
createPR.Click += (_, e) =>
855+
{
856+
Native.OS.OpenBrowser(prURL);
857+
e.Handled = true;
858+
};
859+
860+
menu.Items.Add(createPR);
861+
}
862+
}
845863
menu.Items.Add(new MenuItem() { Header = "-" });
846864
TryToAddCustomActionsToBranchContextMenu(repo, menu, branch);
847865

@@ -1060,6 +1078,9 @@ public ContextMenu CreateContextMenuForRemoteBranch(ViewModels.Repository repo,
10601078
e.Handled = true;
10611079
};
10621080

1081+
menu.Items.Add(delete);
1082+
menu.Items.Add(new MenuItem() { Header = "-" });
1083+
10631084
var createBranch = new MenuItem();
10641085
createBranch.Icon = App.CreateMenuIcon("Icons.Branch.Add");
10651086
createBranch.Header = App.Text("CreateBranch");
@@ -1080,6 +1101,26 @@ public ContextMenu CreateContextMenuForRemoteBranch(ViewModels.Repository repo,
10801101
e.Handled = true;
10811102
};
10821103

1104+
menu.Items.Add(createBranch);
1105+
menu.Items.Add(createTag);
1106+
1107+
var remote = repo.Remotes.Find(x => x.Name.Equals(branch.Remote, StringComparison.Ordinal));
1108+
if (remote != null && remote.TryGetCreatePullRequestURL(out var prURL, branch.Name))
1109+
{
1110+
var createPR = new MenuItem();
1111+
createPR.Header = App.Text("BranchCM.CreatePR");
1112+
createPR.Icon = App.CreateMenuIcon("Icons.CreatePR");
1113+
createPR.Click += (_, e) =>
1114+
{
1115+
Native.OS.OpenBrowser(prURL);
1116+
e.Handled = true;
1117+
};
1118+
1119+
menu.Items.Add(createPR);
1120+
}
1121+
1122+
menu.Items.Add(new MenuItem() { Header = "-" });
1123+
10831124
var archive = new MenuItem();
10841125
archive.Icon = App.CreateMenuIcon("Icons.Archive");
10851126
archive.Header = App.Text("Archive");
@@ -1099,16 +1140,10 @@ public ContextMenu CreateContextMenuForRemoteBranch(ViewModels.Repository repo,
10991140
e.Handled = true;
11001141
};
11011142

1102-
menu.Items.Add(delete);
1103-
menu.Items.Add(new MenuItem() { Header = "-" });
1104-
menu.Items.Add(createBranch);
1105-
menu.Items.Add(createTag);
1106-
menu.Items.Add(new MenuItem() { Header = "-" });
11071143
menu.Items.Add(archive);
11081144
menu.Items.Add(new MenuItem() { Header = "-" });
11091145
TryToAddCustomActionsToBranchContextMenu(repo, menu, branch);
11101146
menu.Items.Add(copy);
1111-
11121147
return menu;
11131148
}
11141149

0 commit comments

Comments
 (0)