feature: git command logs

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo 2025-04-17 12:30:20 +08:00
parent 928a0ad3c5
commit 8b39df32cc
No known key found for this signature in database
101 changed files with 1040 additions and 573 deletions

11
src/App.Utils.cs Normal file
View file

@ -0,0 +1,11 @@
namespace SourceGit
{
public static class CommandExtensions
{
public static T Use<T>(this T cmd, Models.ICommandLog log) where T : Commands.Command
{
cmd.Log = log;
return cmd;
}
}
}

View file

@ -1,23 +1,12 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Archive : Command
{
public Archive(string repo, string revision, string saveTo, Action<string> outputHandler)
public Archive(string repo, string revision, string saveTo)
{
WorkingDirectory = repo;
Context = repo;
Args = $"archive --format=zip --verbose --output=\"{saveTo}\" {revision}";
TraitErrorAsOutput = true;
_outputHandler = outputHandler;
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler;
}
}

View file

@ -1,75 +1,14 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public partial class AssumeUnchanged
public class AssumeUnchanged : Command
{
[GeneratedRegex(@"^(\w)\s+(.+)$")]
private static partial Regex REG_PARSE();
class ViewCommand : Command
public AssumeUnchanged(string repo, string file, bool bAdd)
{
public ViewCommand(string repo)
{
WorkingDirectory = repo;
Args = "ls-files -v";
RaiseError = false;
}
var mode = bAdd ? "--assume-unchanged" : "--no-assume-unchanged";
public List<string> Result()
{
Exec();
return _outs;
}
protected override void OnReadline(string line)
{
var match = REG_PARSE().Match(line);
if (!match.Success)
return;
if (match.Groups[1].Value == "h")
{
_outs.Add(match.Groups[2].Value);
}
}
private readonly List<string> _outs = new List<string>();
WorkingDirectory = repo;
Context = repo;
Args = $"update-index {mode} -- \"{file}\"";
}
class ModCommand : Command
{
public ModCommand(string repo, string file, bool bAdd)
{
var mode = bAdd ? "--assume-unchanged" : "--no-assume-unchanged";
WorkingDirectory = repo;
Context = repo;
Args = $"update-index {mode} -- \"{file}\"";
}
}
public AssumeUnchanged(string repo)
{
_repo = repo;
}
public List<string> View()
{
return new ViewCommand(_repo).Result();
}
public void Add(string file)
{
new ModCommand(_repo, file, true).Exec();
}
public void Remove(string file)
{
new ModCommand(_repo, file, false).Exec();
}
private readonly string _repo;
}
}

View file

@ -21,10 +21,17 @@ namespace SourceGit.Commands
public Models.BlameData Result()
{
var succ = Exec();
if (!succ)
var rs = ReadToEnd();
if (!rs.IsSuccess)
return _result;
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
return new Models.BlameData();
ParseLine(line);
if (_result.IsBinary)
break;
}
if (_needUnifyCommitSHA)
@ -42,13 +49,8 @@ namespace SourceGit.Commands
return _result;
}
protected override void OnReadline(string line)
private void ParseLine(string line)
{
if (_result.IsBinary)
return;
if (string.IsNullOrEmpty(line))
return;
if (line.IndexOf('\0', StringComparison.Ordinal) >= 0)
{
_result.IsBinary = true;

View file

@ -11,29 +11,32 @@
return cmd.ReadToEnd().StdOut.Trim();
}
public static bool Create(string repo, string name, string basedOn)
public static bool Create(string repo, string name, string basedOn, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"branch {name} {basedOn}";
cmd.Log = log;
return cmd.Exec();
}
public static bool Rename(string repo, string name, string to)
public static bool Rename(string repo, string name, string to, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"branch -M {name} {to}";
cmd.Log = log;
return cmd.Exec();
}
public static bool SetUpstream(string repo, string name, string upstream)
public static bool SetUpstream(string repo, string name, string upstream, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Log = log;
if (string.IsNullOrEmpty(upstream))
cmd.Args = $"branch {name} --unset-upstream";
@ -43,25 +46,27 @@
return cmd.Exec();
}
public static bool DeleteLocal(string repo, string name)
public static bool DeleteLocal(string repo, string name, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"branch -D {name}";
cmd.Log = log;
return cmd.Exec();
}
public static bool DeleteRemote(string repo, string remote, string name)
public static bool DeleteRemote(string repo, string remote, string name, Models.ICommandLog log)
{
bool exists = new Remote(repo).HasBranch(remote, name);
if (exists)
return new Push(repo, remote, $"refs/heads/{name}", true).Exec();
return new Push(repo, remote, $"refs/heads/{name}", true).Use(log).Exec();
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"branch -D -r {remote}/{name}";
cmd.Log = log;
return cmd.Exec();
}
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Text;
namespace SourceGit.Commands
@ -12,19 +11,15 @@ namespace SourceGit.Commands
Context = repo;
}
public bool Branch(string branch, Action<string> onProgress)
public bool Branch(string branch)
{
Args = $"checkout --recurse-submodules --progress {branch}";
TraitErrorAsOutput = true;
_outputHandler = onProgress;
return Exec();
}
public bool Branch(string branch, string basedOn, Action<string> onProgress)
public bool Branch(string branch, string basedOn)
{
Args = $"checkout --recurse-submodules --progress -b {branch} {basedOn}";
TraitErrorAsOutput = true;
_outputHandler = onProgress;
return Exec();
}
@ -62,19 +57,10 @@ namespace SourceGit.Commands
return Exec();
}
public bool Commit(string commitId, Action<string> onProgress)
public bool Commit(string commitId)
{
Args = $"checkout --detach --progress {commitId}";
TraitErrorAsOutput = true;
_outputHandler = onProgress;
return Exec();
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private Action<string> _outputHandler;
}
}

View file

@ -1,16 +1,11 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Clone : Command
{
private readonly Action<string> _notifyProgress;
public Clone(string ctx, string path, string url, string localName, string sshKey, string extraArgs, Action<string> ouputHandler)
public Clone(string ctx, string path, string url, string localName, string sshKey, string extraArgs)
{
Context = ctx;
WorkingDirectory = path;
TraitErrorAsOutput = true;
SSHKey = sshKey;
Args = "clone --progress --verbose ";
@ -21,13 +16,6 @@ namespace SourceGit.Commands
if (!string.IsNullOrEmpty(localName))
Args += localName;
_notifyProgress = ouputHandler;
}
protected override void OnReadline(string line)
{
_notifyProgress?.Invoke(line);
}
}
}

View file

@ -32,7 +32,7 @@ namespace SourceGit.Commands
public string SSHKey { get; set; } = string.Empty;
public string Args { get; set; } = string.Empty;
public bool RaiseError { get; set; } = true;
public bool TraitErrorAsOutput { get; set; } = false;
public Models.ICommandLog Log { get; set; } = null;
public bool Exec()
{
@ -40,10 +40,14 @@ namespace SourceGit.Commands
var errs = new List<string>();
var proc = new Process() { StartInfo = start };
Log?.AppendLine($"$ git {Args}\n");
proc.OutputDataReceived += (_, e) =>
{
if (e.Data != null)
OnReadline(e.Data);
if (e.Data == null)
return;
Log?.AppendLine(e.Data);
};
proc.ErrorDataReceived += (_, e) =>
@ -54,8 +58,7 @@ namespace SourceGit.Commands
return;
}
if (TraitErrorAsOutput)
OnReadline(e.Data);
Log?.AppendLine(e.Data);
// Ignore progress messages
if (e.Data.StartsWith("remote: Enumerating objects:", StringComparison.Ordinal))
@ -97,6 +100,7 @@ namespace SourceGit.Commands
if (RaiseError)
Dispatcher.UIThread.Post(() => App.RaiseException(Context, e.Message));
Log?.AppendLine(string.Empty);
return false;
}
@ -114,6 +118,7 @@ namespace SourceGit.Commands
int exitCode = proc.ExitCode;
proc.Close();
Log?.AppendLine(string.Empty);
if (!CancellationToken.IsCancellationRequested && exitCode != 0)
{
@ -162,11 +167,6 @@ namespace SourceGit.Commands
return rs;
}
protected virtual void OnReadline(string line)
{
// Implemented by derived class
}
private ProcessStartInfo CreateGitStartInfo()
{
var start = new ProcessStartInfo();

View file

@ -11,7 +11,6 @@ namespace SourceGit.Commands
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
Args = $"commit --allow-empty --file=\"{_tmpFile}\"";
if (amend)
Args += " --amend --no-edit";

View file

@ -31,12 +31,19 @@ namespace SourceGit.Commands
public List<Models.Change> Result()
{
Exec();
var rs = ReadToEnd();
if (!rs.IsSuccess)
return _changes;
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
ParseLine(line);
_changes.Sort((l, r) => string.Compare(l.Path, r.Path, StringComparison.Ordinal));
return _changes;
}
protected override void OnReadline(string line)
private void ParseLine(string line)
{
var match = REG_FORMAT().Match(line);
if (!match.Success)

View file

@ -5,13 +5,13 @@ namespace SourceGit.Commands
{
public static class Discard
{
public static void All(string repo, bool includeIgnored)
public static void All(string repo, bool includeIgnored, Models.ICommandLog log)
{
new Restore(repo).Exec();
new Clean(repo, includeIgnored).Exec();
new Restore(repo).Use(log).Exec();
new Clean(repo, includeIgnored).Use(log).Exec();
}
public static void Changes(string repo, List<Models.Change> changes)
public static void Changes(string repo, List<Models.Change> changes, Models.ICommandLog log)
{
var needClean = new List<string>();
var needCheckout = new List<string>();
@ -27,13 +27,13 @@ namespace SourceGit.Commands
for (int i = 0; i < needClean.Count; i += 10)
{
var count = Math.Min(10, needClean.Count - i);
new Clean(repo, needClean.GetRange(i, count)).Exec();
new Clean(repo, needClean.GetRange(i, count)).Use(log).Exec();
}
for (int i = 0; i < needCheckout.Count; i += 10)
{
var count = Math.Min(10, needCheckout.Count - i);
new Restore(repo, needCheckout.GetRange(i, count), "--worktree --recurse-submodules").Exec();
new Restore(repo, needCheckout.GetRange(i, count), "--worktree --recurse-submodules").Use(log).Exec();
}
}
}

View file

@ -1,15 +1,11 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Fetch : Command
{
public Fetch(string repo, string remote, bool noTags, bool force, Action<string> outputHandler)
public Fetch(string repo, string remote, bool noTags, bool force)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
Args = "fetch --progress --verbose ";
@ -24,21 +20,12 @@ namespace SourceGit.Commands
Args += remote;
}
public Fetch(string repo, Models.Branch local, Models.Branch remote, Action<string> outputHandler)
public Fetch(string repo, Models.Branch local, Models.Branch remote)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
SSHKey = new Config(repo).Get($"remote.{remote.Remote}.sshkey");
Args = $"fetch --progress --verbose {remote.Remote} {remote.Name}:{local.Name}";
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler;
}
}

View file

@ -1,23 +1,12 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class GC : Command
{
public GC(string repo, Action<string> outputHandler)
public GC(string repo)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
Args = "gc --prune=now";
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler;
}
}

View file

