mirror of
https://github.com/sourcegit-scm/sourcegit
synced 2025-06-20 18:05:00 +00:00
Merge commit 'a4188806
' into fix/file-rename
This commit is contained in:
commit
409ca3804a
6 changed files with 418 additions and 82 deletions
|
@ -8,7 +8,7 @@ namespace SourceGit.Commands
|
|||
{
|
||||
[GeneratedRegex(@"^([MADC])\s+(.+)$")]
|
||||
private static partial Regex REG_FORMAT();
|
||||
[GeneratedRegex(@"^R[0-9]{0,4}\s+(.+)$")]
|
||||
[GeneratedRegex(@"^R[0-9]{0,4}\s+(.+)\s+(.+)$")]
|
||||
private static partial Regex REG_RENAME_FORMAT();
|
||||
|
||||
public CompareRevisions(string repo, string start, string end)
|
||||
|
@ -51,7 +51,11 @@ namespace SourceGit.Commands
|
|||
match = REG_RENAME_FORMAT().Match(line);
|
||||
if (match.Success)
|
||||
{
|
||||
var renamed = new Models.Change() { Path = match.Groups[1].Value };
|
||||
var renamed = new Models.Change()
|
||||
{
|
||||
OriginalPath = match.Groups[1].Value,
|
||||
Path = match.Groups[2].Value
|
||||
};
|
||||
renamed.Set(Models.ChangeState.Renamed);
|
||||
_changes.Add(renamed);
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace SourceGit.Commands
|
|||
}
|
||||
else if (method == Models.CommitSearchMethod.ByFile)
|
||||
{
|
||||
search += $"-- \"{filter}\"";
|
||||
search += $"--follow -- \"{filter}\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
124
src/Commands/QueryFilePathInRevision.cs
Normal file
124
src/Commands/QueryFilePathInRevision.cs
Normal file
|
@ -0,0 +1,124 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace SourceGit.Commands
|
||||
{
|
||||
public partial class QueryFilePathInRevision : Command
|
||||
{
|
||||
[GeneratedRegex(@"^R[0-9]{0,4}\s+(.+)\s+(.+)$")]
|
||||
private static partial Regex REG_RENAME_FORMAT();
|
||||
|
||||
public QueryFilePathInRevision(string repo, string revision, string currentPath)
|
||||
{
|
||||
WorkingDirectory = repo;
|
||||
Context = repo;
|
||||
_revision = revision;
|
||||
_currentPath = currentPath;
|
||||
}
|
||||
|
||||
public string Result()
|
||||
{
|
||||
if (CheckPathExistsInRevision(_currentPath))
|
||||
return _currentPath;
|
||||
|
||||
string mappedPath = FindRenameHistory();
|
||||
return mappedPath ?? _currentPath;
|
||||
}
|
||||
|
||||
private bool CheckPathExistsInRevision(string path)
|
||||
{
|
||||
Args = $"ls-tree -r {_revision} -- \"{path}\"";
|
||||
var rs = ReadToEnd();
|
||||
return rs.IsSuccess && !string.IsNullOrEmpty(rs.StdOut);
|
||||
}
|
||||
|
||||
private string FindRenameHistory()
|
||||
{
|
||||
var fileHistory = BuildFileHistory();
|
||||
if (fileHistory == null || fileHistory.Count == 0)
|
||||
return null;
|
||||
|
||||
foreach (var entry in fileHistory)
|
||||
{
|
||||
if (!IsTargetRevisionBefore(entry.CommitSHA))
|
||||
continue;
|
||||
|
||||
if (CheckPathExistsInRevision(entry.OldPath))
|
||||
return entry.OldPath;
|
||||
}
|
||||
|
||||
if (fileHistory.Count > 0)
|
||||
{
|
||||
var oldestPath = fileHistory[^1].OldPath;
|
||||
if (CheckPathExistsInRevision(oldestPath))
|
||||
return oldestPath;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool IsTargetRevisionBefore(string commitSHA)
|
||||
{
|
||||
Args = $"merge-base --is-ancestor {_revision} {commitSHA}";
|
||||
var rs = ReadToEnd();
|
||||
return rs.IsSuccess;
|
||||
}
|
||||
|
||||
private List<RenameHistoryEntry> BuildFileHistory()
|
||||
{
|
||||
Args = $"log --follow --name-status --pretty=format:\"commit %H\" -M -- \"{_currentPath}\"";
|
||||
var rs = ReadToEnd();
|
||||
if (!rs.IsSuccess)
|
||||
return null;
|
||||
|
||||
var result = new List<RenameHistoryEntry>();
|
||||
var lines = rs.StdOut.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
string currentCommit = null;
|
||||
string currentPath = _currentPath;
|
||||
|
||||
foreach (var t in lines)
|
||||
{
|
||||
var line = t.Trim();
|
||||
|
||||
if (line.StartsWith("commit ", StringComparison.Ordinal))
|
||||
{
|
||||
currentCommit = line.Substring("commit ".Length);
|
||||
continue;
|
||||
}
|
||||
|
||||
var match = REG_RENAME_FORMAT().Match(line);
|
||||
if (match.Success && currentCommit != null)
|
||||
{
|
||||
var oldPath = match.Groups[1].Value;
|
||||
var newPath = match.Groups[2].Value;
|
||||
|
||||
if (newPath == currentPath)
|
||||
{
|
||||
result.Add(new RenameHistoryEntry
|
||||
{
|
||||
CommitSHA = currentCommit,
|
||||
OldPath = oldPath,
|
||||
NewPath = newPath
|
||||
});
|
||||
|
||||
currentPath = oldPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private class RenameHistoryEntry
|
||||
{
|
||||
public string CommitSHA { get; set; }
|
||||
public string OldPath { get; set; }
|
||||
public string NewPath { get; set; }
|
||||
}
|
||||
|
||||
private readonly string _revision;
|
||||
private readonly string _currentPath;
|
||||
}
|
||||
}
|
|
@ -9,17 +9,18 @@ namespace SourceGit.Models
|
|||
Tree,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ChangeState
|
||||
{
|
||||
None,
|
||||
Modified,
|
||||
TypeChanged,
|
||||
Added,
|
||||
Deleted,
|
||||
Renamed,
|
||||
Copied,
|
||||
Untracked,
|
||||
Conflicted,
|
||||
None = 0,
|
||||
Modified = 1 << 0,
|
||||
TypeChanged = 1 << 1,
|
||||
Added = 1 << 2,
|
||||
Deleted = 1 << 3,
|
||||
Renamed = 1 << 4,
|
||||
Copied = 1 << 5,
|
||||
Untracked = 1 << 6,
|
||||
Conflicted = 1 << 7,
|
||||
}
|
||||
|
||||
public enum ConflictReason
|
||||
|
@ -85,6 +86,30 @@ namespace SourceGit.Models
|
|||
OriginalPath = OriginalPath.Substring(1, OriginalPath.Length - 2);
|
||||
}
|
||||
|
||||
public static ChangeState GetPrimaryState(ChangeState state)
|
||||
{
|
||||
if (state == ChangeState.None)
|
||||
return ChangeState.None;
|
||||
if ((state & ChangeState.Conflicted) != 0)
|
||||
return ChangeState.Conflicted;
|
||||
if ((state & ChangeState.Untracked) != 0)
|
||||
return ChangeState.Untracked;
|
||||
if ((state & ChangeState.Renamed) != 0)
|
||||
return ChangeState.Renamed;
|
||||
if ((state & ChangeState.Copied) != 0)
|
||||
return ChangeState.Copied;
|
||||
if ((state & ChangeState.Deleted) != 0)
|
||||
return ChangeState.Deleted;
|
||||
if ((state & ChangeState.Added) != 0)
|
||||
return ChangeState.Added;
|
||||
if ((state & ChangeState.TypeChanged) != 0)
|
||||
return ChangeState.TypeChanged;
|
||||
if ((state & ChangeState.Modified) != 0)
|
||||
return ChangeState.Modified;
|
||||
|
||||
return ChangeState.None;
|
||||
}
|
||||
|
||||
private static readonly string[] CONFLICT_MARKERS =
|
||||
[
|
||||
string.Empty,
|
||||
|
|
|
@ -49,7 +49,8 @@ namespace SourceGit.ViewModels
|
|||
|
||||
public void ResetToSelectedRevision()
|
||||
{
|
||||
new Commands.Checkout(_repo.FullPath).FileWithRevision(_file, $"{_revision.SHA}");
|
||||
var revisionFilePath = new Commands.QueryFilePathInRevision(_repo.FullPath, _revision.SHA, _file).Result();
|
||||
new Commands.Checkout(_repo.FullPath).FileWithRevision(revisionFilePath, $"{_revision.SHA}");
|
||||
}
|
||||
|
||||
private void RefreshViewContent()
|
||||
|
@ -62,10 +63,12 @@ namespace SourceGit.ViewModels
|
|||
|
||||
private void SetViewContentAsRevisionFile()
|
||||
{
|
||||
var objs = new Commands.QueryRevisionObjects(_repo.FullPath, _revision.SHA, _file).Result();
|
||||
var revisionFilePath = new Commands.QueryFilePathInRevision(_repo.FullPath, _revision.SHA, _file).Result();
|
||||
|
||||
var objs = new Commands.QueryRevisionObjects(_repo.FullPath, _revision.SHA, revisionFilePath).Result();
|
||||
if (objs.Count == 0)
|
||||
{
|
||||
ViewContent = new FileHistoriesRevisionFile(_file, null);
|
||||
ViewContent = new FileHistoriesRevisionFile(revisionFilePath, null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -75,30 +78,29 @@ namespace SourceGit.ViewModels
|
|||
case Models.ObjectType.Blob:
|
||||
Task.Run(() =>
|
||||
{
|
||||
var isBinary = new Commands.IsBinary(_repo.FullPath, _revision.SHA, _file).Result();
|
||||
var isBinary = new Commands.IsBinary(_repo.FullPath, _revision.SHA, revisionFilePath).Result();
|
||||
if (isBinary)
|
||||
{
|
||||
var ext = Path.GetExtension(_file);
|
||||
var ext = Path.GetExtension(revisionFilePath);
|
||||
if (IMG_EXTS.Contains(ext))
|
||||
{
|
||||
var stream = Commands.QueryFileContent.Run(_repo.FullPath, _revision.SHA, _file);
|
||||
var stream = Commands.QueryFileContent.Run(_repo.FullPath, _revision.SHA, revisionFilePath);
|
||||
var fileSize = stream.Length;
|
||||
var bitmap = fileSize > 0 ? new Bitmap(stream) : null;
|
||||
var imageType = Path.GetExtension(_file).TrimStart('.').ToUpper(CultureInfo.CurrentCulture);
|
||||
var imageType = Path.GetExtension(revisionFilePath).TrimStart('.').ToUpper(CultureInfo.CurrentCulture);
|
||||
var image = new Models.RevisionImageFile() { Image = bitmap, FileSize = fileSize, ImageType = imageType };
|
||||
Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, image));
|
||||
Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(revisionFilePath, image));
|
||||
}
|
||||
else
|
||||
{
|
||||
var size = new Commands.QueryFileSize(_repo.FullPath, _file, _revision.SHA).Result();
|
||||
var size = new Commands.QueryFileSize(_repo.FullPath, revisionFilePath, _revision.SHA).Result();
|
||||
var binaryFile = new Models.RevisionBinaryFile() { Size = size };
|
||||
Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, binaryFile));
|
||||
Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(revisionFilePath, binaryFile));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var contentStream = Commands.QueryFileContent.Run(_repo.FullPath, _revision.SHA, _file);
|
||||
var contentStream = Commands.QueryFileContent.Run(_repo.FullPath, _revision.SHA, revisionFilePath);
|
||||
var content = new StreamReader(contentStream).ReadToEnd();
|
||||
var matchLFS = REG_LFS_FORMAT().Match(content);
|
||||
if (matchLFS.Success)
|
||||
|
@ -106,19 +108,19 @@ namespace SourceGit.ViewModels
|
|||
var lfs = new Models.RevisionLFSObject() { Object = new Models.LFSObject() };
|
||||
lfs.Object.Oid = matchLFS.Groups[1].Value;
|
||||
lfs.Object.Size = long.Parse(matchLFS.Groups[2].Value);
|
||||
Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, lfs));
|
||||
Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(revisionFilePath, lfs));
|
||||
}
|
||||
else
|
||||
{
|
||||
var txt = new Models.RevisionTextFile() { FileName = obj.Path, Content = content };
|
||||
Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, txt));
|
||||
Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(revisionFilePath, txt));
|
||||
}
|
||||
});
|
||||
break;
|
||||
case Models.ObjectType.Commit:
|
||||
Task.Run(() =>
|
||||
{
|
||||
var submoduleRoot = Path.Combine(_repo.FullPath, _file);
|
||||
var submoduleRoot = Path.Combine(_repo.FullPath, revisionFilePath);
|
||||
var commit = new Commands.QuerySingleCommit(submoduleRoot, obj.SHA).Result();
|
||||
if (commit != null)
|
||||
{
|
||||
|
@ -128,7 +130,7 @@ namespace SourceGit.ViewModels
|
|||
Commit = commit,
|
||||
FullMessage = new Models.CommitFullMessage { Message = message }
|
||||
};
|
||||
Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, module));
|
||||
Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(revisionFilePath, module));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -137,20 +139,38 @@ namespace SourceGit.ViewModels
|
|||
Commit = new Models.Commit() { SHA = obj.SHA },
|
||||
FullMessage = null
|
||||
};
|
||||
Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(_file, module));
|
||||
Dispatcher.UIThread.Invoke(() => ViewContent = new FileHistoriesRevisionFile(revisionFilePath, module));
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
ViewContent = new FileHistoriesRevisionFile(_file, null);
|
||||
ViewContent = new FileHistoriesRevisionFile(revisionFilePath, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetViewContentAsDiff()
|
||||
{
|
||||
var option = new Models.DiffOption(_revision, _file);
|
||||
ViewContent = new DiffContext(_repo.FullPath, option, _viewContent as DiffContext);
|
||||
var revisionFilePath = new Commands.QueryFilePathInRevision(_repo.FullPath, _revision.SHA, _file).Result();
|
||||
|
||||
if (_revision.Parents.Count > 0)
|
||||
{
|
||||
var parentSHA = _revision.Parents[0];
|
||||
var changes = new Commands.CompareRevisions(_repo.FullPath, parentSHA, _revision.SHA).Result();
|
||||
foreach (var change in changes)
|
||||
{
|
||||
if ((change.WorkTree == Models.ChangeState.Renamed || change.Index == Models.ChangeState.Renamed)
|
||||
&& change.Path == revisionFilePath)
|
||||
{
|
||||
var option = new Models.DiffOption(parentSHA, _revision.SHA, change);
|
||||
ViewContent = new DiffContext(_repo.FullPath, option, _viewContent as DiffContext);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var defaultOption = new Models.DiffOption(_revision, revisionFilePath);
|
||||
ViewContent = new DiffContext(_repo.FullPath, defaultOption, _viewContent as DiffContext);
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^version https://git-lfs.github.com/spec/v\d+\r?\noid sha256:([0-9a-f]+)\r?\nsize (\d+)[\r\n]*$")]
|
||||
|
@ -216,7 +236,105 @@ namespace SourceGit.ViewModels
|
|||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
_changes = new Commands.CompareRevisions(_repo.FullPath, _startPoint.SHA, _endPoint.SHA, _file).Result();
|
||||
var startFilePath = new Commands.QueryFilePathInRevision(_repo.FullPath, _startPoint.SHA, _file).Result();
|
||||
var endFilePath = new Commands.QueryFilePathInRevision(_repo.FullPath, _endPoint.SHA, _file).Result();
|
||||
var allChanges = new Commands.CompareRevisions(_repo.FullPath, _startPoint.SHA, _endPoint.SHA).Result();
|
||||
|
||||
var startCommand = new Commands.QueryRevisionObjects(_repo.FullPath, _startPoint.SHA, startFilePath);
|
||||
var startResult = startCommand.Result();
|
||||
bool startFileExists = startResult.Count > 0;
|
||||
|
||||
var endCommand = new Commands.QueryRevisionObjects(_repo.FullPath, _endPoint.SHA, endFilePath);
|
||||
var endResult = endCommand.Result();
|
||||
bool endFileExists = endResult.Count > 0;
|
||||
|
||||
Models.Change renamedChange = null;
|
||||
foreach (var change in allChanges)
|
||||
{
|
||||
if ((change.WorkTree & Models.ChangeState.Renamed) != 0 ||
|
||||
(change.Index & Models.ChangeState.Renamed) != 0)
|
||||
{
|
||||
if (change.Path == endFilePath || change.OriginalPath == startFilePath)
|
||||
{
|
||||
renamedChange = change;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool hasChanges = false;
|
||||
|
||||
if (renamedChange != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(renamedChange.OriginalPath))
|
||||
renamedChange.OriginalPath = startFilePath;
|
||||
|
||||
if (string.IsNullOrEmpty(renamedChange.Path))
|
||||
renamedChange.Path = endFilePath;
|
||||
|
||||
bool hasContentChange = (!startFileExists || IsEmptyFile(_repo.FullPath, _startPoint.SHA, startFilePath)) &&
|
||||
endFileExists && !IsEmptyFile(_repo.FullPath, _endPoint.SHA, endFilePath);
|
||||
|
||||
if (!hasContentChange)
|
||||
hasContentChange = ContainsContentChanges(allChanges, startFilePath, endFilePath);
|
||||
|
||||
if (hasContentChange)
|
||||
{
|
||||
renamedChange.Index |= Models.ChangeState.Modified;
|
||||
renamedChange.WorkTree |= Models.ChangeState.Modified;
|
||||
}
|
||||
|
||||
_changes = [renamedChange];
|
||||
hasChanges = true;
|
||||
}
|
||||
else if (startFilePath != endFilePath)
|
||||
{
|
||||
_changes = new Commands.CompareRevisions(_repo.FullPath, _startPoint.SHA, _endPoint.SHA, startFilePath).Result();
|
||||
|
||||
if (_changes.Count == 0)
|
||||
{
|
||||
var renamed = new Models.Change()
|
||||
{
|
||||
OriginalPath = startFilePath,
|
||||
Path = endFilePath
|
||||
};
|
||||
|
||||
bool hasContentChange = (!startFileExists || IsEmptyFile(_repo.FullPath, _startPoint.SHA, startFilePath)) &&
|
||||
endFileExists && !IsEmptyFile(_repo.FullPath, _endPoint.SHA, endFilePath);
|
||||
|
||||
if (hasContentChange)
|
||||
renamed.Set(Models.ChangeState.Modified | Models.ChangeState.Renamed);
|
||||
else
|
||||
renamed.Set(Models.ChangeState.Renamed);
|
||||
|
||||
_changes = [renamed];
|
||||
hasChanges = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var change in _changes)
|
||||
{
|
||||
if (string.IsNullOrEmpty(change.OriginalPath) && change.Path == startFilePath)
|
||||
{
|
||||
change.OriginalPath = startFilePath;
|
||||
change.Path = endFilePath;
|
||||
|
||||
change.Index |= Models.ChangeState.Renamed;
|
||||
change.WorkTree |= Models.ChangeState.Renamed;
|
||||
}
|
||||
}
|
||||
hasChanges = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasChanges)
|
||||
{
|
||||
_changes = new Commands.CompareRevisions(_repo.FullPath, _startPoint.SHA, _endPoint.SHA, endFilePath).Result();
|
||||
|
||||
if (_changes.Count == 0)
|
||||
_changes = new Commands.CompareRevisions(_repo.FullPath, _startPoint.SHA, _endPoint.SHA, _file).Result();
|
||||
}
|
||||
|
||||
if (_changes.Count == 0)
|
||||
{
|
||||
Dispatcher.UIThread.Invoke(() => ViewContent = null);
|
||||
|
@ -228,6 +346,38 @@ namespace SourceGit.ViewModels
|
|||
});
|
||||
}
|
||||
|
||||
private bool ContainsContentChanges(List<Models.Change> changes, string startPath, string endPath)
|
||||
{
|
||||
foreach (var change in changes)
|
||||
{
|
||||
if (change.Path == endPath || change.OriginalPath == startPath)
|
||||
{
|
||||
bool hasContentChanges =
|
||||
(change.WorkTree == Models.ChangeState.Modified ||
|
||||
change.WorkTree == Models.ChangeState.Added ||
|
||||
change.Index == Models.ChangeState.Modified ||
|
||||
change.Index == Models.ChangeState.Added);
|
||||
|
||||
if (hasContentChanges)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsEmptyFile(string repoPath, string revision, string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var contentStream = Commands.QueryFileContent.Run(repoPath, revision, filePath);
|
||||
return contentStream != null && contentStream.Length == 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private Repository _repo = null;
|
||||
private string _file = null;
|
||||
private Models.Commit _startPoint = null;
|
||||
|
@ -270,7 +420,7 @@ namespace SourceGit.ViewModels
|
|||
Task.Run(() =>
|
||||
{
|
||||
var based = commit ?? string.Empty;
|
||||
var commits = new Commands.QueryCommits(_repo.FullPath, $"--date-order -n 10000 {based} -- \"{file}\"", false).Result();
|
||||
var commits = new Commands.QueryCommits(_repo.FullPath, $"--date-order --follow -n 10000 {based} -- \"{file}\"", false).Result();
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
IsLoading = false;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
|
@ -9,55 +9,86 @@ namespace SourceGit.Views
|
|||
{
|
||||
public class ChangeStatusIcon : Control
|
||||
{
|
||||
private static readonly IBrush[] BACKGROUNDS = [
|
||||
Brushes.Transparent,
|
||||
new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
private static readonly Dictionary<Models.ChangeState, IBrush> BACKGROUNDS = new Dictionary<Models.ChangeState, IBrush>()
|
||||
{
|
||||
{ Models.ChangeState.None, Brushes.Transparent },
|
||||
{ Models.ChangeState.Modified, new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
}
|
||||
},
|
||||
new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
{ Models.ChangeState.TypeChanged, new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
}
|
||||
},
|
||||
new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(47, 185, 47), 0), new GradientStop(Color.FromRgb(75, 189, 75), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
{ Models.ChangeState.Added, new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(47, 185, 47), 0), new GradientStop(Color.FromRgb(75, 189, 75), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
}
|
||||
},
|
||||
new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Colors.Tomato, 0), new GradientStop(Color.FromRgb(252, 165, 150), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
{ Models.ChangeState.Deleted, new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Colors.Tomato, 0), new GradientStop(Color.FromRgb(252, 165, 150), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
}
|
||||
},
|
||||
new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Colors.Orchid, 0), new GradientStop(Color.FromRgb(248, 161, 245), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
{ Models.ChangeState.Renamed, new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Colors.Orchid, 0), new GradientStop(Color.FromRgb(248, 161, 245), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
}
|
||||
},
|
||||
new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
{ Models.ChangeState.Copied, new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(238, 160, 14), 0), new GradientStop(Color.FromRgb(228, 172, 67), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
}
|
||||
},
|
||||
new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(47, 185, 47), 0), new GradientStop(Color.FromRgb(75, 189, 75), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
{ Models.ChangeState.Untracked, new LinearGradientBrush
|
||||
{
|
||||
GradientStops = new GradientStops() { new GradientStop(Color.FromRgb(47, 185, 47), 0), new GradientStop(Color.FromRgb(75, 189, 75), 1) },
|
||||
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
|
||||
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
|
||||
}
|
||||
},
|
||||
Brushes.OrangeRed,
|
||||
];
|
||||
{ Models.ChangeState.Conflicted, Brushes.OrangeRed },
|
||||
};
|
||||
|
||||
private static readonly string[] INDICATOR = ["?", "±", "T", "+", "−", "➜", "❏", "★", "!"];
|
||||
private static readonly string[] TIPS = ["Unknown", "Modified", "Type Changed", "Added", "Deleted", "Renamed", "Copied", "Untracked", "Conflict"];
|
||||
private static readonly Dictionary<Models.ChangeState, string> INDICATOR = new Dictionary<Models.ChangeState, string>()
|
||||
{
|
||||
{ Models.ChangeState.None, "?" },
|
||||
{ Models.ChangeState.Modified, "±" },
|
||||
{ Models.ChangeState.TypeChanged, "T" },
|
||||
{ Models.ChangeState.Added, "+" },
|
||||
{ Models.ChangeState.Deleted, "−" },
|
||||
{ Models.ChangeState.Renamed, "➜" },
|
||||
{ Models.ChangeState.Copied, "❏" },
|
||||
{ Models.ChangeState.Untracked, "★" },
|
||||
{ Models.ChangeState.Conflicted, "!" }
|
||||
};
|
||||
|
||||
private static readonly Dictionary<Models.ChangeState, string> TIPS = new Dictionary<Models.ChangeState, string>()
|
||||
{
|
||||
{ Models.ChangeState.None, "Unknown" },
|
||||
{ Models.ChangeState.Modified, "Modified" },
|
||||
{ Models.ChangeState.TypeChanged, "Type Changed" },
|
||||
{ Models.ChangeState.Added, "Added" },
|
||||
{ Models.ChangeState.Deleted, "Deleted" },
|
||||
{ Models.ChangeState.Renamed, "Renamed" },
|
||||
{ Models.ChangeState.Copied, "Copied" },
|
||||
{ Models.ChangeState.Untracked, "Untracked" },
|
||||
{ Models.ChangeState.Conflicted, "Conflict" }
|
||||
};
|
||||
|
||||
public static readonly StyledProperty<bool> IsUnstagedChangeProperty =
|
||||
AvaloniaProperty.Register<ChangeStatusIcon, bool>(nameof(IsUnstagedChange));
|
||||
|
@ -88,13 +119,15 @@ namespace SourceGit.Views
|
|||
string indicator;
|
||||
if (IsUnstagedChange)
|
||||
{
|
||||
background = BACKGROUNDS[(int)Change.WorkTree];
|
||||
indicator = INDICATOR[(int)Change.WorkTree];
|
||||
var status = Models.Change.GetPrimaryState(Change.WorkTree);
|
||||
background = BACKGROUNDS[status];
|
||||
indicator = INDICATOR[status];
|
||||
}
|
||||
else
|
||||
{
|
||||
background = BACKGROUNDS[(int)Change.Index];
|
||||
indicator = INDICATOR[(int)Change.Index];
|
||||
var status = Models.Change.GetPrimaryState(Change.Index);
|
||||
background = BACKGROUNDS[status];
|
||||
indicator = INDICATOR[status];
|
||||
}
|
||||
|
||||
var txt = new FormattedText(
|
||||
|
@ -130,11 +163,11 @@ namespace SourceGit.Views
|
|||
if (c.IsConflicted)
|
||||
ToolTip.SetTip(this, $"Conflict ({c.ConflictDesc})");
|
||||
else
|
||||
ToolTip.SetTip(this, TIPS[(int)c.WorkTree]);
|
||||
ToolTip.SetTip(this, TIPS[Models.Change.GetPrimaryState(c.Index)]);
|
||||
}
|
||||
else
|
||||
{
|
||||
ToolTip.SetTip(this, TIPS[(int)c.Index]);
|
||||
ToolTip.SetTip(this, TIPS[Models.Change.GetPrimaryState(c.Index)]);
|
||||
}
|
||||
|
||||
InvalidateVisual();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue