diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6a38c174..223fe75f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -25,6 +25,7 @@ jobs:
with:
version: ${{ needs.version.outputs.version }}
publish-packages:
+ needs: [package, version]
name: Publish Packages
uses: ./.github/workflows/publish-packages.yml
secrets:
diff --git a/README.md b/README.md
index 06842e37..6916edbc 100644
--- a/README.md
+++ b/README.md
@@ -47,7 +47,7 @@
## Translation Status
-[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-99.58%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-99.86%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-97.03%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-97.45%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-98.87%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-100.00%25-brightgreen)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-100.00%25-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-100.00%25-brightgreen)](TRANSLATION.md)
+[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-97.50%25-yellow)](TRANSLATION.md) [![es__ES](https://img.shields.io/badge/es__ES-97.78%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-95.00%25-yellow)](TRANSLATION.md) [![it__IT](https://img.shields.io/badge/it__IT-95.56%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-96.81%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-97.92%25-yellow)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-100.00%25-brightgreen)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-100.00%25-brightgreen)](TRANSLATION.md)
## How to Use
@@ -98,6 +98,33 @@ For **macOS** users:
For **Linux** users:
+* For Debian/Ubuntu based distributions, you can add the `sourcegit` repository by following:
+ You may need to install curl and/or gpg first, if you're on a very minimal host:
+ ```shell
+ apt update && apt install curl gpg -y
+ ```
+ Install the registry signing key:
+ ```shell
+ curl -fsSL "https://packages.buildkite.com/sourcegit/sourcegit-deb/gpgkey" | gpg --dearmor -o /etc/apt/keyrings/sourcegit_sourcegit-deb-archive-keyring.gpg
+ ```
+ Configure the source:
+ ```shell
+ echo -e "deb [signed-by=/etc/apt/keyrings/sourcegit_sourcegit-deb-archive-keyring.gpg] https://packages.buildkite.com/sourcegit/sourcegit-deb/any/ any main\ndeb-src [signed-by=/etc/apt/keyrings/sourcegit_sourcegit-deb-archive-keyring.gpg] https://packages.buildkite.com/sourcegit/sourcegit-deb/any/ any main" > /etc/apt/sources.list.d/buildkite-sourcegit-sourcegit-deb.list
+ ```
+ Update your local repository and install the package:
+ ```shell
+ apt update && apt install sourcegit
+ ```
+* For RHEL/Fedora based distributions, you can add the `sourcegit` repository by following:
+ Configure the source:
+ ```shell
+ sudo sh -c 'echo -e "[sourcegit-rpm]\nname=sourcegit-rpm\nbaseurl=https://packages.buildkite.com/sourcegit/sourcegit-rpm/rpm_any/rpm_any/\$basearch\nenabled=1\nrepo_gpgcheck=1\ngpgcheck=0\ngpgkey=https://packages.buildkite.com/sourcegit/sourcegit-rpm/gpgkey\npriority=1"' > /etc/yum.repos.d/sourcegit-rpm.repo
+ ```
+ Install the package with this command:
+ ```shell
+ sudo dnf install -y sourcegit
+ ```
+* `Appimage` files can be found on [AppimageHub](https://appimage.github.io/SourceGit/)
* `xdg-open` must be installed to support open native file manager.
* Make sure [git-credential-manager](https://github.com/git-ecosystem/git-credential-manager/releases) is installed on your linux.
* Maybe you need to set environment variable `AVALONIA_SCREEN_SCALE_FACTORS`. See https://github.com/AvaloniaUI/Avalonia/wiki/Configuring-X11-per-monitor-DPI.
diff --git a/TRANSLATION.md b/TRANSLATION.md
index 761edbf6..6e743f14 100644
--- a/TRANSLATION.md
+++ b/TRANSLATION.md
@@ -1,37 +1,81 @@
-### de_DE.axaml: 99.58%
+### de_DE.axaml: 97.50%
Missing Keys
+- Text.BranchCM.MergeMultiBranches
+- Text.CommitCM.Merge
+- Text.CommitCM.MergeMultiple
- Text.CommitDetail.Files.Search
- Text.Diff.UseBlockNavigation
+- Text.FileCM.ResolveUsing
+- Text.Hotkeys.Global.Clone
+- Text.InProgress.CherryPick.Head
+- Text.InProgress.Merge.Operating
+- Text.InProgress.Rebase.StoppedAt
+- Text.InProgress.Revert.Head
+- Text.Merge.Source
+- Text.MergeMultiple
+- Text.MergeMultiple.CommitChanges
+- Text.MergeMultiple.Strategy
+- Text.MergeMultiple.Targets
+- Text.Repository.Skip
- Text.WorkingCopy.CommitToEdit
-### es_ES.axaml: 99.86%
+### es_ES.axaml: 97.78%
Missing Keys
+- Text.BranchCM.MergeMultiBranches
+- Text.CommitCM.Merge
+- Text.CommitCM.MergeMultiple
- Text.Diff.UseBlockNavigation
+- Text.FileCM.ResolveUsing
+- Text.Hotkeys.Global.Clone
+- Text.InProgress.CherryPick.Head
+- Text.InProgress.Merge.Operating
+- Text.InProgress.Rebase.StoppedAt
+- Text.InProgress.Revert.Head
+- Text.Merge.Source
+- Text.MergeMultiple
+- Text.MergeMultiple.CommitChanges
+- Text.MergeMultiple.Strategy
+- Text.MergeMultiple.Targets
+- Text.Repository.Skip
-### fr_FR.axaml: 97.03%
+### fr_FR.axaml: 95.00%
Missing Keys
+- Text.BranchCM.MergeMultiBranches
- Text.CherryPick.AppendSourceToMessage
- Text.CherryPick.Mainline.Tips
- Text.CommitCM.CherryPickMultiple
+- Text.CommitCM.Merge
+- Text.CommitCM.MergeMultiple
- Text.CommitDetail.Files.Search
- Text.Diff.UseBlockNavigation
- Text.Fetch.Force
+- Text.FileCM.ResolveUsing
+- Text.Hotkeys.Global.Clone
+- Text.InProgress.CherryPick.Head
+- Text.InProgress.Merge.Operating
+- Text.InProgress.Rebase.StoppedAt
+- Text.InProgress.Revert.Head
+- Text.Merge.Source
+- Text.MergeMultiple
+- Text.MergeMultiple.CommitChanges
+- Text.MergeMultiple.Strategy
+- Text.MergeMultiple.Targets
- Text.Preference.Appearance.FontSize
- Text.Preference.Appearance.FontSize.Default
- Text.Preference.Appearance.FontSize.Editor
@@ -44,18 +88,22 @@
- Text.Repository.HistoriesOrder
- Text.Repository.HistoriesOrder.ByDate
- Text.Repository.HistoriesOrder.Topo
+- Text.Repository.Skip
- Text.ScanRepositories
- Text.SHALinkCM.NavigateTo
- Text.WorkingCopy.CommitToEdit
-### it_IT.axaml: 97.45%
+### it_IT.axaml: 95.56%
Missing Keys
+- Text.BranchCM.MergeMultiBranches
+- Text.CommitCM.Merge
+- Text.CommitCM.MergeMultiple
- Text.CommitDetail.Files.Search
- Text.CommitDetail.Info.Children
- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
@@ -63,6 +111,16 @@
- Text.Configure.OpenAI.Preferred.Tip
- Text.Diff.UseBlockNavigation
- Text.Fetch.Force
+- Text.FileCM.ResolveUsing
+- Text.InProgress.CherryPick.Head
+- Text.InProgress.Merge.Operating
+- Text.InProgress.Rebase.StoppedAt
+- Text.InProgress.Revert.Head
+- Text.Merge.Source
+- Text.MergeMultiple
+- Text.MergeMultiple.CommitChanges
+- Text.MergeMultiple.Strategy
+- Text.MergeMultiple.Targets
- Text.Preference.General.ShowChildren
- Text.Repository.FilterCommits
- Text.Repository.FilterCommits.Default
@@ -71,36 +129,66 @@
- Text.Repository.HistoriesOrder
- Text.Repository.HistoriesOrder.ByDate
- Text.Repository.HistoriesOrder.Topo
+- Text.Repository.Skip
- Text.SHALinkCM.CopySHA
- Text.SHALinkCM.NavigateTo
- Text.WorkingCopy.CommitToEdit
-### pt_BR.axaml: 98.87%
+### pt_BR.axaml: 96.81%
Missing Keys
+- Text.BranchCM.MergeMultiBranches
+- Text.CommitCM.Merge
+- Text.CommitCM.MergeMultiple
- Text.CommitDetail.Files.Search
- Text.CommitDetail.Info.Children
- Text.Diff.UseBlockNavigation
- Text.Fetch.Force
+- Text.FileCM.ResolveUsing
+- Text.Hotkeys.Global.Clone
+- Text.InProgress.CherryPick.Head
+- Text.InProgress.Merge.Operating
+- Text.InProgress.Rebase.StoppedAt
+- Text.InProgress.Revert.Head
+- Text.Merge.Source
+- Text.MergeMultiple
+- Text.MergeMultiple.CommitChanges
+- Text.MergeMultiple.Strategy
+- Text.MergeMultiple.Targets
- Text.Preference.General.ShowChildren
- Text.Repository.FilterCommits
+- Text.Repository.Skip
- Text.SHALinkCM.NavigateTo
- Text.WorkingCopy.CommitToEdit
-### ru_RU.axaml: 100.00%
+### ru_RU.axaml: 97.92%
Missing Keys
-
+- Text.BranchCM.MergeMultiBranches
+- Text.CommitCM.Merge
+- Text.CommitCM.MergeMultiple
+- Text.FileCM.ResolveUsing
+- Text.Hotkeys.Global.Clone
+- Text.InProgress.CherryPick.Head
+- Text.InProgress.Merge.Operating
+- Text.InProgress.Rebase.StoppedAt
+- Text.InProgress.Revert.Head
+- Text.Merge.Source
+- Text.MergeMultiple
+- Text.MergeMultiple.CommitChanges
+- Text.MergeMultiple.Strategy
+- Text.MergeMultiple.Targets
+- Text.Repository.Skip
diff --git a/VERSION b/VERSION
index d72e21bd..50a1eb5b 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.42
\ No newline at end of file
+8.43
\ No newline at end of file
diff --git a/src/Commands/Branch.cs b/src/Commands/Branch.cs
index 4ec8da82..391aeeb2 100644
--- a/src/Commands/Branch.cs
+++ b/src/Commands/Branch.cs
@@ -2,6 +2,15 @@
{
public static class Branch
{
+ public static string ShowCurrent(string repo)
+ {
+ var cmd = new Command();
+ cmd.WorkingDirectory = repo;
+ cmd.Context = repo;
+ cmd.Args = $"branch --show-current";
+ return cmd.ReadToEnd().StdOut.Trim();
+ }
+
public static bool Create(string repo, string name, string basedOn)
{
var cmd = new Command();
diff --git a/src/Commands/Command.cs b/src/Commands/Command.cs
index 8d304410..96a5b9c9 100644
--- a/src/Commands/Command.cs
+++ b/src/Commands/Command.cs
@@ -74,7 +74,11 @@ namespace SourceGit.Commands
}
if (string.IsNullOrEmpty(e.Data))
+ {
+ errs.Add(string.Empty);
return;
+ }
+
if (TraitErrorAsOutput)
OnReadline(e.Data);
@@ -89,6 +93,7 @@ namespace SourceGit.Commands
return;
if (REG_PROGRESS().IsMatch(e.Data))
return;
+
errs.Add(e.Data);
};
@@ -99,12 +104,8 @@ namespace SourceGit.Commands
catch (Exception e)
{
if (RaiseError)
- {
- Dispatcher.UIThread.Invoke(() =>
- {
- App.RaiseException(Context, e.Message);
- });
- }
+ Dispatcher.UIThread.Post(() => App.RaiseException(Context, e.Message));
+
return false;
}
@@ -115,15 +116,15 @@ namespace SourceGit.Commands
int exitCode = proc.ExitCode;
proc.Close();
- if (!isCancelled && exitCode != 0 && errs.Count > 0)
+ if (!isCancelled && exitCode != 0)
{
if (RaiseError)
{
- Dispatcher.UIThread.Invoke(() =>
- {
- App.RaiseException(Context, string.Join("\n", errs));
- });
+ var errMsg = string.Join("\n", errs);
+ if (!string.IsNullOrWhiteSpace(errMsg))
+ Dispatcher.UIThread.Post(() => App.RaiseException(Context, errMsg));
}
+
return false;
}
diff --git a/src/Commands/Merge.cs b/src/Commands/Merge.cs
index cf2e285f..bd1f3653 100644
--- a/src/Commands/Merge.cs
+++ b/src/Commands/Merge.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Text;
namespace SourceGit.Commands
{
@@ -13,6 +15,29 @@ namespace SourceGit.Commands
Args = $"merge --progress {source} {mode}";
}
+ public Merge(string repo, List targets, bool autoCommit, string strategy, Action outputHandler)
+ {
+ _outputHandler = outputHandler;
+ WorkingDirectory = repo;
+ Context = repo;
+ TraitErrorAsOutput = true;
+
+ var builder = new StringBuilder();
+ builder.Append("merge --progress ");
+ if (!string.IsNullOrEmpty(strategy))
+ builder.Append($"--strategy={strategy} ");
+ if (!autoCommit)
+ builder.Append("--no-commit ");
+
+ foreach (var t in targets)
+ {
+ builder.Append(t);
+ builder.Append(' ');
+ }
+
+ Args = builder.ToString();
+ }
+
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
diff --git a/src/Commands/QueryCommitsWithFullMessage.cs b/src/Commands/QueryCommitsWithFullMessage.cs
index 116cb3cd..c15cdbe1 100644
--- a/src/Commands/QueryCommitsWithFullMessage.cs
+++ b/src/Commands/QueryCommitsWithFullMessage.cs
@@ -52,16 +52,28 @@ namespace SourceGit.Commands
_current.Commit.CommitterTime = ulong.Parse(line);
break;
default:
- if (line.Equals(_boundary, StringComparison.Ordinal))
- nextPartIdx = -1;
+ var boundary = rs.StdOut.IndexOf(_boundary, end + 1);
+ if (boundary > end)
+ {
+ _current.Message = rs.StdOut.Substring(start, boundary - start - 1);
+ end = boundary + _boundary.Length;
+ }
else
- _current.Message += line;
+ {
+ _current.Message = rs.StdOut.Substring(start);
+ end = rs.StdOut.Length - 2;
+ }
+
+ nextPartIdx = -1;
break;
}
nextPartIdx++;
start = end + 1;
+ if (start >= rs.StdOut.Length - 1)
+ break;
+
end = rs.StdOut.IndexOf('\n', start);
}
diff --git a/src/Models/MergeStrategy.cs b/src/Models/MergeStrategy.cs
new file mode 100644
index 00000000..ab1d446b
--- /dev/null
+++ b/src/Models/MergeStrategy.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace SourceGit.Models
+{
+ public class MergeStrategy
+ {
+ public string Name { get; internal set; }
+ public string Desc { get; internal set; }
+ public string Arg { get; internal set; }
+
+ public static List ForMultiple { get; private set; } = [
+ new MergeStrategy("Default", "Let Git automatically select a strategy", string.Empty),
+ new MergeStrategy("Octopus", "Attempt merging multiple heads", "octopus"),
+ new MergeStrategy("Ours", "Record the merge without modifying the tree", "ours"),
+ ];
+
+ public MergeStrategy(string n, string d, string a)
+ {
+ Name = n;
+ Desc = d;
+ Arg = a;
+ }
+ }
+}
diff --git a/src/Models/Remote.cs b/src/Models/Remote.cs
index 2b88c3be..3c452460 100644
--- a/src/Models/Remote.cs
+++ b/src/Models/Remote.cs
@@ -6,7 +6,7 @@ namespace SourceGit.Models
{
public partial class Remote
{
- [GeneratedRegex(@"^https?://([-a-zA-Z0-9:%._\+~#=]+@)?[-a-zA-Z0-9:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}(:[0-9]{1,5})?\b(/[-a-zA-Z0-9()@:%_\+.~#?&=]*)*(\.git)?$")]
+ [GeneratedRegex(@"^https?://([-a-zA-Z0-9:%._\+~#=]+@)?[-a-zA-Z0-9:%._\+~#=]{1,256}(\.[a-zA-Z0-9()]{1,6})?(:[0-9]{1,5})?\b(/[-a-zA-Z0-9()@:%_\+.~#?&=]+)+(\.git)?$")]
private static partial Regex REG_HTTPS();
[GeneratedRegex(@"^[\w\-]+@[\w\.\-]+(\:[0-9]+)?:[\w\-/~%]+/[\w\-\.%]+(\.git)?$")]
private static partial Regex REG_SSH1();
diff --git a/src/Resources/Icons.axaml b/src/Resources/Icons.axaml
index 8fa23fb7..3eeb4b41 100644
--- a/src/Resources/Icons.axaml
+++ b/src/Resources/Icons.axaml
@@ -15,9 +15,9 @@
M797 829a49 49 0 1049 49 49 49 0 00-49-49zm147-114A49 49 0 10992 764a49 49 0 00-49-49zM928 861a49 49 0 1049 49A49 49 0 00928 861zm-5-586L992 205 851 64l-71 71a67 67 0 00-94 0l235 235a67 67 0 000-94zm-853 128a32 32 0 00-32 50 1291 1291 0 0075 112L288 552c20 0 25 21 8 37l-93 86a1282 1282 0 00120 114l100-32c19-6 28 15 14 34l-40 55c26 19 53 36 82 53a89 89 0 00115-20 1391 1391 0 00256-485l-188-188s-306 224-595 198z
M1280 704c0 141-115 256-256 256H288C129 960 0 831 0 672c0-126 80-232 192-272A327 327 0 01192 384c0-177 143-320 320-320 119 0 222 64 277 160C820 204 857 192 896 192c106 0 192 86 192 192 0 24-5 48-13 69C1192 477 1280 580 1280 704zm-493-128H656V352c0-18-14-32-32-32h-96c-18 0-32 14-32 32v224h-131c-29 0-43 34-23 55l211 211c12 12 33 12 45 0l211-211c20-20 6-55-23-55z
M853 102H171C133 102 102 133 102 171v683C102 891 133 922 171 922h683C891 922 922 891 922 853V171C922 133 891 102 853 102zM390 600l-48 48L205 512l137-137 48 48L301 512l88 88zM465 819l-66-18L559 205l66 18L465 819zm218-171L634 600 723 512l-88-88 48-48L819 512 683 649z
- M320 171A21 21 0 00299 192v213c0 49-32 85-61 107 30 22 61 58 61 107V832A21 21 0 00320 853h85v85H320A107 107 0 01213 832V619c0-11-8-26-32-42a157 157 0 00-33-17c-11-4-18-5-20-5v-85c2 0 9-1 20-5a157 157 0 0033-17c24-16 32-32 32-42V192A107 107 0 01320 85h85v85H320zm384 0h-85V85H704A107 107 0 01811 192v213c0 11 8 26 32 42 11 7 22 13 33 17 11 4 18 5 20 5v85c-2 0-9 1-20 5a157 157 0 00-33 17c-24 16-32 31-32 42V832A107 107 0 01704 939h-85v-85H704A21 21 0 00725 832V619c0-49 32-85 61-107-30-22-61-58-61-107V192A21 21 0 00704 171z
+ M684 736 340 736l0-53 344 1-0 53zM552 565l-213-2 0-53 212 2-0 53zM684 392 340 392l0-53 344 1-0 53zM301 825c-45 0-78-9-100-27-22-18-33-43-33-75v-116c0-22-4-37-12-45-7-9-20-13-40-13v-61c19 0 32-4 40-12 8-9 12-24 12-46v-116c0-32 11-57 33-75 22-18 56-27 100-27h24v61h-24a35 35 0 00-27 12 41 41 0 00-11 29v116c0 35-10 60-31 75a66 66 0 01-31 14c11 2 22 6 31 14 20 17 31 42 31 75v116c0 12 4 22 11 29 7 8 16 12 27 12h24v61h-24zM701 764h24c10 0 19-4 27-12a41 41 0 0011-29v-116c0-33 10-58 31-75 9-7 19-12 31-14a66 66 0 01-31-14c-20-15-31-40-31-75v-116a41 41 0 00-11-29 35 35 0 00-27-12h-24v-61h24c45 0 78 9 100 27 22 18 33 43 33 75v116c0 22 4 37 11 46 8 8 21 12 40 12v61c-19 0-33 4-40 13-7 8-11 23-11 45v116c0 32-11 57-33 75-22 18-55 27-100 27h-24v-61z
M128 854h768v86H128zM390 797c13 13 29 19 48 19s35-6 45-19l291-288c26-22 26-64 0-90L435 83l-61 61L426 192l-272 269c-22 22-22 64 0 90l237 246zm93-544 211 211-32 32H240l243-243zM707 694c0 48 38 86 86 86 48 0 86-38 86-86 0-22-10-45-26-61L794 576l-61 61c-13 13-26 35-26 58z
- M796 471A292 292 0 00512 256a293 293 0 00-284 215H0v144h228A293 293 0 00512 832a291 291 0 00284-217H1024V471h-228M512 688A146 146 0 01366 544A145 145 0 01512 400c80 0 146 63 146 144A146 146 0 01512 688
+ M0 512M1024 512M512 0M512 1024M796 471A292 292 0 00512 256a293 293 0 00-284 215H0v144h228A293 293 0 00512 832a291 291 0 00284-217H1024V471h-228M512 688A146 146 0 01366 544A145 145 0 01512 400c80 0 146 63 146 144A146 146 0 01512 688
M796 561a5 5 0 014 7l-39 90a5 5 0 004 7h100a5 5 0 014 8l-178 247a5 5 0 01-9-4l32-148a5 5 0 00-5-6h-89a5 5 0 01-4-7l86-191a5 5 0 014-3h88zM731 122a73 73 0 0173 73v318a54 54 0 00-8-1H731V195H244v634h408l-16 73H244a73 73 0 01-73-73V195a73 73 0 0173-73h488zm-219 366v73h-195v-73h195zm146-146v73H317v-73h341z
M645 448l64 64 220-221L704 64l-64 64 115 115H128v90h628zM375 576l-64-64-220 224L314 960l64-64-116-115H896v-90H262z
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
diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml
index bb31fa8f..2045a229 100644
--- a/src/Resources/Locales/de_DE.axaml
+++ b/src/Resources/Locales/de_DE.axaml
@@ -389,10 +389,10 @@
Verwerfen
Initialisiere Repository
Pfad:
- Cherry-Pick wird durchgeführt. Drücke 'Abbrechen' um den originalen HEAD wiederherzustellen.
- Merge request wird durchgeführt. Drücke 'Abbrechen' um den originalen HEAD wiederherzustellen.
- Rebase wird durchgeführt. Drücke 'Abbrechen' um den originalen HEAD wiederherzustellen.
- Revert wird durchgeführt. Drücke 'Abbrechen' um den originalen HEAD wiederherzustellen.
+ Cherry-Pick wird durchgeführt.
+ Merge request wird durchgeführt.
+ Rebase wird durchgeführt.
+ Revert wird durchgeführt.
Interaktiver Rebase
Ziel Branch:
Auf:
@@ -403,7 +403,6 @@
Branch mergen
Ziel-Branch:
Merge Option:
- Quell-Branch:
Bewege Repository Knoten
Wähle Vorgänger-Knoten für:
Name:
@@ -562,7 +561,6 @@
Aktualisiern
REMOTES
REMOTE HINZUFÜGEN
- KONFLIKTE BEHEBEN
Commit suchen
Dateiname
Commit-Nachricht
diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml
index 89309bef..65b4dd96 100644
--- a/src/Resources/Locales/en_US.axaml
+++ b/src/Resources/Locales/en_US.axaml
@@ -58,6 +58,7 @@
Fetch ${0}$ into ${1}$...
Git Flow - Finish ${0}$
Merge ${0}$ into ${1}$...
+ Merge selected {0} branches into current
Pull ${0}$
Pull ${0}$ into ${1}$...
Push ${0}$
@@ -101,7 +102,7 @@
Repository URL:
CLOSE
Editor
- Cherry-Pick This Commit
+ Cherry-Pick Commit
Cherry-Pick ...
Checkout Commit
Compare with HEAD
@@ -109,14 +110,16 @@
Copy Info
Copy SHA
Custom Action
- Interactive Rebase ${0}$ to Here
- Rebase ${0}$ to Here
+ Interactively Rebase ${0}$ on Here
+ Merge to ${0}$
+ Merge ...
+ Rebase ${0}$ on Here
Reset ${0}$ to Here
Revert Commit
Reword
Save as Patch...
Squash into Parent
- Squash Child Commits to Here
+ Squash Children into Here
CHANGES
Search Changes...
FILES
@@ -279,6 +282,7 @@
Discard {0} files...
Discard Changes in Selected Line(s)
Open External Merge Tool
+ Resolve Using ${0}$
Save as Patch...
Stage
Stage {0} files
@@ -357,6 +361,7 @@
Keyboard Shortcuts Reference
GLOBAL
Cancel current popup
+ Clone new repository
Close current page
Go to previous page
Go to next page
@@ -388,10 +393,14 @@
Discard
Initialize Repository
Path:
- Cherry-Pick in progress. Press 'Abort' to restore original HEAD.
- Merge request in progress. Press 'Abort' to restore original HEAD.
- Rebase in progress. Press 'Abort' to restore original HEAD.
- Revert in progress. Press 'Abort' to restore original HEAD.
+ Cherry-Pick in progress.
+ Processing commit
+ Merge request in progress.
+ Operating
+ Rebase in progress.
+ Stopped at
+ Revert in progress.
+ Reverting commit
Interactive Rebase
Target Branch:
On:
@@ -402,7 +411,11 @@
Merge Branch
Into:
Merge Option:
- Source Branch:
+ Source:
+ Merge (Multiple)
+ Commit all changes
+ Strategy:
+ Targets:
Move Repository Node
Select parent node for:
Name:
@@ -561,7 +574,6 @@
Refresh
REMOTES
ADD REMOTE
- RESOLVE
Search Commit
File
Message
@@ -569,6 +581,7 @@
Author & Committer
Current Branch
Show Tags as Tree
+ SKIP
Statistics
SUBMODULES
ADD SUBMODULE
diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml
index 7c7f8d7c..cf0f05b5 100644
--- a/src/Resources/Locales/es_ES.axaml
+++ b/src/Resources/Locales/es_ES.axaml
@@ -392,10 +392,10 @@
Descartar
Inicializar Repositorio
Ruta:
- Cherry-Pick en progreso. Presiona 'Abort' para restaurar el HEAD original.
- Merge en progreso. Presiona 'Abort' para restaurar el HEAD original.
- Rebase en progreso. Presiona 'Abort' para restaurar el HEAD original.
- Revert en progreso. Presiona 'Abort' para restaurar el HEAD original.
+ Cherry-Pick en progreso.
+ Merge en progreso.
+ Rebase en progreso.
+ Revert en progreso.
Rebase Interactivo
Rama Objetivo:
En:
@@ -404,7 +404,6 @@
Merge Rama
En:
Opción de Merge:
- Rama Fuente:
Mover Nodo del Repositorio
Seleccionar nodo padre para:
Nombre:
@@ -564,7 +563,6 @@
Refrescar
REMOTOS
AÑADIR REMOTO
- RESOLVER
Buscar Commit
Archivo
Mensaje
diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml
index a9a18be3..20237bb9 100644
--- a/src/Resources/Locales/fr_FR.axaml
+++ b/src/Resources/Locales/fr_FR.axaml
@@ -386,10 +386,10 @@
Rejeter
Initialiser le repository
Chemin :
- Cherry-Pick en cours. Appuyer sur 'Abort' pour restaurer le HEAD d'origine.
- Merge request in progress. Appuyer sur 'Abort' pour restaurer le HEAD d'origine.
- Rebase in progress. Appuyer sur 'Abort' pour restaurer le HEAD d'origine.
- Revert in progress. Appuyer sur 'Abort' pour restaurer le HEAD d'origine.
+ Cherry-Pick en cours.
+ Merge request in progress.
+ Rebase in progress.
+ Revert in progress.
Rebase interactif
Branche cible :
Sur :
@@ -400,7 +400,6 @@
Merger la branche
Dans :
Option de merge:
- Branche source :
Déplacer le noeud du repository
Sélectionnier le noeud parent pour :
Nom :
@@ -549,7 +548,6 @@
Rafraîchir
DEPOTS DISTANTS
AJOUTER DEPOT DISTANT
- RESOUDRE
Rechercher un commit
Fichier
Message
diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml
index 2e3acf3e..18754c8e 100644
--- a/src/Resources/Locales/it_IT.axaml
+++ b/src/Resources/Locales/it_IT.axaml
@@ -357,6 +357,7 @@
Riferimento Scorciatoie da Tastiera
GLOBALE
Annulla il popup corrente
+ Clona una nuova repository
Chiudi la pagina corrente
Vai alla pagina precedente
Vai alla pagina successiva
@@ -388,10 +389,10 @@
Scarta
Inizializza Repository
Percorso:
- Cherry-Pick in corso. Premi 'Annulla' per ripristinare l'HEAD originale.
- Richiesta di merge in corso. Premi 'Annulla' per ripristinare l'HEAD originale.
- Rebase in corso. Premi 'Annulla' per ripristinare l'HEAD originale.
- Revert in corso. Premi 'Annulla' per ripristinare l'HEAD originale.
+ Cherry-Pick in corso.
+ Richiesta di merge in corso.
+ Rebase in corso.
+ Revert in corso.
Rebase Interattivo
Branch di destinazione:
Su:
@@ -402,7 +403,6 @@
Unisci Branch
In:
Opzione di Merge:
- Branch Sorgente:
Sposta Nodo Repository
Seleziona nodo padre per:
Nome:
@@ -554,7 +554,6 @@
Aggiorna
REMOTI
AGGIUNGI REMOTO
- RISOLVI
Cerca Commit
File
Messaggio
diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml
index 4d011b32..d72dd370 100644
--- a/src/Resources/Locales/pt_BR.axaml
+++ b/src/Resources/Locales/pt_BR.axaml
@@ -412,10 +412,10 @@
Descartar
Inicializar Repositório
Caminho:
- Cherry-Pick em andamento. Pressione 'Abort' para restaurar o HEAD original.
- Merge em andamento. Pressione 'Abort' para restaurar o HEAD original.
- Rebase em andamento. Pressione 'Abort' para restaurar o HEAD original.
- Revert em andamento. Pressione 'Abort' para restaurar o HEAD original.
+ Cherry-Pick em andamento.
+ Merge em andamento.
+ Rebase em andamento.
+ Revert em andamento.
Rebase Interativo
Ramo Alvo:
Em:
@@ -426,7 +426,6 @@
Mesclar Ramo
Para:
Opção de Mesclagem:
- Ramo de Origem:
Mover nó do repositório
Selecionar nó pai para:
Nome:
@@ -583,7 +582,6 @@
Atualizar
REMOTOS
ADICIONAR REMOTO
- RESOLVER
Pesquisar Commit
Arquivo
Mensagem
diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml
index 5b2cf129..76142274 100644
--- a/src/Resources/Locales/ru_RU.axaml
+++ b/src/Resources/Locales/ru_RU.axaml
@@ -392,10 +392,10 @@
Отклонить
Инициализировать хранилище
Путь:
- Выполняется частичный забор. Нажмите «Отказ» для восстановления заголовка.
- Выполняет запрос слияния. Нажмите «Отказ» для восстановления заголовка.
- Выполняется перенос. Нажмите «Отказ» для восстановления заголовка.
- Выполняется возврат. Нажмите «Отказ» для восстановления заголовка.
+ Выполняется частичный забор.
+ Выполняет запрос слияния.
+ Выполняется перенос.
+ Выполняется возврат.
Интерактивное перемещение
Целевая ветка:
На:
@@ -406,7 +406,6 @@
Слить ветку
В:
Опции слияния:
- Исходная ветка:
Переместить узел хранилища
Выбрать родительский узел для:
Имя:
@@ -566,7 +565,6 @@
Обновить
ВНЕШНИЕ ХРАНИЛИЩА
ДОБАВИТЬ ВНЕШНЕЕ ХРАНИЛИЩЕ
- РАЗРЕШИТЬ
Поиск фиксации
Файл
Сообщение
diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml
index d6bbad3f..f711cdee 100644
--- a/src/Resources/Locales/zh_CN.axaml
+++ b/src/Resources/Locales/zh_CN.axaml
@@ -61,6 +61,7 @@
拉取(fetch) ${0}$ 至 ${1}$...
GIT工作流 - 完成 ${0}$
合并 ${0}$ 到 ${1}$...
+ 合并 {0} 个分支到当前分支
拉回(pull) ${0}$
拉回(pull) ${0}$ 内容至 ${1}$...
推送(push)${0}$
@@ -113,6 +114,8 @@
复制提交指纹
自定义操作
交互式变基(rebase -i) ${0}$ 到此处
+ 合并(merge)此提交至 ${0}$
+ 合并(merge)...
变基(rebase) ${0}$ 到此处
重置(reset) ${0}$ 到此处
回滚此提交
@@ -282,6 +285,7 @@
放弃 {0} 个文件的更改...
放弃选中的更改
使用外部合并工具打开
+ 应用 ${0}$
另存为补丁...
暂存(add)
暂存(add){0} 个文件
@@ -360,6 +364,7 @@
快捷键参考
全局快捷键
取消弹出面板
+ 克隆远程仓库
关闭当前页面
切换到上一个页面
切换到下一个页面
@@ -391,10 +396,14 @@
丢弃
初始化新仓库
路径 :
- 挑选(Cherry-Pick)操作进行中。点击【终止】回滚到操作前的状态。
- 合并操作进行中。点击【终止】回滚到操作前的状态。
- 变基(Rebase)操作进行中。点击【终止】回滚到操作前的状态。
- 回滚提交操作进行中。点击【终止】回滚到操作前的状态。
+ 挑选(Cherry-Pick)操作进行中。
+ 正在处理提交
+ 合并操作进行中。
+ 正在处理
+ 变基(Rebase)操作进行中。
+ 当前停止于
+ 回滚提交操作进行中。
+ 正在回滚提交
交互式变基
目标分支 :
起始提交 :
@@ -405,7 +414,11 @@
合并分支
目标分支 :
合并方式 :
- 合并分支 :
+ 合并目标 :
+ 合并(多目标)
+ 提交变化
+ 合并策略 :
+ 目标列表 :
调整仓库分组
请选择目标分组:
名称 :
@@ -565,7 +578,6 @@
重新加载
远程列表
添加远程
- 解决冲突
查找提交
文件
提交信息
@@ -573,6 +585,7 @@
作者及提交者
仅在当前分支中查找
以树型结构展示
+ 跳过此提交
提交统计
子模块列表
添加子模块
diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml
index 0034c3f4..90444967 100644
--- a/src/Resources/Locales/zh_TW.axaml
+++ b/src/Resources/Locales/zh_TW.axaml
@@ -61,6 +61,7 @@
提取 (fetch) ${0}$ 到 ${1}$...
Git 工作流 - 完成 ${0}$
合併 ${0}$ 到 ${1}$...
+ 合併 {0} 個分支到目前分支
拉取 (pull) ${0}$
拉取 (pull) ${0}$ 內容至 ${1}$...
推送 (push) ${0}$
@@ -113,6 +114,8 @@
複製提交編號
自訂動作
互動式重定基底 (rebase -i) ${0}$ 到此處
+ 合併 (merge) 此提交到 ${0}$
+ 合併 (merge)...
重定基底 (rebase) ${0}$ 到此處
重設 (reset) ${0}$ 到此處
復原此提交
@@ -282,6 +285,7 @@
捨棄已選的 {0} 個檔案變更...
捨棄選取的變更
使用外部合併工具開啟
+ 使用 ${0}$
另存為修補檔 (patch)...
暫存 (add)
暫存 (add) 已選的 {0} 個檔案
@@ -360,6 +364,7 @@
快速鍵參考
全域快速鍵
取消彈出面板
+ 複製 (clone) 遠端存放庫
關閉目前頁面
切換到上一個頁面
切換到下一個頁面
@@ -391,10 +396,14 @@
捨棄
初始化存放庫
路徑:
- 揀選 (cherry-pick) 操作進行中。點選 [中止] 復原到操作前的狀態。
- 合併操作進行中。點選 [中止] 復原到操作前的狀態。
- 重定基底 (rebase) 操作進行中。點選 [中止] 復原到操作前的狀態。
- 復原提交操作進行中。點選 [中止] 復原到操作前的狀態。
+ 揀選 (cherry-pick) 操作進行中。
+ 正在處理提交
+ 合併操作進行中。
+ 正在處理
+ 重定基底 (rebase) 操作進行中。
+ 当前停止于
+ 復原提交操作進行中。
+ 正在復原提交
互動式重定基底
目標分支:
起始提交:
@@ -405,7 +414,11 @@
合併分支
目標分支:
合併方式:
- 合併分支:
+ 合併目標:
+ 合併(多目標)
+ 提交變更
+ 合併策略:
+ 目標列表:
調整存放庫分組
請選擇目標分組:
名稱:
@@ -564,7 +577,6 @@
重新載入
遠端列表
新增遠端
- 解決衝突
搜尋提交
檔案
提交訊息
@@ -572,6 +584,7 @@
作者及提交者
僅搜尋目前分支
以樹型結構展示
+ 跳過此提交
提交統計
子模組列表
新增子模組
diff --git a/src/ViewModels/CherryPick.cs b/src/ViewModels/CherryPick.cs
index 19dac059..c3ee07cc 100644
--- a/src/ViewModels/CherryPick.cs
+++ b/src/ViewModels/CherryPick.cs
@@ -72,10 +72,9 @@ namespace SourceGit.ViewModels
return Task.Run(() =>
{
- var succ = false;
if (IsMergeCommit)
{
- succ = new Commands.CherryPick(
+ new Commands.CherryPick(
_repo.FullPath,
Targets[0].SHA,
!AutoCommit,
@@ -84,7 +83,7 @@ namespace SourceGit.ViewModels
}
else
{
- succ = new Commands.CherryPick(
+ new Commands.CherryPick(
_repo.FullPath,
string.Join(' ', Targets.ConvertAll(c => c.SHA)),
!AutoCommit,
@@ -93,7 +92,7 @@ namespace SourceGit.ViewModels
}
CallUIThread(() => _repo.SetWatcherEnabled(true));
- return succ;
+ return true;
});
}
diff --git a/src/ViewModels/Conflict.cs b/src/ViewModels/Conflict.cs
new file mode 100644
index 00000000..41a0a137
--- /dev/null
+++ b/src/ViewModels/Conflict.cs
@@ -0,0 +1,78 @@
+namespace SourceGit.ViewModels
+{
+ public class Conflict
+ {
+ public object Theirs
+ {
+ get;
+ private set;
+ }
+
+ public object Mine
+ {
+ get;
+ private set;
+ }
+
+ public bool IsResolved
+ {
+ get;
+ private set;
+ }
+
+ public Conflict(Repository repo, WorkingCopy wc, Models.Change change)
+ {
+ _wc = wc;
+ _change = change;
+
+ IsResolved = new Commands.IsConflictResolved(repo.FullPath, change).ReadToEnd().IsSuccess;
+
+ var context = wc.InProgressContext;
+ if (context is CherryPickInProgress cherryPick)
+ {
+ Theirs = cherryPick.Head;
+ Mine = repo.CurrentBranch;
+ }
+ else if (context is RebaseInProgress rebase)
+ {
+ Theirs = repo.Branches.Find(x => x.IsLocal && x.Name == rebase.HeadName) ??
+ new Models.Branch()
+ {
+ IsLocal = true,
+ Name = rebase.HeadName,
+ FullName = $"refs/heads/{rebase.HeadName}"
+ };
+
+ Mine = rebase.Onto;
+ }
+ else if (context is RevertInProgress revert)
+ {
+ Theirs = revert.Head;
+ Mine = repo.CurrentBranch;
+ }
+ else if (context is MergeInProgress merge)
+ {
+ Theirs = merge.Source;
+ Mine = repo.CurrentBranch;
+ }
+ }
+
+ public void UseTheirs()
+ {
+ _wc.UseTheirs([_change]);
+ }
+
+ public void UseMine()
+ {
+ _wc.UseMine([_change]);
+ }
+
+ public void OpenExternalMergeTool()
+ {
+ _wc.UseExternalMergeTool(_change);
+ }
+
+ private WorkingCopy _wc = null;
+ private Models.Change _change = null;
+ }
+}
diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs
index 18ca2e51..59f420eb 100644
--- a/src/ViewModels/Histories.cs
+++ b/src/ViewModels/Histories.cs
@@ -228,22 +228,28 @@ namespace SourceGit.ViewModels
{
var selected = new List();
var canCherryPick = true;
+ var canMerge = true;
+
foreach (var item in list.SelectedItems)
{
if (item is Models.Commit c)
{
selected.Add(c);
- if (c.IsMerged || c.Parents.Count > 1)
+ if (c.IsMerged)
+ {
+ canMerge = false;
canCherryPick = false;
+ }
+ else if (c.Parents.Count > 1)
+ {
+ canCherryPick = false;
+ }
}
}
// Sort selected commits in order.
- selected.Sort((l, r) =>
- {
- return _commits.IndexOf(r) - _commits.IndexOf(l);
- });
+ selected.Sort((l, r) => _commits.IndexOf(r) - _commits.IndexOf(l));
var multipleMenu = new ContextMenu();
@@ -259,9 +265,25 @@ namespace SourceGit.ViewModels
e.Handled = true;
};
multipleMenu.Items.Add(cherryPickMultiple);
- multipleMenu.Items.Add(new MenuItem() { Header = "-" });
}
+ if (canMerge)
+ {
+ var mergeMultiple = new MenuItem();
+ mergeMultiple.Header = App.Text("CommitCM.MergeMultiple");
+ mergeMultiple.Icon = App.CreateMenuIcon("Icons.Merge");
+ mergeMultiple.Click += (_, e) =>
+ {
+ if (PopupHost.CanCreatePopup())
+ PopupHost.ShowPopup(new MergeMultiple(_repo, selected));
+ e.Handled = true;
+ };
+ multipleMenu.Items.Add(mergeMultiple);
+ }
+
+ if (canCherryPick || canMerge)
+ multipleMenu.Items.Add(new MenuItem() { Header = "-" });
+
var saveToPatchMultiple = new MenuItem();
saveToPatchMultiple.Icon = App.CreateMenuIcon("Icons.Diff");
saveToPatchMultiple.Header = App.Text("CommitCM.SaveAsPatch");
@@ -385,24 +407,26 @@ namespace SourceGit.ViewModels
};
menu.Items.Add(reset);
- var squash = new MenuItem();
- squash.Header = App.Text("CommitCM.SquashCommitsSinceThis");
- squash.Icon = App.CreateMenuIcon("Icons.SquashIntoParent");
- squash.IsVisible = commit.IsMerged;
- squash.Click += (_, e) =>
+ if (commit.IsMerged)
{
- if (_repo.LocalChangesCount > 0)
+ var squash = new MenuItem();
+ squash.Header = App.Text("CommitCM.SquashCommitsSinceThis");
+ squash.Icon = App.CreateMenuIcon("Icons.SquashIntoParent");
+ squash.Click += (_, e) =>
{
- App.RaiseException(_repo.FullPath, "You have local changes. Please run stash or discard first.");
- return;
- }
+ if (_repo.LocalChangesCount > 0)
+ {
+ App.RaiseException(_repo.FullPath, "You have local changes. Please run stash or discard first.");
+ return;
+ }
- if (PopupHost.CanCreatePopup())
- PopupHost.ShowPopup(new Squash(_repo, commit, commit.SHA));
+ if (PopupHost.CanCreatePopup())
+ PopupHost.ShowPopup(new Squash(_repo, commit, commit.SHA));
- e.Handled = true;
- };
- menu.Items.Add(squash);
+ e.Handled = true;
+ };
+ menu.Items.Add(squash);
+ }
}
else
{
@@ -460,6 +484,21 @@ namespace SourceGit.ViewModels
};
menu.Items.Add(rebase);
+ if (!commit.HasDecorators)
+ {
+ var merge = new MenuItem();
+ merge.Header = new Views.NameHighlightedTextBlock("CommitCM.Merge", current.Name);
+ merge.Icon = App.CreateMenuIcon("Icons.Merge");
+ merge.Click += (_, e) =>
+ {
+ if (PopupHost.CanCreatePopup())
+ PopupHost.ShowPopup(new Merge(_repo, commit, current.Name));
+
+ e.Handled = true;
+ };
+ menu.Items.Add(merge);
+ }
+
var cherryPick = new MenuItem();
cherryPick.Header = App.Text("CommitCM.CherryPick");
cherryPick.Icon = App.CreateMenuIcon("Icons.CherryPick");
@@ -504,27 +543,6 @@ namespace SourceGit.ViewModels
e.Handled = true;
};
menu.Items.Add(revert);
-
- var interactiveRebase = new MenuItem();
- interactiveRebase.Header = new Views.NameHighlightedTextBlock("CommitCM.InteractiveRebase", current.Name);
- interactiveRebase.Icon = App.CreateMenuIcon("Icons.InteractiveRebase");
- interactiveRebase.IsVisible = current.Head != commit.SHA;
- interactiveRebase.Click += (_, e) =>
- {
- if (_repo.LocalChangesCount > 0)
- {
- App.RaiseException(_repo.FullPath, "You have local changes. Please run stash or discard first.");
- return;
- }
-
- App.OpenDialog(new Views.InteractiveRebase()
- {
- DataContext = new InteractiveRebase(_repo, current, commit)
- });
-
- e.Handled = true;
- };
- menu.Items.Add(interactiveRebase);
}
if (current.Head != commit.SHA)
@@ -543,6 +561,30 @@ namespace SourceGit.ViewModels
menu.Items.Add(new MenuItem() { Header = "-" });
+ if (commit.IsMerged && current.Head != commit.SHA)
+ {
+ var interactiveRebase = new MenuItem();
+ interactiveRebase.Header = new Views.NameHighlightedTextBlock("CommitCM.InteractiveRebase", current.Name);
+ interactiveRebase.Icon = App.CreateMenuIcon("Icons.InteractiveRebase");
+ interactiveRebase.Click += (_, e) =>
+ {
+ if (_repo.LocalChangesCount > 0)
+ {
+ App.RaiseException(_repo.FullPath, "You have local changes. Please run stash or discard first.");
+ return;
+ }
+
+ App.OpenDialog(new Views.InteractiveRebase()
+ {
+ DataContext = new InteractiveRebase(_repo, current, commit)
+ });
+
+ e.Handled = true;
+ };
+ menu.Items.Add(interactiveRebase);
+ menu.Items.Add(new MenuItem() { Header = "-" });
+ }
+
if (current.Head != commit.SHA)
{
var compareWithHead = new MenuItem();
@@ -825,8 +867,13 @@ namespace SourceGit.ViewModels
fastForward.IsEnabled = current.TrackStatus.Ahead.Count == 0;
fastForward.Click += (_, e) =>
{
+ var b = _repo.Branches.Find(x => x.FriendlyName == upstream);
+ if (b == null)
+ return;
+
if (PopupHost.CanCreatePopup())
- PopupHost.ShowAndStartPopup(new Merge(_repo, upstream, current.Name));
+ PopupHost.ShowAndStartPopup(new Merge(_repo, b, current.Name));
+
e.Handled = true;
};
submenu.Items.Add(fastForward);
@@ -921,7 +968,7 @@ namespace SourceGit.ViewModels
merge.Click += (_, e) =>
{
if (PopupHost.CanCreatePopup())
- PopupHost.ShowPopup(new Merge(_repo, branch.Name, current.Name));
+ PopupHost.ShowPopup(new Merge(_repo, branch, current.Name));
e.Handled = true;
};
submenu.Items.Add(merge);
@@ -1005,7 +1052,7 @@ namespace SourceGit.ViewModels
merge.Click += (_, e) =>
{
if (PopupHost.CanCreatePopup())
- PopupHost.ShowPopup(new Merge(_repo, name, current.Name));
+ PopupHost.ShowPopup(new Merge(_repo, branch, current.Name));
e.Handled = true;
};
@@ -1064,7 +1111,7 @@ namespace SourceGit.ViewModels
merge.Click += (_, e) =>
{
if (PopupHost.CanCreatePopup())
- PopupHost.ShowPopup(new Merge(_repo, tag.Name, current.Name));
+ PopupHost.ShowPopup(new Merge(_repo, tag, current.Name));
e.Handled = true;
};
submenu.Items.Add(merge);
diff --git a/src/ViewModels/InProgressContexts.cs b/src/ViewModels/InProgressContexts.cs
index f7b85032..78f845f3 100644
--- a/src/ViewModels/InProgressContexts.cs
+++ b/src/ViewModels/InProgressContexts.cs
@@ -4,31 +4,29 @@ namespace SourceGit.ViewModels
{
public abstract class InProgressContext
{
- public string Repository
- {
- get;
- set;
- }
-
- public string Cmd
- {
- get;
- set;
- }
-
public InProgressContext(string repo, string cmd)
{
- Repository = repo;
- Cmd = cmd;
+ _repo = repo;
+ _cmd = cmd;
}
public bool Abort()
{
return new Commands.Command()
{
- WorkingDirectory = Repository,
- Context = Repository,
- Args = $"{Cmd} --abort",
+ WorkingDirectory = _repo,
+ Context = _repo,
+ Args = $"{_cmd} --abort",
+ }.Exec();
+ }
+
+ public virtual bool Skip()
+ {
+ return new Commands.Command()
+ {
+ WorkingDirectory = _repo,
+ Context = _repo,
+ Args = $"{_cmd} --skip",
}.Exec();
}
@@ -36,32 +34,96 @@ namespace SourceGit.ViewModels
{
return new Commands.Command()
{
- WorkingDirectory = Repository,
- Context = Repository,
+ WorkingDirectory = _repo,
+ Context = _repo,
Editor = Commands.Command.EditorType.None,
- Args = $"{Cmd} --continue",
+ Args = $"{_cmd} --continue",
}.Exec();
}
+
+ protected string GetFriendlyNameOfCommit(Models.Commit commit)
+ {
+ var branchDecorator = commit.Decorators.Find(x => x.Type == Models.DecoratorType.LocalBranchHead || x.Type == Models.DecoratorType.RemoteBranchHead);
+ if (branchDecorator != null)
+ return branchDecorator.Name;
+
+ var tagDecorator = commit.Decorators.Find(x => x.Type == Models.DecoratorType.Tag);
+ if (tagDecorator != null)
+ return tagDecorator.Name;
+
+ return commit.SHA.Substring(0, 10);
+ }
+
+ protected string _repo = string.Empty;
+ protected string _cmd = string.Empty;
}
public class CherryPickInProgress : InProgressContext
{
- public CherryPickInProgress(string repo) : base(repo, "cherry-pick") { }
+ public Models.Commit Head
+ {
+ get;
+ private set;
+ }
+
+ public string HeadName
+ {
+ get => GetFriendlyNameOfCommit(Head);
+ }
+
+ public CherryPickInProgress(Repository repo) : base(repo.FullPath, "cherry-pick")
+ {
+ var headSHA = File.ReadAllText(Path.Combine(repo.GitDir, "CHERRY_PICK_HEAD")).Trim();
+ Head = new Commands.QuerySingleCommit(repo.FullPath, headSHA).Result() ?? new Models.Commit() { SHA = headSHA };
+ }
}
public class RebaseInProgress : InProgressContext
{
+ public string HeadName
+ {
+ get;
+ private set;
+ }
+
+ public string BaseName
+ {
+ get => GetFriendlyNameOfCommit(Onto);
+ }
+
+ public Models.Commit StoppedAt
+ {
+ get;
+ private set;
+ }
+
+ public Models.Commit Onto
+ {
+ get;
+ private set;
+ }
+
public RebaseInProgress(Repository repo) : base(repo.FullPath, "rebase")
{
_gitDir = repo.GitDir;
+
+ var stoppedSHA = File.ReadAllText(Path.Combine(repo.GitDir, "rebase-merge", "stopped-sha")).Trim();
+ StoppedAt = new Commands.QuerySingleCommit(repo.FullPath, stoppedSHA).Result() ?? new Models.Commit() { SHA = stoppedSHA };
+
+ var ontoSHA = File.ReadAllText(Path.Combine(repo.GitDir, "rebase-merge", "onto")).Trim();
+ Onto = new Commands.QuerySingleCommit(repo.FullPath, ontoSHA).Result() ?? new Models.Commit() { SHA = ontoSHA };
+
+ HeadName = File.ReadAllText(Path.Combine(repo.GitDir, "rebase-merge", "head-name")).Trim();
+ if (HeadName.StartsWith("refs/heads/"))
+ HeadName = HeadName.Substring(11);
}
public override bool Continue()
{
var succ = new Commands.Command()
{
- WorkingDirectory = Repository,
- Context = Repository,
+ WorkingDirectory = _repo,
+ Context = _repo,
Editor = Commands.Command.EditorType.RebaseEditor,
Args = $"rebase --continue",
}.Exec();
@@ -90,11 +152,49 @@ namespace SourceGit.ViewModels
public class RevertInProgress : InProgressContext
{
- public RevertInProgress(string repo) : base(repo, "revert") { }
+ public Models.Commit Head
+ {
+ get;
+ private set;
+ }
+
+ public RevertInProgress(Repository repo) : base(repo.FullPath, "revert")
+ {
+ var headSHA = File.ReadAllText(Path.Combine(repo.GitDir, "REVERT_HEAD")).Trim();
+ Head = new Commands.QuerySingleCommit(repo.FullPath, headSHA).Result() ?? new Models.Commit() { SHA = headSHA };
+ }
}
public class MergeInProgress : InProgressContext
{
- public MergeInProgress(string repo) : base(repo, "merge") { }
+ public string Current
+ {
+ get;
+ private set;
+ }
+
+ public Models.Commit Source
+ {
+ get;
+ private set;
+ }
+
+ public string SourceName
+ {
+ get => GetFriendlyNameOfCommit(Source);
+ }
+
+ public MergeInProgress(Repository repo) : base(repo.FullPath, "merge")
+ {
+ Current = Commands.Branch.ShowCurrent(repo.FullPath);
+
+ var sourceSHA = File.ReadAllText(Path.Combine(repo.GitDir, "MERGE_HEAD")).Trim();
+ Source = new Commands.QuerySingleCommit(repo.FullPath, sourceSHA).Result() ?? new Models.Commit() { SHA = sourceSHA };
+ }
+
+ public override bool Skip()
+ {
+ return true;
+ }
}
}
diff --git a/src/ViewModels/Merge.cs b/src/ViewModels/Merge.cs
index b7630101..d07ee9b7 100644
--- a/src/ViewModels/Merge.cs
+++ b/src/ViewModels/Merge.cs
@@ -5,7 +5,7 @@ namespace SourceGit.ViewModels
{
public class Merge : Popup
{
- public string Source
+ public object Source
{
get;
}
@@ -21,9 +21,33 @@ namespace SourceGit.ViewModels
set;
}
- public Merge(Repository repo, string source, string into)
+ public Merge(Repository repo, Models.Branch source, string into)
{
_repo = repo;
+ _sourceName = source.FriendlyName;
+
+ Source = source;
+ Into = into;
+ SelectedMode = AutoSelectMergeMode();
+ View = new Views.Merge() { DataContext = this };
+ }
+
+ public Merge(Repository repo, Models.Commit source, string into)
+ {
+ _repo = repo;
+ _sourceName = source.SHA;
+
+ Source = source;
+ Into = into;
+ SelectedMode = AutoSelectMergeMode();
+ View = new Views.Merge() { DataContext = this };
+ }
+
+ public Merge(Repository repo, Models.Tag source, string into)
+ {
+ _repo = repo;
+ _sourceName = source.Name;
+
Source = source;
Into = into;
SelectedMode = AutoSelectMergeMode();
@@ -33,11 +57,11 @@ namespace SourceGit.ViewModels
public override Task Sure()
{
_repo.SetWatcherEnabled(false);
- ProgressDescription = $"Merging '{Source}' into '{Into}' ...";
+ ProgressDescription = $"Merging '{_sourceName}' into '{Into}' ...";
return Task.Run(() =>
{
- var succ = new Commands.Merge(_repo.FullPath, Source, SelectedMode.Arg, SetProgressDescription).Exec();
+ var succ = new Commands.Merge(_repo.FullPath, _sourceName, SelectedMode.Arg, SetProgressDescription).Exec();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});
@@ -59,5 +83,6 @@ namespace SourceGit.ViewModels
}
private readonly Repository _repo = null;
+ private readonly string _sourceName = string.Empty;
}
}
diff --git a/src/ViewModels/MergeMultiple.cs b/src/ViewModels/MergeMultiple.cs
new file mode 100644
index 00000000..dd984a15
--- /dev/null
+++ b/src/ViewModels/MergeMultiple.cs
@@ -0,0 +1,93 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace SourceGit.ViewModels
+{
+ public class MergeMultiple : Popup
+ {
+ public List