@ -35,17 +35,17 @@ namespace SourceGit.Commands
config.ContainsKey("gitflow.prefix.hotfix");
}
public static bool Init(string repo, List<Models.Branch> branches, string master, string develop, string feature, string release, string hotfix, string version)
public static bool Init(string repo, List<Models.Branch> branches, string master, string develop, string feature, string release, string hotfix, string version, Models.ICommandLog log)
{
var current = branches.Find(x => x.IsCurrent);
var masterBranch = branches.Find(x => x.Name == master);
if (masterBranch == null && current != null)
Branch.Create(repo, master, current.Head);
Branch.Create(repo, master, current.Head, log);
var devBranch = branches.Find(x => x.Name == develop);
if (devBranch == null && current != null)
Branch.Create(repo, develop, current.Head);
Branch.Create(repo, develop, current.Head, log);
var config = new Config(repo);
config.Set("gitflow.branch.master", master);
@ -61,6 +61,7 @@ namespace SourceGit.Commands
init.WorkingDirectory = repo;
init.Context = repo;
init.Args = "flow init -d";
init.Log = log;
return init.Exec();
}
@ -113,7 +114,7 @@ namespace SourceGit.Commands
return rs;
}
public static bool Start(string repo, string type, string name)
public static bool Start(string repo, string type, string name, Models.ICommandLog log)
{
if (!SUPPORTED_BRANCH_TYPES.Contains(type))
{
@ -129,10 +130,11 @@ namespace SourceGit.Commands
start.WorkingDirectory = repo;
start.Context = repo;
start.Args = $"flow {type} start {name}";
start.Log = log;
return start.Exec();
}
public static bool Finish(string repo, string type, string name, bool keepBranch)
public static bool Finish(string repo, string type, string name, bool keepBranch, Models.ICommandLog log)
{
if (!SUPPORTED_BRANCH_TYPES.Contains(type))
{
@ -149,6 +151,7 @@ namespace SourceGit.Commands
finish.WorkingDirectory = repo;
finish.Context = repo;
finish.Args = $"flow {type} finish {option} {name}";
finish.Log = log;
return finish.Exec();
}

View file

@ -10,5 +10,10 @@
Context = repo;
Args = $"diff -a --ignore-cr-at-eol --check {opt}";
}
public bool Result()
{
return ReadToEnd().IsSuccess;
}
}
}

View file

@ -12,21 +12,13 @@ namespace SourceGit.Commands
class SubCmd : Command
{
public SubCmd(string repo, string args, Action<string> onProgress)
public SubCmd(string repo, string args, Models.ICommandLog log)
{
WorkingDirectory = repo;
Context = repo;
Args = args;
TraitErrorAsOutput = true;
_outputHandler = onProgress;
Log = log;
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler;
}
public LFS(string repo)
@ -49,30 +41,30 @@ namespace SourceGit.Commands
return new SubCmd(_repo, "lfs install --local", null).Exec();
}
public bool Track(string pattern, bool isFilenameMode = false)
public bool Track(string pattern, bool isFilenameMode, Models.ICommandLog log)
{
var opt = isFilenameMode ? "--filename" : "";
return new SubCmd(_repo, $"lfs track {opt} \"{pattern}\"", null).Exec();
return new SubCmd(_repo, $"lfs track {opt} \"{pattern}\"", log).Exec();
}
public void Fetch(string remote, Action<string> outputHandler)
public void Fetch(string remote, Models.ICommandLog log)
{
new SubCmd(_repo, $"lfs fetch {remote}", outputHandler).Exec();
new SubCmd(_repo, $"lfs fetch {remote}", log).Exec();
}
public void Pull(string remote, Action<string> outputHandler)
public void Pull(string remote, Models.ICommandLog log)
{
new SubCmd(_repo, $"lfs pull {remote}", outputHandler).Exec();
new SubCmd(_repo, $"lfs pull {remote}", log).Exec();
}
public void Push(string remote, Action<string> outputHandler)
public void Push(string remote, Models.ICommandLog log)
{
new SubCmd(_repo, $"lfs push {remote}", outputHandler).Exec();
new SubCmd(_repo, $"lfs push {remote}", log).Exec();
}
public void Prune(Action<string> outputHandler)
public void Prune(Models.ICommandLog log)
{
new SubCmd(_repo, "lfs prune", outputHandler).Exec();
new SubCmd(_repo, "lfs prune", log).Exec();
}
public List<Models.LFSLock> Locks(string remote)

View file

@ -1,26 +1,21 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Text;
namespace SourceGit.Commands
{
public class Merge : Command
{
public Merge(string repo, string source, string mode, Action<string> outputHandler)
public Merge(string repo, string source, string mode)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
Args = $"merge --progress {source} {mode}";
}
public Merge(string repo, List<string> targets, bool autoCommit, string strategy, Action<string> outputHandler)
public Merge(string repo, List<string> targets, bool autoCommit, string strategy)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
var builder = new StringBuilder();
builder.Append("merge --progress ");
@ -37,12 +32,5 @@ namespace SourceGit.Commands
Args = builder.ToString();
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler = null;
}
}

View file

@ -1,15 +1,11 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Pull : Command
{
public Pull(string repo, string remote, string branch, bool useRebase, bool noTags, Action<string> outputHandler)
public Pull(string repo, string remote, string branch, bool useRebase, bool noTags)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
Args = "pull --verbose --progress ";
@ -21,12 +17,5 @@ namespace SourceGit.Commands
Args += $"{remote} {branch}";
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler;
}
}

View file

@ -1,16 +1,11 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Push : Command
{
public Push(string repo, string local, string remote, string remoteBranch, bool withTags, bool checkSubmodules, bool track, bool force, Action<string> onProgress)
public Push(string repo, string local, string remote, string remoteBranch, bool withTags, bool checkSubmodules, bool track, bool force)
{
_outputHandler = onProgress;
WorkingDirectory = repo;
Context = repo;
TraitErrorAsOutput = true;
SSHKey = new Config(repo).Get($"remote.{remote}.sshkey");
Args = "push --progress --verbose ";
@ -38,12 +33,5 @@ namespace SourceGit.Commands
Args += $"{remote} {refname}";
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private readonly Action<string> _outputHandler = null;
}
}

View file

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace SourceGit.Commands
{
public partial class QueryAssumeUnchangedFiles : Command
{
[GeneratedRegex(@"^(\w)\s+(.+)$")]
private static partial Regex REG_PARSE();
public QueryAssumeUnchangedFiles(string repo)
{
WorkingDirectory = repo;
Args = "ls-files -v";
RaiseError = false;
}
public List<string> Result()
{
var outs = new List<string>();
var rs = ReadToEnd();
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var match = REG_PARSE().Match(line);
if (!match.Success)
continue;
if (match.Groups[1].Value == "h")
outs.Add(match.Groups[2].Value);
}
return outs;
}
}
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace SourceGit.Commands
{
@ -14,17 +15,21 @@ namespace SourceGit.Commands
public List<string> Result()
{
Exec();
return _lines;
}
var rs = ReadToEnd();
var outs = new List<string>();
if (rs.IsSuccess)
{
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
if (line.Contains(_commit))
outs.Add(line.Substring(0, 40));
}
}
protected override void OnReadline(string line)
{
if (line.Contains(_commit))
_lines.Add(line.Substring(0, 40));
return outs;
}
private string _commit;
private List<string> _lines = new List<string>();
}
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace SourceGit.Commands
@ -18,21 +19,23 @@ namespace SourceGit.Commands
public List<Models.Change> Result()
{
Exec();
return _changes;
}
var outs = new List<Models.Change>();
var rs = ReadToEnd();
if (!rs.IsSuccess)
return outs;
protected override void OnReadline(string line)
{
var match = REG_FORMAT().Match(line);
if (!match.Success)
return;
var change = new Models.Change() { Path = match.Groups[2].Value };
var status = match.Groups[1].Value;
switch (status)
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var match = REG_FORMAT().Match(line);
if (!match.Success)
continue;
var change = new Models.Change() { Path = match.Groups[2].Value };
var status = match.Groups[1].Value;
switch (status)
{
case " M":
change.Set(Models.ChangeState.None, Models.ChangeState.Modified);
break;
@ -145,12 +148,14 @@ namespace SourceGit.Commands
change.Set(Models.ChangeState.Untracked, Models.ChangeState.Untracked);
break;
default:
return;
break;
}
if (change.Index != Models.ChangeState.None || change.WorkTree != Models.ChangeState.None)
outs.Add(change);
}
_changes.Add(change);
return outs;
}
private readonly List<Models.Change> _changes = new List<Models.Change>();
}
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace SourceGit.Commands
@ -17,27 +18,31 @@ namespace SourceGit.Commands
public List<Models.Remote> Result()
{
Exec();
return _loaded;
}
var outs = new List<Models.Remote>();
var rs = ReadToEnd();
if (!rs.IsSuccess)
return outs;
protected override void OnReadline(string line)
{
var match = REG_REMOTE().Match(line);
if (!match.Success)
return;
var remote = new Models.Remote()
var lines = rs.StdOut.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
Name = match.Groups[1].Value,
URL = match.Groups[2].Value,
};
var match = REG_REMOTE().Match(line);
if (!match.Success)
continue;
if (_loaded.Find(x => x.Name == remote.Name) != null)
return;
_loaded.Add(remote);
var remote = new Models.Remote()
{
Name = match.Groups[1].Value,
URL = match.Groups[2].Value,
};
if (outs.Find(x => x.Name == remote.Name) != null)
continue;
outs.Add(remote);
}
return outs;
}
private readonly List<Models.Remote> _loaded = new List<Models.Remote>();
}
}

View file

@ -14,35 +14,50 @@ namespace SourceGit.Commands
public List<Models.Stash> Result()
{
Exec();
return _stashes;
}
var outs = new List<Models.Stash>();
var rs = ReadToEnd();
if (!rs.IsSuccess)
return outs;
protected override void OnReadline(string line)
{
switch (_nextLineIdx)
var nextPartIdx = 0;
var start = 0;
var end = rs.StdOut.IndexOf('\n', start);
while (end > 0)
{
case 0:
_current = new Models.Stash() { SHA = line };
_stashes.Add(_current);
break;
case 1:
ParseParent(line);
break;
case 2:
_current.Time = ulong.Parse(line);
break;
case 3:
_current.Name = line;
break;
case 4:
_current.Message = line;
break;
var line = rs.StdOut.Substring(start, end - start);
switch (nextPartIdx)
{
case 0:
_current = new Models.Stash() { SHA = line };
outs.Add(_current);
break;
case 1:
ParseParent(line);
break;
case 2:
_current.Time = ulong.Parse(line);
break;
case 3:
_current.Name = line;
break;
case 4:
_current.Message = line;
break;
}
nextPartIdx++;
if (nextPartIdx > 4)
nextPartIdx = 0;
start = end + 1;
end = rs.StdOut.IndexOf('\n', start);
}
_nextLineIdx++;
if (_nextLineIdx > 4)
_nextLineIdx = 0;
if (start < rs.StdOut.Length)
_current.Message = rs.StdOut.Substring(start);
return outs;
}
private void ParseParent(string data)
@ -53,8 +68,6 @@ namespace SourceGit.Commands
_current.Parents.AddRange(data.Split(separator: ' ', options: StringSplitOptions.RemoveEmptyEntries));
}
private readonly List<Models.Stash> _stashes = new List<Models.Stash>();
private Models.Stash _current = null;
private int _nextLineIdx = 0;
}
}

View file

@ -1,6 +1,4 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class Submodule : Command
{
@ -10,9 +8,8 @@ namespace SourceGit.Commands
Context = repo;
}
public bool Add(string url, string relativePath, bool recursive, Action<string> outputHandler)
public bool Add(string url, string relativePath, bool recursive)
{
_outputHandler = outputHandler;
Args = $"submodule add {url} \"{relativePath}\"";
if (!Exec())
return false;
@ -29,7 +26,7 @@ namespace SourceGit.Commands
}
}
public bool Update(string module, bool init, bool recursive, bool useRemote, Action<string> outputHandler)
public bool Update(string module, bool init, bool recursive, bool useRemote)
{
Args = "submodule update";
@ -42,7 +39,6 @@ namespace SourceGit.Commands
if (!string.IsNullOrEmpty(module))
Args += $" -- \"{module}\"";
_outputHandler = outputHandler;
return Exec();
}
@ -55,12 +51,5 @@ namespace SourceGit.Commands
Args = $"rm -rf \"{relativePath}\"";
return Exec();
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private Action<string> _outputHandler;
}
}

View file

@ -1,26 +1,27 @@
using System.Collections.Generic;
using System.IO;
using System.IO;
namespace SourceGit.Commands
{
public static class Tag
{
public static bool Add(string repo, string name, string basedOn)
public static bool Add(string repo, string name, string basedOn, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"tag {name} {basedOn}";
cmd.Log = log;
return cmd.Exec();
}
public static bool Add(string repo, string name, string basedOn, string message, bool sign)
public static bool Add(string repo, string name, string basedOn, string message, bool sign, Models.ICommandLog log)
{
var param = sign ? "--sign -a" : "--no-sign -a";
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"tag {param} {name} {basedOn} ";
cmd.Log = log;
if (!string.IsNullOrEmpty(message))
{
@ -36,22 +37,14 @@ namespace SourceGit.Commands
return cmd.Exec();
}
public static bool Delete(string repo, string name, List<Models.Remote> remotes)
public static bool Delete(string repo, string name, Models.ICommandLog log)
{
var cmd = new Command();
cmd.WorkingDirectory = repo;
cmd.Context = repo;
cmd.Args = $"tag --delete {name}";
if (!cmd.Exec())
return false;
if (remotes != null)
{
foreach (var r in remotes)
new Push(repo, r.Name, $"refs/tags/{name}", true).Exec();
}
return true;
cmd.Log = log;
return cmd.Exec();
}
}
}

View file

@ -1,23 +1,12 @@
using System;
namespace SourceGit.Commands
namespace SourceGit.Commands
{
public class UpdateRef : Command
{
public UpdateRef(string repo, string refName, string toRevision, Action<string> outputHandler)
public UpdateRef(string repo, string refName, string toRevision)
{
_outputHandler = outputHandler;
WorkingDirectory = repo;
Context = repo;
Args = $"update-ref {refName} {toRevision}";
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private Action<string> _outputHandler;
}
}

View file

@ -56,7 +56,7 @@ namespace SourceGit.Commands
return worktrees;
}
public bool Add(string fullpath, string name, bool createNew, string tracking, Action<string> outputHandler)
public bool Add(string fullpath, string name, bool createNew, string tracking)
{
Args = "worktree add ";
@ -78,14 +78,12 @@ namespace SourceGit.Commands
else if (!string.IsNullOrEmpty(name) && !createNew)
Args += name;
_outputHandler = outputHandler;
return Exec();
}
public bool Prune(Action<string> outputHandler)
public bool Prune()
{
Args = "worktree prune -v";
_outputHandler = outputHandler;
return Exec();
}
@ -101,22 +99,14 @@ namespace SourceGit.Commands
return Exec();
}
public bool Remove(string fullpath, bool force, Action<string> outputHandler)
public bool Remove(string fullpath, bool force)
{
if (force)
Args = $"worktree remove -f \"{fullpath}\"";
else
Args = $"worktree remove \"{fullpath}\"";
_outputHandler = outputHandler;
return Exec();
}
protected override void OnReadline(string line)
{
_outputHandler?.Invoke(line);
}
private Action<string> _outputHandler = null;
}
}

View file

@ -22,7 +22,6 @@ namespace SourceGit.Models
new ConventionalCommitType("Styles", "style", "Elements or code styles without changing the code logic"),
new ConventionalCommitType("Tests", "test", "Adding or updating tests"),
new ConventionalCommitType("Chores", "chore", "Other changes that don't modify src or test files"),
};
public ConventionalCommitType(string name, string type, string description)

View file

@ -0,0 +1,7 @@
namespace SourceGit.Models
{
public interface ICommandLog
{
void AppendLine(string line);
}
}

View file

@ -78,6 +78,7 @@
<StreamGeometry x:Key="Icons.Loading">M512 0C233 0 7 223 0 500C6 258 190 64 416 64c230 0 416 200 416 448c0 53 43 96 96 96s96-43 96-96c0-283-229-512-512-512zm0 1023c279 0 505-223 512-500c-6 242-190 436-416 436c-230 0-416-200-416-448c0-53-43-96-96-96s-96 43-96 96c0 283 229 512 512 512z</StreamGeometry>
<StreamGeometry x:Key="Icons.Local">M976 0h-928A48 48 0 000 48v652a48 48 0 0048 48h416V928H200a48 48 0 000 96h624a48 48 0 000-96H560v-180h416a48 48 0 0048-48V48A48 48 0 00976 0zM928 652H96V96h832v556z</StreamGeometry>
<StreamGeometry x:Key="Icons.Lock">M832 464h-68V240a128 128 0 00-128-128h-248a128 128 0 00-128 128v224H192c-18 0-32 14-32 32v384c0 18 14 32 32 32h640c18 0 32-14 32-32v-384c0-18-14-32-32-32zm-292 237v53a8 8 0 01-8 8h-40a8 8 0 01-8-8v-53a48 48 0 1156 0zm152-237H332V240a56 56 0 0156-56h248a56 56 0 0156 56v224z</StreamGeometry>
<StreamGeometry x:Key="Icons.Logs">M908 366h-25V248a18 18 0 00-0-2 20 20 0 00-5-13L681 7 681 7a19 19 0 00-4-3c-0-0-1-1-1-1a29 29 0 00-4-2L671 1a24 24 0 00-5-1H181a40 40 0 00-40 40v326h-25c-32 0-57 26-57 57v298c0 32 26 57 57 57h25v204c0 22 18 40 40 40H843a40 40 0 0040-40v-204h25c32 0 57-26 57-57V424a57 57 0 00-57-57zM181 40h465v205c0 11 9 20 20 20h177v101H181V40zm413 527c0 89-54 143-134 143-81 0-128-61-128-138 0-82 52-143 132-143 84 0 129 63 129 138zm-440 139V433h62v220h108v52h-170zm690 267H181v-193H843l0 193zm18-280a305 305 0 01-91 15c-50 0-86-12-111-37-25-23-39-59-38-99 0-90 66-142 155-142 35 0 62 7 76 13l-13 49c-15-6-33-12-63-12-51 0-90 29-90 88 0 56 35 89 86 89 14 0 25-2 30-4v-57h-42v-48h101v143zM397 570c0 53 25 91 66 91 42 0 65-40 65-92 0-49-23-91-66-91-42 0-66 40-66 93z</StreamGeometry>
<StreamGeometry x:Key="Icons.Menu">M192 192m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0ZM192 512m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0ZM192 832m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0ZM864 160H352c-17.7 0-32 14.3-32 32s14.3 32 32 32h512c17.7 0 32-14.3 32-32s-14.3-32-32-32zM864 480H352c-17.7 0-32 14.3-32 32s14.3 32 32 32h512c17.7 0 32-14.3 32-32s-14.3-32-32-32zM864 800H352c-17.7 0-32 14.3-32 32s14.3 32 32 32h512c17.7 0 32-14.3 32-32s-14.3-32-32-32z</StreamGeometry>
<StreamGeometry x:Key="Icons.Merge">M824 645V307c0-56-46-102-102-102h-102V102l-154 154 154 154V307h102v338c-46 20-82 67-82 123 0 72 61 133 133 133 72 0 133-61 133-133 0-56-36-102-82-123zm-51 195c-41 0-72-31-72-72s31-72 72-72c41 0 72 31 72 72s-31 72-72 72zM384 256c0-72-61-133-133-133-72 0-133 61-133 133 0 56 36 102 82 123v266C154 666 118 712 118 768c0 72 61 133 133 133 72 0 133-61 133-133 0-56-36-102-82-123V379C348 358 384 312 384 256zM323 768c0 41-31 72-72 72-41 0-72-31-72-72s31-72 72-72c41 0 72 31 72 72zM251 328c-41 0-72-31-72-72s31-72 72-72c41 0 72 31 72 72s-31 72-72 72z</StreamGeometry>
<StreamGeometry x:Key="Icons.Modified">M896 64H128C96 64 64 96 64 128v768c0 32 32 64 64 64h768c32 0 64-32 64-64V128c0-32-32-64-64-64z m-64 736c0 16-17 32-32 32H224c-18 0-32-12-32-32V224c0-16 16-32 32-32h576c15 0 32 16 32 32v576zM512 384c-71 0-128 57-128 128s57 128 128 128 128-57 128-128-57-128-128-128z</StreamGeometry>

View file

@ -612,6 +612,7 @@
<x:String x:Key="Text.Repository.Tags.Sort" xml:space="preserve">Sort</x:String>
<x:String x:Key="Text.Repository.Terminal" xml:space="preserve">Open in Terminal</x:String>
<x:String x:Key="Text.Repository.UseRelativeTimeInHistories" xml:space="preserve">Use relative time in histories</x:String>
<x:String x:Key="Text.Repository.ViewLogs" xml:space="preserve">View Logs</x:String>
<x:String x:Key="Text.Repository.Worktrees" xml:space="preserve">WORKTREES</x:String>
<x:String x:Key="Text.Repository.Worktrees.Add" xml:space="preserve">ADD WORKTREE</x:String>
<x:String x:Key="Text.Repository.Worktrees.Prune" xml:space="preserve">PRUNE</x:String>
@ -697,6 +698,7 @@
<x:String x:Key="Text.UpdateSubmodules.Target" xml:space="preserve">Submodule:</x:String>
<x:String x:Key="Text.UpdateSubmodules.UseRemote" xml:space="preserve">Use --remote option</x:String>
<x:String x:Key="Text.URL" xml:space="preserve">URL:</x:String>
<x:String x:Key="Text.ViewLogs" xml:space="preserve">Logs</x:String>
<x:String x:Key="Text.Warn" xml:space="preserve">Warning</x:String>
<x:String x:Key="Text.Welcome" xml:space="preserve">Welcome Page</x:String>
<x:String x:Key="Text.Welcome.AddRootFolder" xml:space="preserve">Create Group</x:String>

View file

@ -616,6 +616,7 @@
<x:String x:Key="Text.Repository.Tags.Sort" xml:space="preserve">排序</x:String>
<x:String x:Key="Text.Repository.Terminal" xml:space="preserve">在终端中打开</x:String>
<x:String x:Key="Text.Repository.UseRelativeTimeInHistories" xml:space="preserve">在提交列表中使用相对时间</x:String>
<x:String x:Key="Text.Repository.ViewLogs" xml:space="preserve">查看命令日志</x:String>
<x:String x:Key="Text.Repository.Worktrees" xml:space="preserve">工作树列表</x:String>
<x:String x:Key="Text.Repository.Worktrees.Add" xml:space="preserve">新增工作树</x:String>
<x:String x:Key="Text.Repository.Worktrees.Prune" xml:space="preserve">清理</x:String>
@ -701,6 +702,7 @@
<x:String x:Key="Text.UpdateSubmodules.Target" xml:space="preserve">子模块 </x:String>
<x:String x:Key="Text.UpdateSubmodules.UseRemote" xml:space="preserve">启用 '--remote'</x:String>
<x:String x:Key="Text.URL" xml:space="preserve">仓库地址 </x:String>
<x:String x:Key="Text.ViewLogs" xml:space="preserve">日志列表</x:String>
<x:String x:Key="Text.Warn" xml:space="preserve">警告</x:String>
<x:String x:Key="Text.Welcome" xml:space="preserve">起始页</x:String>
<x:String x:Key="Text.Welcome.AddRootFolder" xml:space="preserve">新建分组</x:String>
@ -757,4 +759,4 @@
<x:String x:Key="Text.Worktree.Lock" xml:space="preserve">锁定工作树</x:String>
<x:String x:Key="Text.Worktree.Remove" xml:space="preserve">移除工作树</x:String>
<x:String x:Key="Text.Worktree.Unlock" xml:space="preserve">解除工作树锁定</x:String>
</ResourceDictionary>
</ResourceDictionary>

View file

@ -616,6 +616,7 @@
<x:String x:Key="Text.Repository.Tags.Sort" xml:space="preserve">排序</x:String>
<x:String x:Key="Text.Repository.Terminal" xml:space="preserve">在終端機中開啟</x:String>
<x:String x:Key="Text.Repository.UseRelativeTimeInHistories" xml:space="preserve">在提交列表中使用相對時間</x:String>
<x:String x:Key="Text.Repository.ViewLogs" xml:space="preserve">檢視 GIT 指令的日誌</x:String>
<x:String x:Key="Text.Repository.Worktrees" xml:space="preserve">工作區列表</x:String>
<x:String x:Key="Text.Repository.Worktrees.Add" xml:space="preserve">新增工作區</x:String>
<x:String x:Key="Text.Repository.Worktrees.Prune" xml:space="preserve">清理</x:String>
@ -701,6 +702,7 @@
<x:String x:Key="Text.UpdateSubmodules.Target" xml:space="preserve">子模組:</x:String>
<x:String x:Key="Text.UpdateSubmodules.UseRemote" xml:space="preserve">啟用 [--remote] 選項</x:String>
<x:String x:Key="Text.URL" xml:space="preserve">存放庫網址:</x:String>
<x:String x:Key="Text.ViewLogs" xml:space="preserve">日誌清單</x:String>
<x:String x:Key="Text.Warn" xml:space="preserve">警告</x:String>
<x:String x:Key="Text.Welcome" xml:space="preserve">起始頁</x:String>
<x:String x:Key="Text.Welcome.AddRootFolder" xml:space="preserve">新增群組</x:String>
@ -757,4 +759,4 @@
<x:String x:Key="Text.Worktree.Lock" xml:space="preserve">鎖定工作區</x:String>
<x:String x:Key="Text.Worktree.Remove" xml:space="preserve">移除工作區</x:String>
<x:String x:Key="Text.Worktree.Unlock" xml:space="preserve">解除鎖定工作區</x:String>
</ResourceDictionary>
</ResourceDictionary>

View file

@ -93,15 +93,19 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Adding remote ...";
var log = _repo.CreateLog("Add Remote");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.Remote(_repo.FullPath).Add(_name, _url);
var succ = new Commands.Remote(_repo.FullPath).Use(log).Add(_name, _url);
if (succ)
{
SetProgressDescription("Fetching from added remote ...");
new Commands.Config(_repo.FullPath).Set($"remote.{_name}.sshkey", _useSSH ? SSHKey : null);
new Commands.Fetch(_repo.FullPath, _name, false, false, SetProgressDescription).Exec();
new Commands.Config(_repo.FullPath).Use(log).Set($"remote.{_name}.sshkey", _useSSH ? SSHKey : null);
new Commands.Fetch(_repo.FullPath, _name, false, false).Use(log).Exec();
}
log.Complete();
CallUIThread(() =>
{
_repo.MarkFetched();

View file

@ -61,9 +61,14 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Adding submodule...";
var log = _repo.CreateLog("Add Submodule");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.Submodule(_repo.FullPath).Add(_url, _relativePath, Recursive, SetProgressDescription);
var succ = new Commands.Submodule(_repo.FullPath).Use(log).Add(_url, _relativePath, Recursive);
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -114,10 +114,15 @@ namespace SourceGit.ViewModels
var branchName = _selectedBranch;
var tracking = _setTrackingBranch ? SelectedTrackingBranch : string.Empty;
var log = _repo.CreateLog("Add Worktree");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.Worktree(_repo.FullPath).Add(_path, branchName, _createNewBranch, tracking, SetProgressDescription);
var succ = new Commands.Worktree(_repo.FullPath).Use(log).Add(_path, branchName, _createNewBranch, tracking);
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -47,9 +47,12 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Apply patch...";
var log = _repo.CreateLog("Apply Patch");
return Task.Run(() =>
{
var succ = new Commands.Apply(_repo.FullPath, _patchFile, _ignoreWhiteSpace, SelectedWhiteSpaceMode.Arg, null).Exec();
var succ = new Commands.Apply(_repo.FullPath, _patchFile, _ignoreWhiteSpace, SelectedWhiteSpaceMode.Arg, null).Use(log).Exec();
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -22,7 +22,7 @@ namespace SourceGit.ViewModels
set;
} = false;
public ApplyStash(string repo, Models.Stash stash)
public ApplyStash(Repository repo, Models.Stash stash)
{
_repo = repo;
Stash = stash;
@ -33,16 +33,18 @@ namespace SourceGit.ViewModels
{
ProgressDescription = $"Applying stash: {Stash.Name}";
var log = _repo.CreateLog("Apply Stash");
return Task.Run(() =>
{
var succ = new Commands.Stash(_repo).Apply(Stash.Name, RestoreIndex);
var succ = new Commands.Stash(_repo.FullPath).Use(log).Apply(Stash.Name, RestoreIndex);
if (succ && DropAfterApply)
new Commands.Stash(_repo).Drop(Stash.Name);
new Commands.Stash(_repo.FullPath).Use(log).Drop(Stash.Name);
log.Complete();
return true;
});
}
private readonly string _repo;
private readonly Repository _repo;
}
}

View file

@ -51,9 +51,14 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Archiving ...";
var log = _repo.CreateLog("Archive");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.Archive(_repo.FullPath, _revision, _saveFile, SetProgressDescription).Exec();
var succ = new Commands.Archive(_repo.FullPath, _revision, _saveFile).Use(log).Exec();
log.Complete();
CallUIThread(() =>
{
_repo.SetWatcherEnabled(true);

View file

@ -16,11 +16,8 @@ namespace SourceGit.ViewModels
Task.Run(() =>
{
var collect = new Commands.AssumeUnchanged(_repo).View();
Dispatcher.UIThread.Invoke(() =>
{
Files.AddRange(collect);
});
var collect = new Commands.QueryAssumeUnchangedFiles(_repo).Result();
Dispatcher.UIThread.Invoke(() => Files.AddRange(collect));
});
}
@ -28,7 +25,7 @@ namespace SourceGit.ViewModels
{
if (!string.IsNullOrEmpty(file))
{
new Commands.AssumeUnchanged(_repo).Remove(file);
new Commands.AssumeUnchanged(_repo, file, false).Exec();
Files.Remove(file);
}
}

View file

@ -28,6 +28,9 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Checkout '{Branch}' ...";
var log = _repo.CreateLog($"Checkout '{Branch}'");
Use(log);
return Task.Run(() =>
{
var changes = new Commands.CountLocalChangesWithoutUntracked(_repo.FullPath).Result();
@ -36,15 +39,14 @@ namespace SourceGit.ViewModels
{
if (DiscardLocalChanges)
{
SetProgressDescription("Discard local changes ...");
Commands.Discard.All(_repo.FullPath, false);
Commands.Discard.All(_repo.FullPath, false, log);
}
else
{
SetProgressDescription("Stash local changes ...");
var succ = new Commands.Stash(_repo.FullPath).Push("CHECKOUT_AUTO_STASH");
var succ = new Commands.Stash(_repo.FullPath).Use(log).Push("CHECKOUT_AUTO_STASH");
if (!succ)
{
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return false;
}
@ -53,14 +55,11 @@ namespace SourceGit.ViewModels
}
}
SetProgressDescription("Checkout branch ...");
var rs = new Commands.Checkout(_repo.FullPath).Branch(Branch, SetProgressDescription);
var rs = new Commands.Checkout(_repo.FullPath).Use(log).Branch(Branch);
if (needPopStash)
{
SetProgressDescription("Re-apply local changes...");
rs = new Commands.Stash(_repo.FullPath).Pop("stash@{0}");
}
rs = new Commands.Stash(_repo.FullPath).Use(log).Pop("stash@{0}");
log.Complete();
CallUIThread(() =>
{

View file

@ -28,6 +28,9 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Checkout Commit '{Commit.SHA}' ...";
var log = _repo.CreateLog("Checkout Commit");
Use(log);
return Task.Run(() =>
{
var changes = new Commands.CountLocalChangesWithoutUntracked(_repo.FullPath).Result();
@ -36,15 +39,14 @@ namespace SourceGit.ViewModels
{
if (DiscardLocalChanges)
{
SetProgressDescription("Discard local changes ...");
Commands.Discard.All(_repo.FullPath, false);
Commands.Discard.All(_repo.FullPath, false, log);
}
else
{
SetProgressDescription("Stash local changes ...");
var succ = new Commands.Stash(_repo.FullPath).Push("CHECKOUT_AUTO_STASH");
var succ = new Commands.Stash(_repo.FullPath).Use(log).Push("CHECKOUT_AUTO_STASH");
if (!succ)
{
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return false;
}
@ -53,15 +55,11 @@ namespace SourceGit.ViewModels
}
}
SetProgressDescription("Checkout commit ...");
var rs = new Commands.Checkout(_repo.FullPath).Commit(Commit.SHA, SetProgressDescription);
var rs = new Commands.Checkout(_repo.FullPath).Use(log).Commit(Commit.SHA);
if (needPopStash)
{
SetProgressDescription("Re-apply local changes...");
rs = new Commands.Stash(_repo.FullPath).Pop("stash@{0}");
}
rs = new Commands.Stash(_repo.FullPath).Use(log).Pop("stash@{0}");
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return rs;
});

View file

@ -70,6 +70,9 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Cherry-Pick commit(s) ...";
var log = _repo.CreateLog("Cherry-Pick");
Use(log);
return Task.Run(() =>
{
if (IsMergeCommit)
@ -79,7 +82,7 @@ namespace SourceGit.ViewModels
Targets[0].SHA,
!AutoCommit,
AppendSourceToMessage,
$"-m {MainlineForMergeCommit + 1}").Exec();
$"-m {MainlineForMergeCommit + 1}").Use(log).Exec();
}
else
{
@ -88,9 +91,10 @@ namespace SourceGit.ViewModels
string.Join(' ', Targets.ConvertAll(c => c.SHA)),
!AutoCommit,
AppendSourceToMessage,
string.Empty).Exec();
string.Empty).Use(log).Exec();
}
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return true;
});

View file

@ -15,9 +15,13 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Cleanup (GC & prune) ...";
var log = _repo.CreateLog("Cleanup (GC & prune)");
Use(log);
return Task.Run(() =>
{
new Commands.GC(_repo.FullPath, SetProgressDescription).Exec();
new Commands.GC(_repo.FullPath).Use(log).Exec();
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return true;
});

View file

@ -15,9 +15,13 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Clear all stashes...";
var log = _repo.CreateLog("Clear Stashes");
Use(log);
return Task.Run(() =>
{
new Commands.Stash(_repo.FullPath).Clear();
new Commands.Stash(_repo.FullPath).Use(log).Clear();
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return true;
});

View file

@ -103,9 +103,13 @@ namespace SourceGit.ViewModels
{
ProgressDescription = "Clone ...";
// Create a temp log.
var log = new CommandLog("Clone");
Use(log);
return Task.Run(() =>
{
var cmd = new Commands.Clone(_pageId, _parentFolder, _remote, _local, _useSSH ? _sshKey : "", _extraArgs, SetProgressDescription);
var cmd = new Commands.Clone(_pageId, _parentFolder, _remote, _local, _useSSH ? _sshKey : "", _extraArgs).Use(log);
if (!cmd.Exec())
return false;
@ -142,12 +146,11 @@ namespace SourceGit.ViewModels
{
var submoduleList = new Commands.QuerySubmodules(path).Result();
foreach (var submodule in submoduleList)
{
var update = new Commands.Submodule(path);
update.Update(submodule.Path, true, true, false, SetProgressDescription);
}
new Commands.Submodule(path).Use(log).Update(submodule.Path, true, true, false);
}
log.Complete();
CallUIThread(() =>
{
var node = Preferences.Instance.FindOrAddNodeByRepositoryPath(path, null, true);

View file

@ -0,0 +1,87 @@
using System;
using System.Text;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
{
public class CommandLog : ObservableObject, Models.ICommandLog
{
public string Name
{
get;
private set;
} = string.Empty;
public DateTime Time
{
get;
} = DateTime.Now;
public string TimeStr
{
get => Time.ToString("T");
}
public bool IsComplete
{
get;
private set;
} = false;
public string Content
{
get
{
return IsComplete ? _content : _builder.ToString();
}
}
public CommandLog(string name)
{
Name = name;
}
public void Register(Action<string> handler)
{
if (!IsComplete)
_onNewLineReceived += handler;
}
public void AppendLine(string line = null)
{
var newline = line ?? string.Empty;
Dispatcher.UIThread.Invoke(() =>
{
_builder.AppendLine(newline);
_onNewLineReceived?.Invoke(newline);
});
}
public void Complete()
{
IsComplete = true;
Dispatcher.UIThread.Invoke(() =>
{
_content = _builder.ToString();
_builder.Clear();
_builder = null;
OnPropertyChanged(nameof(IsComplete));
if (_onNewLineReceived != null)
{
var dumpHandlers = _onNewLineReceived.GetInvocationList();
foreach (var d in dumpHandlers)
_onNewLineReceived -= (Action<string>)d;
}
});
}
private string _content = string.Empty;
private StringBuilder _builder = new StringBuilder();
private event Action<string> _onNewLineReceived;
}
}

View file

@ -46,7 +46,7 @@
_wc = wc;
_change = change;
IsResolved = new Commands.IsConflictResolved(repo.FullPath, change).ReadToEnd().IsSuccess;
IsResolved = new Commands.IsConflictResolved(repo.FullPath, change).Result();
var context = wc.InProgressContext;
if (context is CherryPickInProgress cherryPick)

View file

@ -92,6 +92,9 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
var fixedName = FixName(_name);
var log = _repo.CreateLog($"Create Branch '{fixedName}'");
Use(log);
return Task.Run(() =>
{
var succ = false;
@ -103,15 +106,14 @@ namespace SourceGit.ViewModels
{
if (DiscardLocalChanges)
{
SetProgressDescription("Discard local changes...");
Commands.Discard.All(_repo.FullPath, false);
Commands.Discard.All(_repo.FullPath, false, log);
}
else
{
SetProgressDescription("Stash local changes");
succ = new Commands.Stash(_repo.FullPath).Push("CREATE_BRANCH_AUTO_STASH");
succ = new Commands.Stash(_repo.FullPath).Use(log).Push("CREATE_BRANCH_AUTO_STASH");
if (!succ)
{
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return false;
}
@ -120,21 +122,17 @@ namespace SourceGit.ViewModels
}
}
SetProgressDescription($"Create new branch '{fixedName}'");
succ = new Commands.Checkout(_repo.FullPath).Branch(fixedName, _baseOnRevision, SetProgressDescription);
succ = new Commands.Checkout(_repo.FullPath).Use(log).Branch(fixedName, _baseOnRevision);
if (needPopStash)
{
SetProgressDescription("Re-apply local changes...");
new Commands.Stash(_repo.FullPath).Pop("stash@{0}");
}
new Commands.Stash(_repo.FullPath).Use(log).Pop("stash@{0}");
}
else
{
SetProgressDescription($"Create new branch '{fixedName}'");
succ = Commands.Branch.Create(_repo.FullPath, fixedName, _baseOnRevision);
succ = Commands.Branch.Create(_repo.FullPath, fixedName, _baseOnRevision, log);
}
log.Complete();
CallUIThread(() =>
{
if (succ && CheckoutAfterCreated)

View file

@ -83,23 +83,24 @@ namespace SourceGit.ViewModels
ProgressDescription = "Create tag...";
var remotes = PushToRemotes ? _repo.Remotes : null;
var log = _repo.CreateLog("Create Tag");
Use(log);
return Task.Run(() =>
{
bool succ;
if (_annotated)
succ = Commands.Tag.Add(_repo.FullPath, _tagName, _basedOn, Message, SignTag);
succ = Commands.Tag.Add(_repo.FullPath, _tagName, _basedOn, Message, SignTag, log);
else
succ = Commands.Tag.Add(_repo.FullPath, _tagName, _basedOn);
succ = Commands.Tag.Add(_repo.FullPath, _tagName, _basedOn, log);
if (succ && remotes != null)
{
foreach (var remote in remotes)
{
SetProgressDescription($"Pushing tag to remote {remote.Name} ...");
new Commands.Push(_repo.FullPath, remote.Name, $"refs/tags/{_tagName}", false).Exec();
}
new Commands.Push(_repo.FullPath, remote.Name, $"refs/tags/{_tagName}", false).Use(log).Exec();
}
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -48,23 +48,25 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Deleting branch...";
var log = _repo.CreateLog("Delete Branch");
Use(log);
return Task.Run(() =>
{
if (Target.IsLocal)
{
Commands.Branch.DeleteLocal(_repo.FullPath, Target.Name);
Commands.Branch.DeleteLocal(_repo.FullPath, Target.Name, log);
if (_alsoDeleteTrackingRemote && TrackingRemoteBranch != null)
{
SetProgressDescription("Deleting remote-tracking branch...");
Commands.Branch.DeleteRemote(_repo.FullPath, TrackingRemoteBranch.Remote, TrackingRemoteBranch.Name);
}
Commands.Branch.DeleteRemote(_repo.FullPath, TrackingRemoteBranch.Remote, TrackingRemoteBranch.Name, log);
}
else
{
Commands.Branch.DeleteRemote(_repo.FullPath, Target.Remote, Target.Name);
Commands.Branch.DeleteRemote(_repo.FullPath, Target.Remote, Target.Name, log);
}
log.Complete();
CallUIThread(() =>
{
_repo.MarkBranchesDirtyManually();

View file

@ -23,25 +23,24 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Deleting multiple branches...";
var log = _repo.CreateLog("Delete Multiple Branches");
Use(log);
return Task.Run(() =>
{
if (_isLocal)
{
foreach (var target in Targets)
{
SetProgressDescription($"Deleting local branch : {target.Name}");
Commands.Branch.DeleteLocal(_repo.FullPath, target.Name);
}
Commands.Branch.DeleteLocal(_repo.FullPath, target.Name, log);
}
else
{
foreach (var target in Targets)
{
SetProgressDescription($"Deleting remote branch : {target.FriendlyName}");
Commands.Branch.DeleteRemote(_repo.FullPath, target.Remote, target.Name);
}
Commands.Branch.DeleteRemote(_repo.FullPath, target.Remote, target.Name, log);
}
log.Complete();
CallUIThread(() =>
{
_repo.MarkBranchesDirtyManually();

View file

@ -22,9 +22,14 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Deleting remote ...";
var log = _repo.CreateLog("Delete Remote");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.Remote(_repo.FullPath).Delete(Remote.Name);
var succ = new Commands.Remote(_repo.FullPath).Use(log).Delete(Remote.Name);
log.Complete();
CallUIThread(() =>
{
_repo.MarkBranchesDirtyManually();

View file

@ -23,9 +23,13 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Deleting submodule ...";
var log = _repo.CreateLog("Delete Submodule");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.Submodule(_repo.FullPath).Delete(Submodule);
var succ = new Commands.Submodule(_repo.FullPath).Use(log).Delete(Submodule);
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -28,10 +28,21 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Deleting tag '{Target.Name}' ...";
var remotes = PushToRemotes ? _repo.Remotes : null;
var remotes = PushToRemotes ? _repo.Remotes : [];
var log = _repo.CreateLog("Delete Tag");
Use(log);
return Task.Run(() =>
{
var succ = Commands.Tag.Delete(_repo.FullPath, Target.Name, remotes);
var succ = Commands.Tag.Delete(_repo.FullPath, Target.Name, log);
if (succ)
{
foreach (var r in remotes)
new Commands.Push(_repo.FullPath, r.Name, $"refs/tags/{Target.Name}", true).Use(log).Exec();
}
log.Complete();
CallUIThread(() =>
{
_repo.MarkTagsDirtyManually();

View file

@ -65,12 +65,17 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = _changes == null ? "Discard all local changes ..." : $"Discard total {_changes.Count} changes ...";
var log = _repo.CreateLog("Discard all");
Use(log);
return Task.Run(() =>
{
if (Mode is DiscardAllMode all)
Commands.Discard.All(_repo.FullPath, all.IncludeIgnored);
Commands.Discard.All(_repo.FullPath, all.IncludeIgnored, log);
else
Commands.Discard.Changes(_repo.FullPath, _changes);
Commands.Discard.Changes(_repo.FullPath, _changes, log);
log.Complete();
CallUIThread(() =>
{

View file

@ -6,7 +6,7 @@ namespace SourceGit.ViewModels
{
public Models.Stash Stash { get; private set; }
public DropStash(string repo, Models.Stash stash)
public DropStash(Repository repo, Models.Stash stash)
{
_repo = repo;
Stash = stash;
@ -17,13 +17,17 @@ namespace SourceGit.ViewModels
{
ProgressDescription = $"Dropping stash: {Stash.Name}";
var log = _repo.CreateLog("Drop Stash");
Use(log);
return Task.Run(() =>
{
new Commands.Stash(_repo).Drop(Stash.Name);
new Commands.Stash(_repo.FullPath).Use(log).Drop(Stash.Name);
log.Complete();
return true;
});
}
private readonly string _repo;
private readonly Repository _repo;
}
}

View file

@ -127,7 +127,6 @@ namespace SourceGit.ViewModels
if (pushURL != _url)
new Commands.Remote(_repo.FullPath).SetURL(_name, _url, true);
SetProgressDescription("Post processing ...");
new Commands.Config(_repo.FullPath).Set($"remote.{_name}.sshkey", _useSSH ? SSHKey : null);
CallUIThread(() => _repo.SetWatcherEnabled(true));

View file

@ -43,7 +43,7 @@ namespace SourceGit.ViewModels
return Task.Run(() =>
{
if (CustomAction.WaitForExit)
Commands.ExecuteCustomAction.RunAndWait(_repo.FullPath, CustomAction.Executable, _args, SetProgressDescription);
Commands.ExecuteCustomAction.RunAndWait(_repo.FullPath, CustomAction.Executable, _args, output => CallUIThread(() => ProgressDescription = output));
else
Commands.ExecuteCustomAction.Run(_repo.FullPath, CustomAction.Executable, _args);

View file

@ -29,9 +29,13 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Fast-Forward ...";
var log = _repo.CreateLog("Fast-Forward (No checkout)");
Use(log);
return Task.Run(() =>
{
new Commands.UpdateRef(_repo.FullPath, Local.FullName, To.FullName, SetProgressDescription).Exec();
new Commands.UpdateRef(_repo.FullPath, Local.FullName, To.FullName).Use(log).Exec();
log.Complete();
CallUIThread(() =>
{
_repo.NavigateToCommit(To.Head);

View file

@ -65,22 +65,23 @@ namespace SourceGit.ViewModels
var notags = _repo.Settings.FetchWithoutTags;
var force = _repo.Settings.EnableForceOnFetch;
var log = _repo.CreateLog("Fetch");
Use(log);
return Task.Run(() =>
{
if (FetchAllRemotes)
{
foreach (var remote in _repo.Remotes)
{
SetProgressDescription($"Fetching remote: {remote.Name}");
new Commands.Fetch(_repo.FullPath, remote.Name, notags, force, SetProgressDescription).Exec();
}
new Commands.Fetch(_repo.FullPath, remote.Name, notags, force).Use(log).Exec();
}
else
{
SetProgressDescription($"Fetching remote: {SelectedRemote.Name}");
new Commands.Fetch(_repo.FullPath, SelectedRemote.Name, notags, force, SetProgressDescription).Exec();
new Commands.Fetch(_repo.FullPath, SelectedRemote.Name, notags, force).Use(log).Exec();
}
log.Complete();
CallUIThread(() =>
{
_repo.NavigateToBranchDelayed(_repo.CurrentBranch?.Upstream);

View file

@ -29,9 +29,13 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Fast-Forward ...";
var log = _repo.CreateLog($"Fetch Into '{Local.FriendlyName}'");
Use(log);
return Task.Run(() =>
{
new Commands.Fetch(_repo.FullPath, Local, Upstream, SetProgressDescription).Exec();
new Commands.Fetch(_repo.FullPath, Local, Upstream).Use(log).Exec();
log.Complete();
CallUIThread(() =>
{
_repo.NavigateToBranchDelayed(Upstream.FullName);

View file

@ -33,11 +33,17 @@ namespace SourceGit.ViewModels
public override Task<bool> Sure()
{
_repo.SetWatcherEnabled(false);
var name = Branch.Name.StartsWith(_prefix) ? Branch.Name.Substring(_prefix.Length) : Branch.Name;
ProgressDescription = $"Git Flow - finishing {_type} {name} ...";
var log = _repo.CreateLog("Gitflow - Finish");
Use(log);
return Task.Run(() =>
{
var name = Branch.Name.StartsWith(_prefix) ? Branch.Name.Substring(_prefix.Length) : Branch.Name;
SetProgressDescription($"Git Flow - finishing {_type} {name} ...");
var succ = Commands.GitFlow.Finish(_repo.FullPath, _type, name, KeepBranch);
var succ = Commands.GitFlow.Finish(_repo.FullPath, _type, name, KeepBranch, log);
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -50,10 +50,15 @@ namespace SourceGit.ViewModels
public override Task<bool> Sure()
{
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Git Flow - starting {_type} {_name} ...";
var log = _repo.CreateLog("Gitflow - Start");
Use(log);
return Task.Run(() =>
{
SetProgressDescription($"Git Flow - starting {_type} {_name} ...");
var succ = Commands.GitFlow.Start(_repo.FullPath, _type, _name);
var succ = Commands.GitFlow.Start(_repo.FullPath, _type, _name, log);
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -302,17 +302,20 @@ namespace SourceGit.ViewModels
var picker = await storageProvider.OpenFolderPickerAsync(options);
if (picker.Count == 1)
{
var log = _repo.CreateLog("Save as Patch");
var succ = false;
for (var i = 0; i < selected.Count; i++)
{
var saveTo = GetPatchFileName(picker[0].Path.LocalPath, selected[i], i);
succ = await Task.Run(() => new Commands.FormatPatch(_repo.FullPath, selected[i].SHA, saveTo).Exec());
succ = await Task.Run(() => new Commands.FormatPatch(_repo.FullPath, selected[i].SHA, saveTo).Use(log).Exec());
if (!succ)
break;
}
if (succ)
App.SendNotification(_repo.FullPath, App.Text("SaveAsPatchSuccess"));
log.Complete();
}
}
catch (Exception exception)

View file

@ -30,19 +30,24 @@ namespace SourceGit.ViewModels
{
ProgressDescription = $"Initialize git repository at: '{_targetPath}'";
var log = new CommandLog("Initialize");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.Init(_pageId, _targetPath).Exec();
if (!succ)
return false;
var succ = new Commands.Init(_pageId, _targetPath).Use(log).Exec();
log.Complete();
CallUIThread(() =>
if (succ)
{
Preferences.Instance.FindOrAddNodeByRepositoryPath(_targetPath, _parentNode, true);
Welcome.Instance.Refresh();
});
CallUIThread(() =>
{
Preferences.Instance.FindOrAddNodeByRepositoryPath(_targetPath, _parentNode, true);
Welcome.Instance.Refresh();
});
}
return true;
return succ;
});
}

View file

@ -106,9 +106,23 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Init git-flow ...";
var log = _repo.CreateLog("Gitflow - Init");
Use(log);
return Task.Run(() =>
{
var succ = Commands.GitFlow.Init(_repo.FullPath, _repo.Branches, _master, _develop, _featurePrefix, _releasePrefix, _hotfixPrefix, _tagPrefix);
var succ = Commands.GitFlow.Init(
_repo.FullPath,
_repo.Branches,
_master,
_develop,
_featurePrefix,
_releasePrefix,
_hotfixPrefix,
_tagPrefix,
log);
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -24,9 +24,14 @@ namespace SourceGit.ViewModels
{
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Fetching LFS objects from remote ...";
var log = _repo.CreateLog("LFS Fetch");
Use(log);
return Task.Run(() =>
{
new Commands.LFS(_repo.FullPath).Fetch(SelectedRemote.Name, SetProgressDescription);
new Commands.LFS(_repo.FullPath).Fetch(SelectedRemote.Name, log);
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return true;
});

View file

@ -15,9 +15,13 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "LFS prune ...";
var log = _repo.CreateLog("LFS Prune");
Use(log);
return Task.Run(() =>
{
new Commands.LFS(_repo.FullPath).Prune(SetProgressDescription);
new Commands.LFS(_repo.FullPath).Prune(log);
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return true;
});

View file

@ -24,9 +24,14 @@ namespace SourceGit.ViewModels
{
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Pull LFS objects from remote ...";
var log = _repo.CreateLog("LFS Pull");
Use(log);
return Task.Run(() =>
{
new Commands.LFS(_repo.FullPath).Pull(SelectedRemote.Name, SetProgressDescription);
new Commands.LFS(_repo.FullPath).Pull(SelectedRemote.Name, log);
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return true;
});

View file

@ -24,9 +24,14 @@ namespace SourceGit.ViewModels
{
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Push LFS objects to remote ...";
var log = _repo.CreateLog("LFS Push");
Use(log);
return Task.Run(() =>
{
new Commands.LFS(_repo.FullPath).Push(SelectedRemote.Name, SetProgressDescription);
new Commands.LFS(_repo.FullPath).Push(SelectedRemote.Name, log);
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return true;
});

View file

@ -29,9 +29,13 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Adding custom LFS tracking pattern ...";
var log = _repo.CreateLog("LFS Add Custom Pattern");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.LFS(_repo.FullPath).Track(_pattern, IsFilename);
var succ = new Commands.LFS(_repo.FullPath).Track(_pattern, IsFilename, log);
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -59,9 +59,14 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Merging '{_sourceName}' into '{Into}' ...";
var log = _repo.CreateLog($"Merging '{_sourceName}' into '{Into}'");
Use(log);
return Task.Run(() =>
{
new Commands.Merge(_repo.FullPath, _sourceName, Mode.Arg, SetProgressDescription).Exec();
new Commands.Merge(_repo.FullPath, _sourceName, Mode.Arg).Use(log).Exec();
log.Complete();
CallUIThread(() =>
{
_repo.NavigateToBranchDelayed(_repo.CurrentBranch?.FullName);

View file

@ -46,15 +46,18 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Merge head(s) ...";
var log = _repo.CreateLog("Merge Multiple Heads");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.Merge(
_repo.FullPath,
ConvertTargetToMergeSources(),
AutoCommit,
Strategy.Arg,
SetProgressDescription).Exec();
Strategy.Arg).Use(log).Exec();
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -52,9 +52,9 @@ namespace SourceGit.ViewModels
Dispatcher.UIThread.Invoke(action);
}
protected void SetProgressDescription(string description)
protected void Use(CommandLog log)
{
CallUIThread(() => ProgressDescription = description);
log.Register(newline => ProgressDescription = newline.Trim());
}
private bool _inProgress = false;

View file

@ -22,9 +22,13 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Run `prune` on remote ...";
var log = _repo.CreateLog($"Prune Remote '{Remote.Name}'");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.Remote(_repo.FullPath).Prune(Remote.Name);
var succ = new Commands.Remote(_repo.FullPath).Use(log).Prune(Remote.Name);
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -15,9 +15,13 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Prune worktrees ...";
var log = _repo.CreateLog("Prune Worktrees");
Use(log);
return Task.Run(() =>
{
new Commands.Worktree(_repo.FullPath).Prune(SetProgressDescription);
new Commands.Worktree(_repo.FullPath).Use(log).Prune();
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return true;
});

View file

@ -118,6 +118,9 @@ namespace SourceGit.ViewModels
{
_repo.SetWatcherEnabled(false);
var log = _repo.CreateLog("Pull");
Use(log);
return Task.Run(() =>
{
var changes = new Commands.CountLocalChangesWithoutUntracked(_repo.FullPath).Result();
@ -126,15 +129,14 @@ namespace SourceGit.ViewModels
{
if (DiscardLocalChanges)
{
SetProgressDescription("Discard local changes ...");
Commands.Discard.All(_repo.FullPath, false);
Commands.Discard.All(_repo.FullPath, false, log);
}
else
{
SetProgressDescription("Stash local changes...");
var succ = new Commands.Stash(_repo.FullPath).Push("PULL_AUTO_STASH");
var succ = new Commands.Stash(_repo.FullPath).Use(log).Push("PULL_AUTO_STASH");
if (!succ)
{
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return false;
}
@ -146,16 +148,14 @@ namespace SourceGit.ViewModels
bool rs;
if (FetchAllBranches)
{
SetProgressDescription($"Fetching remote: {_selectedRemote.Name}...");
rs = new Commands.Fetch(
_repo.FullPath,
_selectedRemote.Name,
NoTags,
false,
SetProgressDescription).Exec();
false).Use(log).Exec();
if (!rs)
{
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return false;
}
@ -164,39 +164,31 @@ namespace SourceGit.ViewModels
// Use merge/rebase instead of pull as fetch is done manually.
if (UseRebase)
{
SetProgressDescription($"Rebase {_current.Name} on {_selectedBranch.FriendlyName} ...");
rs = new Commands.Rebase(_repo.FullPath, _selectedBranch.FriendlyName, false).Exec();
}
rs = new Commands.Rebase(_repo.FullPath, _selectedBranch.FriendlyName, false).Use(log).Exec();
else
{
SetProgressDescription($"Merge {_selectedBranch.FriendlyName} into {_current.Name} ...");
rs = new Commands.Merge(_repo.FullPath, _selectedBranch.FriendlyName, "", SetProgressDescription).Exec();
}
rs = new Commands.Merge(_repo.FullPath, _selectedBranch.FriendlyName, "").Use(log).Exec();
}
else
{
SetProgressDescription($"Pull {_selectedRemote.Name}/{_selectedBranch.Name}...");
rs = new Commands.Pull(
_repo.FullPath,
_selectedRemote.Name,
_selectedBranch.Name,
UseRebase,
NoTags,
SetProgressDescription).Exec();
NoTags).Use(log).Exec();
}
if (rs && needPopStash)
{
SetProgressDescription("Re-apply local changes...");
rs = new Commands.Stash(_repo.FullPath).Pop("stash@{0}");
}
rs = new Commands.Stash(_repo.FullPath).Use(log).Pop("stash@{0}");
log.Complete();
CallUIThread(() =>
{
_repo.NavigateToBranchDelayed(_repo.CurrentBranch?.FullName);
_repo.SetWatcherEnabled(true);
});
return rs;
});
}

View file

@ -164,6 +164,9 @@ namespace SourceGit.ViewModels
var remoteBranchName = _selectedRemoteBranch.Name;
ProgressDescription = $"Push {_selectedLocalBranch.Name} -> {_selectedRemote.Name}/{remoteBranchName} ...";
var log = _repo.CreateLog("Push");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.Push(
@ -174,8 +177,9 @@ namespace SourceGit.ViewModels
PushAllTags,
_repo.Submodules.Count > 0 && CheckSubmodules,
_isSetTrackOptionVisible && Tracking,
ForcePush,
SetProgressDescription).Exec();
ForcePush).Use(log).Exec();
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -41,6 +41,9 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Pushing tag ...";
var log = _repo.CreateLog("Push Tag");
Use(log);
return Task.Run(() =>
{
var succ = true;
@ -49,18 +52,17 @@ namespace SourceGit.ViewModels
{
foreach (var remote in _repo.Remotes)
{
SetProgressDescription($"Pushing tag to remote {remote.Name} ...");
succ = new Commands.Push(_repo.FullPath, remote.Name, tag, false).Exec();
succ = new Commands.Push(_repo.FullPath, remote.Name, tag, false).Use(log).Exec();
if (!succ)
break;
}
}
else
{
SetProgressDescription($"Pushing tag to remote {SelectedRemote.Name} ...");
succ = new Commands.Push(_repo.FullPath, SelectedRemote.Name, tag, false).Exec();
succ = new Commands.Push(_repo.FullPath, SelectedRemote.Name, tag, false).Use(log).Exec();
}
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -47,9 +47,13 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Rebasing ...";
var log = _repo.CreateLog("Rebase");
Use(log);
return Task.Run(() =>
{
new Commands.Rebase(_repo.FullPath, _revision, AutoStash).Exec();
new Commands.Rebase(_repo.FullPath, _revision, AutoStash).Use(log).Exec();
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return true;
});

View file

@ -26,11 +26,15 @@ namespace SourceGit.ViewModels
public override Task<bool> Sure()
{
_repo.SetWatcherEnabled(false);
ProgressDescription = "Remove worktrees ...";
ProgressDescription = "Remove worktree ...";
var log = _repo.CreateLog("Remove worktree");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.Worktree(_repo.FullPath).Remove(Target.FullPath, Force, SetProgressDescription);
var succ = new Commands.Worktree(_repo.FullPath).Use(log).Remove(Target.FullPath, Force);
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -54,10 +54,15 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Rename '{Target.Name}'";
var log = _repo.CreateLog($"Rename Branch '{Target.Name}'");
Use(log);
return Task.Run(() =>
{
var oldName = Target.FullName;
var succ = Commands.Branch.Rename(_repo.FullPath, Target.Name, fixedName);
var succ = Commands.Branch.Rename(_repo.FullPath, Target.Name, fixedName, log);
log.Complete();
CallUIThread(() =>
{
if (succ)

View file

@ -6,6 +6,7 @@ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Media.Imaging;
@ -409,6 +410,12 @@ namespace SourceGit.ViewModels
set;
} = 0;
public AvaloniaList<CommandLog> Logs
{
get;
private set;
} = new AvaloniaList<CommandLog>();
public Repository(bool isBare, string path, string gitDir)
{
IsBare = isBare;
@ -472,6 +479,8 @@ namespace SourceGit.ViewModels
public void Close()
{
SelectedView = null; // Do NOT modify. Used to remove exists widgets for GC.Collect
Logs.Clear();
_settings.LastCommitMessage = _workingCopy.CommitMessage;
var settingsSerialized = JsonSerializer.Serialize(_settings, JsonCodeGen.Default.RepositorySettings);
@ -538,6 +547,13 @@ namespace SourceGit.ViewModels
GetOwnerPage()?.StartPopup(popup);
}
public CommandLog CreateLog(string name)
{
var log = new CommandLog(name);
Logs.Insert(0, log);
return log;
}
public void RefreshAll()
{
Task.Run(() =>
@ -2560,7 +2576,7 @@ namespace SourceGit.ViewModels
Dispatcher.UIThread.Invoke(() => IsAutoFetching = true);
foreach (var remote in remotes)
new Commands.Fetch(_fullpath, remote, false, false, null) { RaiseError = false }.Exec();
new Commands.Fetch(_fullpath, remote, false, false) { RaiseError = false }.Exec();
_lastFetchTime = DateTime.Now;
Dispatcher.UIThread.Invoke(() => IsAutoFetching = false);
}

View file

@ -36,9 +36,13 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Reset current branch to {To.SHA} ...";
var log = _repo.CreateLog($"Reset HEAD to '{To.SHA}'");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.Reset(_repo.FullPath, To.SHA, SelectedMode.Arg).Exec();
var succ = new Commands.Reset(_repo.FullPath, To.SHA, SelectedMode.Arg).Use(log).Exec();
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -29,9 +29,13 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Revert commit '{Target.SHA}' ...";
var log = _repo.CreateLog($"Revert '{Target.SHA}'");
Use(log);
return Task.Run(() =>
{
new Commands.Revert(_repo.FullPath, Target.SHA, AutoCommit).Exec();
new Commands.Revert(_repo.FullPath, Target.SHA, AutoCommit).Use(log).Exec();
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return true;
});

View file

@ -37,9 +37,13 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Editing head commit message ...";
var log = _repo.CreateLog("Reword HEAD");
Use(log);
return Task.Run(() =>
{
var succ = new Commands.Commit(_repo.FullPath, _message, true, _repo.Settings.EnableSignOffForCommit).Run();
var succ = new Commands.Commit(_repo.FullPath, _message, true, _repo.Settings.EnableSignOffForCommit).Use(log).Run();
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -97,7 +97,7 @@ namespace SourceGit.ViewModels
subdir.Name.Equals(".idea", StringComparison.Ordinal))
continue;
SetProgressDescription($"Scanning {subdir.FullName}...");
CallUIThread(() => ProgressDescription = $"Scanning {subdir.FullName}...");
var normalizedSelf = subdir.FullName.Replace("\\", "/");
if (_managed.Contains(normalizedSelf))

View file

@ -55,17 +55,22 @@ namespace SourceGit.ViewModels
public override Task<bool> Sure()
{
SetProgressDescription("Setting upstream...");
ProgressDescription = "Setting upstream...";
var upstream = (_unset || SelectedRemoteBranch == null) ? string.Empty : SelectedRemoteBranch.FullName;
if (upstream == Local.Upstream)
return null;
var log = _repo.CreateLog("Set Upstream");
Use(log);
return Task.Run(() =>
{
var succ = Commands.Branch.SetUpstream(_repo.FullPath, Local.Name, upstream.Replace("refs/remotes/", ""));
var succ = Commands.Branch.SetUpstream(_repo.FullPath, Local.Name, upstream.Replace("refs/remotes/", ""), log);
if (succ)
_repo.RefreshBranches();
log.Complete();
return true;
});
}

View file

@ -31,6 +31,9 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = "Squashing ...";
var log = _repo.CreateLog("Squash");
Use(log);
return Task.Run(() =>
{
var autoStashed = false;
@ -38,9 +41,10 @@ namespace SourceGit.ViewModels
if (_repo.LocalChangesCount > 0)
{
succ = new Commands.Stash(_repo.FullPath).Push("SQUASH_AUTO_STASH");
succ = new Commands.Stash(_repo.FullPath).Use(log).Push("SQUASH_AUTO_STASH");
if (!succ)
{
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return false;
}
@ -48,13 +52,14 @@ namespace SourceGit.ViewModels
autoStashed = true;
}
succ = new Commands.Reset(_repo.FullPath, Target.SHA, "--soft").Exec();
succ = new Commands.Reset(_repo.FullPath, Target.SHA, "--soft").Use(log).Exec();
if (succ)
succ = new Commands.Commit(_repo.FullPath, _message, true, _repo.Settings.EnableSignOffForCommit).Run();
succ = new Commands.Commit(_repo.FullPath, _message, true, _repo.Settings.EnableSignOffForCommit).Use(log).Run();
if (succ && autoStashed)
new Commands.Stash(_repo.FullPath).Pop("stash@{0}");
new Commands.Stash(_repo.FullPath).Use(log).Pop("stash@{0}");
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return succ;
});

View file

@ -56,6 +56,9 @@ namespace SourceGit.ViewModels
_repo.SetWatcherEnabled(false);
ProgressDescription = $"Stash changes ...";
var log = _repo.CreateLog("Stash Local Changes");
Use(log);
return Task.Run(() =>
{
var succ = false;
@ -66,7 +69,7 @@ namespace SourceGit.ViewModels
{
if (Native.OS.GitVersion >= Models.GitVersions.STASH_PUSH_ONLY_STAGED)
{
succ = new Commands.Stash(_repo.FullPath).PushOnlyStaged(Message, KeepIndex);
succ = new Commands.Stash(_repo.FullPath).Use(log).PushOnlyStaged(Message, KeepIndex);
}
else
{
@ -77,22 +80,23 @@ namespace SourceGit.ViewModels
staged.Add(c);
}
succ = StashWithChanges(staged);
succ = StashWithChanges(staged, log);
}
}
else
{
succ = new Commands.Stash(_repo.FullPath).Push(Message, IncludeUntracked, KeepIndex);
succ = new Commands.Stash(_repo.FullPath).Use(log).Push(Message, IncludeUntracked, KeepIndex);
}
}
else
{
succ = StashWithChanges(_changes);
succ = StashWithChanges(_changes, log);
}
if (AutoRestore && succ)
succ = new Commands.Stash(_repo.FullPath).Apply("stash@{0}", true);
succ = new Commands.Stash(_repo.FullPath).Use(log).Apply("stash@{0}", true);
log.Complete();
CallUIThread(() =>
{
_repo.MarkWorkingCopyDirtyManually();
@ -103,7 +107,7 @@ namespace SourceGit.ViewModels
});
}
private bool StashWithChanges(List<Models.Change> changes)
private bool StashWithChanges(List<Models.Change> changes, CommandLog log)
{
if (changes.Count == 0)
return true;
@ -117,7 +121,7 @@ namespace SourceGit.ViewModels
var tmpFile = Path.GetTempFileName();
File.WriteAllLines(tmpFile, paths);
succ = new Commands.Stash(_repo.FullPath).Push(Message, tmpFile, KeepIndex);
succ = new Commands.Stash(_repo.FullPath).Use(log).Push(Message, tmpFile, KeepIndex);
File.Delete(tmpFile);
}
else
@ -126,7 +130,7 @@ namespace SourceGit.ViewModels
{
var count = Math.Min(10, changes.Count - i);
var step = changes.GetRange(i, count);
succ = new Commands.Stash(_repo.FullPath).Push(Message, step, KeepIndex);
succ = new Commands.Stash(_repo.FullPath).Use(log).Push(Message, step, KeepIndex);
if (!succ)
break;
}

View file

@ -147,7 +147,7 @@ namespace SourceGit.ViewModels
apply.Click += (_, ev) =>
{
if (_repo.CanCreatePopup())
_repo.ShowPopup(new ApplyStash(_repo.FullPath, stash));
_repo.ShowPopup(new ApplyStash(_repo, stash));
ev.Handled = true;
};
@ -157,7 +157,7 @@ namespace SourceGit.ViewModels
drop.Click += (_, ev) =>
{
if (_repo.CanCreatePopup())
_repo.ShowPopup(new DropStash(_repo.FullPath, stash));
_repo.ShowPopup(new DropStash(_repo, stash));
ev.Handled = true;
};

View file

@ -62,19 +62,21 @@ namespace SourceGit.ViewModels
else
targets = [SelectedSubmodule];
var log = _repo.CreateLog("Update Submodule");
Use(log);
return Task.Run(() =>
{
foreach (var submodule in targets)
{
ProgressDescription = $"Updating submodule {submodule} ...";
new Commands.Submodule(_repo.FullPath).Update(
new Commands.Submodule(_repo.FullPath).Use(log).Update(
submodule,
EnableInit,
EnableRecursive,
EnableRemote,
SetProgressDescription);
EnableRemote);
}
log.Complete();
CallUIThread(() => _repo.SetWatcherEnabled(true));
return true;
});

View file

@ -0,0 +1,27 @@
using Avalonia.Collections;
using CommunityToolkit.Mvvm.ComponentModel;
namespace SourceGit.ViewModels
{
public class ViewLogs : ObservableObject
{
public AvaloniaList<CommandLog> Logs
{
get => _repo.Logs;
}
public CommandLog SelectedLog
{
get => _selectedLog;
set => SetProperty(ref _selectedLog, value);
}
public ViewLogs(Repository repo)
{
_repo = repo;
}
private Repository _repo = null;
private CommandLog _selectedLog = null;
}
}

View file

@ -711,7 +711,9 @@ namespace SourceGit.ViewModels
assumeUnchanged.IsVisible = change.WorkTree != Models.ChangeState.Untracked;
assumeUnchanged.Click += (_, e) =>
{
new Commands.AssumeUnchanged(_repo.FullPath).Add(change.Path);
var log = _repo.CreateLog("Assume File Unchanged");
new Commands.AssumeUnchanged(_repo.FullPath, change.Path, true).Use(log).Exec();
log.Complete();
e.Handled = true;
};
@ -805,10 +807,12 @@ namespace SourceGit.ViewModels
lfsTrackThisFile.Header = App.Text("GitLFS.Track", filename);
lfsTrackThisFile.Click += async (_, e) =>
{
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Track(filename, true));
var log = _repo.CreateLog("Track LFS");
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Track(filename, true, log));
if (succ)
App.SendNotification(_repo.FullPath, $"Tracking file named {filename} successfully!");
log.Complete();
e.Handled = true;
};
lfs.Items.Add(lfsTrackThisFile);
@ -819,10 +823,12 @@ namespace SourceGit.ViewModels
lfsTrackByExtension.Header = App.Text("GitLFS.TrackByExtension", extension);
lfsTrackByExtension.Click += async (_, e) =>
{
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Track("*" + extension));
var log = _repo.CreateLog("Track LFS");
var succ = await Task.Run(() => new Commands.LFS(_repo.FullPath).Track("*" + extension, false, log));
if (succ)
App.SendNotification(_repo.FullPath, $"Tracking all *{extension} files successfully!");
log.Complete();
e.Handled = true;
};
lfs.Items.Add(lfsTrackByExtension);
@ -1581,9 +1587,11 @@ namespace SourceGit.ViewModels
IsStaging = true;
_repo.SetWatcherEnabled(false);
var log = _repo.CreateLog("Stage");
if (count == _unstaged.Count)
{
await Task.Run(() => new Commands.Add(_repo.FullPath, _repo.IncludeUntracked).Exec());
await Task.Run(() => new Commands.Add(_repo.FullPath, _repo.IncludeUntracked).Use(log).Exec());
}
else if (Native.OS.GitVersion >= Models.GitVersions.ADD_WITH_PATHSPECFILE)
{
@ -1593,7 +1601,7 @@ namespace SourceGit.ViewModels
var tmpFile = Path.GetTempFileName();
File.WriteAllLines(tmpFile, paths);
await Task.Run(() => new Commands.Add(_repo.FullPath, tmpFile).Exec());
await Task.Run(() => new Commands.Add(_repo.FullPath, tmpFile).Use(log).Exec());
File.Delete(tmpFile);
}
else
@ -1605,9 +1613,10 @@ namespace SourceGit.ViewModels
for (int i = 0; i < count; i += 10)
{
var step = paths.GetRange(i, Math.Min(10, count - i));
await Task.Run(() => new Commands.Add(_repo.FullPath, step).Exec());
await Task.Run(() => new Commands.Add(_repo.FullPath, step).Use(log).Exec());
}
}
log.Complete();
_repo.MarkWorkingCopyDirtyManually();
_repo.SetWatcherEnabled(true);
IsStaging = false;
@ -1624,22 +1633,26 @@ namespace SourceGit.ViewModels
IsUnstaging = true;
_repo.SetWatcherEnabled(false);
var log = _repo.CreateLog("Unstage");
if (_useAmend)
{
log.AppendLine("$ git update-index --index-info ");
await Task.Run(() => new Commands.UnstageChangesForAmend(_repo.FullPath, changes).Exec());
}
else if (count == _staged.Count)
{
await Task.Run(() => new Commands.Reset(_repo.FullPath).Exec());
await Task.Run(() => new Commands.Reset(_repo.FullPath).Use(log).Exec());
}
else
{
for (int i = 0; i < count; i += 10)
{
var step = changes.GetRange(i, Math.Min(10, count - i));
await Task.Run(() => new Commands.Reset(_repo.FullPath, step).Exec());
await Task.Run(() => new Commands.Reset(_repo.FullPath, step).Use(log).Exec());
}
}
log.Complete();
_repo.MarkWorkingCopyDirtyManually();
_repo.SetWatcherEnabled(true);
IsUnstaging = false;
@ -1703,14 +1716,17 @@ namespace SourceGit.ViewModels
_repo.Settings.PushCommitMessage(_commitMessage);
_repo.SetWatcherEnabled(false);
var log = _repo.CreateLog("Commit");
Task.Run(() =>
{
var succ = true;
if (autoStage && _unstaged.Count > 0)
succ = new Commands.Add(_repo.FullPath, _repo.IncludeUntracked).Exec();
succ = new Commands.Add(_repo.FullPath, _repo.IncludeUntracked).Use(log).Exec();
if (succ)
succ = new Commands.Commit(_repo.FullPath, _commitMessage, _useAmend, _repo.Settings.EnableSignOffForCommit).Run();
succ = new Commands.Commit(_repo.FullPath, _commitMessage, _useAmend, _repo.Settings.EnableSignOffForCommit).Use(log).Run();
log.Complete();
Dispatcher.UIThread.Post(() =>
{

View file

@ -21,6 +21,10 @@
<Path Width="13" Height="13" Data="{StaticResource Icons.Terminal}"/>
</Button>
<Button Classes="icon_button" Width="32" Click="OpenGitLogs" ToolTip.Tip="{DynamicResource Text.Repository.ViewLogs}">
<Path Width="14" Height="14" Margin="0,1,0,0" Data="{StaticResource Icons.Logs}"/>
</Button>
<Button Classes="icon_button" Width="32" Click="OpenStatistics" ToolTip.Tip="{DynamicResource Text.Repository.Statistics}">
<Path Width="13" Height="13" Data="{StaticResource Icons.Statistics}"/>
</Button>
@ -102,7 +106,7 @@
<Button Classes="icon_button" Width="32" Margin="8,0,0,0" Command="{Binding Cleanup}" ToolTip.Tip="{DynamicResource Text.Repository.Clean}">
<Path Width="14" Height="14" Margin="0,1,0,0" Data="{StaticResource Icons.Clean}"/>
</Button>
</Button>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,4,0">

View file

@ -128,6 +128,16 @@ namespace SourceGit.Views
e.Handled = true;
}
private async void OpenGitLogs(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModels.Repository repo && TopLevel.GetTopLevel(this) is Window owner)
{
var dialog = new ViewLogs() { DataContext = new ViewModels.ViewLogs(repo) };
await dialog.ShowDialog(owner);
e.Handled = true;
}
}
}
}

View file

@ -1972,7 +1972,7 @@ namespace SourceGit.Views
if (!selection.HasLeftChanges)
{
Commands.Discard.Changes(repo.FullPath, [change]);
Commands.Discard.Changes(repo.FullPath, [change], null);
}
else
{

112
src/Views/ViewLogs.axaml Normal file
View file

@ -0,0 +1,112 @@
<v:ChromelessWindow xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.ViewLogs"
x:DataType="vm:ViewLogs"
x:Name="ThisControl"
Title="{DynamicResource Text.ViewLogs}"
Icon="/App.ico"
Width="800" Height="500"
CanResize="False"
WindowStartupLocation="CenterOwner">
<Grid RowDefinitions="Auto,*">
<!-- TitleBar -->
<Grid Grid.Row="0" Height="28" IsVisible="{Binding !#ThisControl.UseSystemWindowFrame}">
<Border Background="{DynamicResource Brush.TitleBar}"
BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Brush.Border0}"
PointerPressed="BeginMoveWindow"/>
<Path Width="14" Height="14"
Margin="10,0,0,0"
HorizontalAlignment="Left"
Data="{StaticResource Icons.Logs}"
IsVisible="{OnPlatform True, macOS=False}"/>
<TextBlock Classes="bold"
Text="{DynamicResource Text.ViewLogs}"
HorizontalAlignment="Center" VerticalAlignment="Center"
IsHitTestVisible="False"/>
<v:CaptionButtons HorizontalAlignment="Right"
IsCloseButtonOnly="True"
IsVisible="{OnPlatform True, macOS=False}"/>
</Grid>
<!-- Body -->
<Grid Grid.Row="1" ColumnDefinitions="300,4,*" Margin="8">
<ListBox Grid.Column="0"
Padding="4"
Background="{DynamicResource Brush.Contents}"
BorderThickness="1"
BorderBrush="{DynamicResource Brush.Border2}"
ItemsSource="{Binding Logs}"
SelectedItem="{Binding SelectedLog, Mode=TwoWay}"
SelectionMode="Single"
Grid.IsSharedSizeScope="True"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Height" Value="28"/>
<Setter Property="CornerRadius" Value="4"/>
</Style>
</ListBox.Styles>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate DataType="vm:CommandLog">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="SearchCommitTimeColumn"/>
<ColumnDefinition Width="28"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Classes="primary"
Text="{Binding Name}"
Margin="4,0,0,0"
VerticalAlignment="Center"
TextTrimming="CharacterEllipsis"/>
<v:LoadingIcon Grid.Column="1"
Width="14" Height="14"
Margin="2,0,0,0"
IsVisible="{Binding !IsComplete}"/>
<TextBlock Grid.Column="2"
Classes="primary"
Margin="4,0"
Foreground="{DynamicResource Brush.FG2}"
Text="{Binding TimeStr}"
HorizontalAlignment="Right" VerticalAlignment="Center"/>
<Button Grid.Column="3" Classes="icon_button" Click="OnRemoveLog">
<Path Width="14" Height="14" Data="{StaticResource Icons.Clear}"/>
</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Border Grid.Column="2"
BorderBrush="{DynamicResource Brush.Border2}"
BorderThickness="1"
Background="{DynamicResource Brush.Contents}">
<v:LogContentPresenter Log="{Binding SelectedLog}"
FontFamily="{DynamicResource Fonts.Monospace}"/>
</Border>
</Grid>
</Grid>
</v:ChromelessWindow>

Some files were not shown because too many files have changed in this diff Show more