mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-23 20:47:25 -08:00
feature: add an indicator that shows those commits the current branch ahead/behind its upstream
This commit is contained in:
parent
9de2853003
commit
f0649c95b5
12 changed files with 180 additions and 194 deletions
|
@ -24,20 +24,8 @@ namespace SourceGit.Commands
|
||||||
{
|
{
|
||||||
Exec();
|
Exec();
|
||||||
|
|
||||||
foreach (var b in _branches)
|
foreach (var b in _needQueryTrackStatus)
|
||||||
{
|
b.TrackStatus = new QueryTrackStatus(WorkingDirectory, b.Name, b.Upstream).Result();
|
||||||
if (b.IsLocal && !string.IsNullOrEmpty(b.UpstreamTrackStatus))
|
|
||||||
{
|
|
||||||
if (b.UpstreamTrackStatus == "=")
|
|
||||||
{
|
|
||||||
b.UpstreamTrackStatus = string.Empty;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
b.UpstreamTrackStatus = ParseTrackStatus(b.Name, b.Upstream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _branches;
|
return _branches;
|
||||||
}
|
}
|
||||||
|
@ -84,35 +72,16 @@ namespace SourceGit.Commands
|
||||||
branch.Head = parts[1];
|
branch.Head = parts[1];
|
||||||
branch.IsCurrent = parts[2] == "*";
|
branch.IsCurrent = parts[2] == "*";
|
||||||
branch.Upstream = parts[3];
|
branch.Upstream = parts[3];
|
||||||
branch.UpstreamTrackStatus = parts[4];
|
|
||||||
|
if (branch.IsLocal && !parts[4].Equals("=", StringComparison.Ordinal))
|
||||||
|
_needQueryTrackStatus.Add(branch);
|
||||||
|
else
|
||||||
|
branch.TrackStatus = new Models.BranchTrackStatus();
|
||||||
|
|
||||||
_branches.Add(branch);
|
_branches.Add(branch);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ParseTrackStatus(string local, string upstream)
|
|
||||||
{
|
|
||||||
var cmd = new Command();
|
|
||||||
cmd.WorkingDirectory = WorkingDirectory;
|
|
||||||
cmd.Context = Context;
|
|
||||||
cmd.Args = $"rev-list --left-right --count {local}...{upstream}";
|
|
||||||
|
|
||||||
var rs = cmd.ReadToEnd();
|
|
||||||
if (!rs.IsSuccess)
|
|
||||||
return string.Empty;
|
|
||||||
|
|
||||||
var match = REG_AHEAD_BEHIND().Match(rs.StdOut);
|
|
||||||
if (!match.Success)
|
|
||||||
return string.Empty;
|
|
||||||
|
|
||||||
var ahead = int.Parse(match.Groups[1].Value);
|
|
||||||
var behind = int.Parse(match.Groups[2].Value);
|
|
||||||
var track = "";
|
|
||||||
if (ahead > 0)
|
|
||||||
track += $"{ahead}↑";
|
|
||||||
if (behind > 0)
|
|
||||||
track += $" {behind}↓";
|
|
||||||
return track.Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly List<Models.Branch> _branches = new List<Models.Branch>();
|
private readonly List<Models.Branch> _branches = new List<Models.Branch>();
|
||||||
|
private List<Models.Branch> _needQueryTrackStatus = new List<Models.Branch>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,6 @@ namespace SourceGit.Commands
|
||||||
argsBuilder.Append("--all-match -i");
|
argsBuilder.Append("--all-match -i");
|
||||||
search = argsBuilder.ToString();
|
search = argsBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
WorkingDirectory = repo;
|
WorkingDirectory = repo;
|
||||||
Context = repo;
|
Context = repo;
|
||||||
|
@ -63,7 +62,9 @@ namespace SourceGit.Commands
|
||||||
ParseParent(line);
|
ParseParent(line);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
ParseDecorators(line);
|
_current.ParseDecorators(line);
|
||||||
|
if (_current.IsMerged && !_isHeadFounded)
|
||||||
|
_isHeadFounded = true;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
_current.Author = Models.User.FindOrAdd(line);
|
_current.Author = Models.User.FindOrAdd(line);
|
||||||
|
@ -114,74 +115,6 @@ namespace SourceGit.Commands
|
||||||
_current.Parents.Add(data.Substring(idx + 1));
|
_current.Parents.Add(data.Substring(idx + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ParseDecorators(string data)
|
|
||||||
{
|
|
||||||
if (data.Length < 3)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var subs = data.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
foreach (var sub in subs)
|
|
||||||
{
|
|
||||||
var d = sub.Trim();
|
|
||||||
if (d.EndsWith("/HEAD", StringComparison.Ordinal))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
_current.Decorators.Add(new Models.Decorator()
|
|
||||||
{
|
|
||||||
Type = Models.DecoratorType.Tag,
|
|
||||||
Name = d.Substring(15),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
_current.IsMerged = true;
|
|
||||||
_current.Decorators.Add(new Models.Decorator()
|
|
||||||
{
|
|
||||||
Type = Models.DecoratorType.CurrentBranchHead,
|
|
||||||
Name = d.Substring(19),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (d.Equals("HEAD"))
|
|
||||||
{
|
|
||||||
_current.IsMerged = true;
|
|
||||||
_current.Decorators.Add(new Models.Decorator()
|
|
||||||
{
|
|
||||||
Type = Models.DecoratorType.CurrentCommitHead,
|
|
||||||
Name = d,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (d.StartsWith("refs/heads/", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
_current.Decorators.Add(new Models.Decorator()
|
|
||||||
{
|
|
||||||
Type = Models.DecoratorType.LocalBranchHead,
|
|
||||||
Name = d.Substring(11),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (d.StartsWith("refs/remotes/", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
_current.Decorators.Add(new Models.Decorator()
|
|
||||||
{
|
|
||||||
Type = Models.DecoratorType.RemoteBranchHead,
|
|
||||||
Name = d.Substring(13),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_current.Decorators.Sort((l, r) =>
|
|
||||||
{
|
|
||||||
if (l.Type != r.Type)
|
|
||||||
return (int)l.Type - (int)r.Type;
|
|
||||||
else
|
|
||||||
return string.Compare(l.Name, r.Name, StringComparison.Ordinal);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (_current.IsMerged && !_isHeadFounded)
|
|
||||||
_isHeadFounded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MarkFirstMerged()
|
private void MarkFirstMerged()
|
||||||
{
|
{
|
||||||
Args = $"log --since=\"{_commits[^1].CommitterTimeStr}\" --format=\"%H\"";
|
Args = $"log --since=\"{_commits[^1].CommitterTimeStr}\" --format=\"%H\"";
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace SourceGit.Commands
|
||||||
if (!string.IsNullOrEmpty(lines[1]))
|
if (!string.IsNullOrEmpty(lines[1]))
|
||||||
commit.Parents.AddRange(lines[1].Split(' ', StringSplitOptions.RemoveEmptyEntries));
|
commit.Parents.AddRange(lines[1].Split(' ', StringSplitOptions.RemoveEmptyEntries));
|
||||||
if (!string.IsNullOrEmpty(lines[2]))
|
if (!string.IsNullOrEmpty(lines[2]))
|
||||||
commit.IsMerged = ParseDecorators(commit.Decorators, lines[2]);
|
commit.ParseDecorators(lines[2]);
|
||||||
commit.Author = Models.User.FindOrAdd(lines[3]);
|
commit.Author = Models.User.FindOrAdd(lines[3]);
|
||||||
commit.AuthorTime = ulong.Parse(lines[4]);
|
commit.AuthorTime = ulong.Parse(lines[4]);
|
||||||
commit.Committer = Models.User.FindOrAdd(lines[5]);
|
commit.Committer = Models.User.FindOrAdd(lines[5]);
|
||||||
|
@ -39,70 +39,6 @@ namespace SourceGit.Commands
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ParseDecorators(List<Models.Decorator> decorators, string data)
|
|
||||||
{
|
|
||||||
bool isHeadOfCurrent = false;
|
|
||||||
|
|
||||||
var subs = data.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
foreach (var sub in subs)
|
|
||||||
{
|
|
||||||
var d = sub.Trim();
|
|
||||||
if (d.EndsWith("/HEAD", StringComparison.Ordinal))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
decorators.Add(new Models.Decorator()
|
|
||||||
{
|
|
||||||
Type = Models.DecoratorType.Tag,
|
|
||||||
Name = d.Substring(15),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
isHeadOfCurrent = true;
|
|
||||||
decorators.Add(new Models.Decorator()
|
|
||||||
{
|
|
||||||
Type = Models.DecoratorType.CurrentBranchHead,
|
|
||||||
Name = d.Substring(19),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (d.Equals("HEAD"))
|
|
||||||
{
|
|
||||||
isHeadOfCurrent = true;
|
|
||||||
decorators.Add(new Models.Decorator()
|
|
||||||
{
|
|
||||||
Type = Models.DecoratorType.CurrentCommitHead,
|
|
||||||
Name = d,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (d.StartsWith("refs/heads/", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
decorators.Add(new Models.Decorator()
|
|
||||||
{
|
|
||||||
Type = Models.DecoratorType.LocalBranchHead,
|
|
||||||
Name = d.Substring(11),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (d.StartsWith("refs/remotes/", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
decorators.Add(new Models.Decorator()
|
|
||||||
{
|
|
||||||
Type = Models.DecoratorType.RemoteBranchHead,
|
|
||||||
Name = d.Substring(13),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
decorators.Sort((l, r) =>
|
|
||||||
{
|
|
||||||
if (l.Type != r.Type)
|
|
||||||
return (int)l.Type - (int)r.Type;
|
|
||||||
else
|
|
||||||
return string.Compare(l.Name, r.Name, StringComparison.Ordinal);
|
|
||||||
});
|
|
||||||
|
|
||||||
return isHeadOfCurrent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
34
src/Commands/QueryTrackStatus.cs
Normal file
34
src/Commands/QueryTrackStatus.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace SourceGit.Commands
|
||||||
|
{
|
||||||
|
public class QueryTrackStatus : Command
|
||||||
|
{
|
||||||
|
public QueryTrackStatus(string repo, string local, string upstream)
|
||||||
|
{
|
||||||
|
WorkingDirectory = repo;
|
||||||
|
Context = repo;
|
||||||
|
Args = $"rev-list --left-right {local}...{upstream}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Models.BranchTrackStatus Result()
|
||||||
|
{
|
||||||
|
var status = new Models.BranchTrackStatus();
|
||||||
|
|
||||||
|
var rs = ReadToEnd();
|
||||||
|
if (!rs.IsSuccess)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
var lines = rs.StdOut.Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
if (line[0] == '>')
|
||||||
|
status.Behind.Add(line.Substring(1));
|
||||||
|
else
|
||||||
|
status.Ahead.Add(line.Substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,26 @@
|
||||||
namespace SourceGit.Models
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace SourceGit.Models
|
||||||
{
|
{
|
||||||
|
public class BranchTrackStatus
|
||||||
|
{
|
||||||
|
public List<string> Ahead { get; set; } = new List<string>();
|
||||||
|
public List<string> Behind { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (Ahead.Count == 0 && Behind.Count == 0)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var track = "";
|
||||||
|
if (Ahead.Count > 0)
|
||||||
|
track += $"{Ahead.Count}↑";
|
||||||
|
if (Behind.Count > 0)
|
||||||
|
track += $" {Behind.Count}↓";
|
||||||
|
return track.Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class Branch
|
public class Branch
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
@ -8,7 +29,7 @@
|
||||||
public bool IsLocal { get; set; }
|
public bool IsLocal { get; set; }
|
||||||
public bool IsCurrent { get; set; }
|
public bool IsCurrent { get; set; }
|
||||||
public string Upstream { get; set; }
|
public string Upstream { get; set; }
|
||||||
public string UpstreamTrackStatus { get; set; }
|
public BranchTrackStatus TrackStatus { get; set; }
|
||||||
public string Remote { get; set; }
|
public string Remote { get; set; }
|
||||||
public bool IsHead { get; set; }
|
public bool IsHead { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace SourceGit.Models
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
} = 0.5;
|
} = 0.65;
|
||||||
|
|
||||||
public string SHA { get; set; } = string.Empty;
|
public string SHA { get; set; } = string.Empty;
|
||||||
public User Author { get; set; } = User.Invalid;
|
public User Author { get; set; } = User.Invalid;
|
||||||
|
@ -23,7 +23,10 @@ namespace SourceGit.Models
|
||||||
public List<string> Parents { get; set; } = new List<string>();
|
public List<string> Parents { get; set; } = new List<string>();
|
||||||
public List<Decorator> Decorators { get; set; } = new List<Decorator>();
|
public List<Decorator> Decorators { get; set; } = new List<Decorator>();
|
||||||
public bool HasDecorators => Decorators.Count > 0;
|
public bool HasDecorators => Decorators.Count > 0;
|
||||||
|
|
||||||
public bool IsMerged { get; set; } = false;
|
public bool IsMerged { get; set; } = false;
|
||||||
|
public bool CanPushToUpstream { get; set; } = false;
|
||||||
|
public bool CanPullFromUpstream { get; set; } = false;
|
||||||
public Thickness Margin { get; set; } = new Thickness(0);
|
public Thickness Margin { get; set; } = new Thickness(0);
|
||||||
|
|
||||||
public string AuthorTimeStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");
|
public string AuthorTimeStr => DateTime.UnixEpoch.AddSeconds(AuthorTime).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");
|
||||||
|
@ -35,5 +38,70 @@ namespace SourceGit.Models
|
||||||
|
|
||||||
public double Opacity => IsMerged ? 1 : OpacityForNotMerged;
|
public double Opacity => IsMerged ? 1 : OpacityForNotMerged;
|
||||||
public FontWeight FontWeight => IsCurrentHead ? FontWeight.Bold : FontWeight.Regular;
|
public FontWeight FontWeight => IsCurrentHead ? FontWeight.Bold : FontWeight.Regular;
|
||||||
|
|
||||||
|
public void ParseDecorators(string data)
|
||||||
|
{
|
||||||
|
if (data.Length < 3)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var subs = data.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
foreach (var sub in subs)
|
||||||
|
{
|
||||||
|
var d = sub.Trim();
|
||||||
|
if (d.EndsWith("/HEAD", StringComparison.Ordinal))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
Decorators.Add(new Decorator()
|
||||||
|
{
|
||||||
|
Type = DecoratorType.Tag,
|
||||||
|
Name = d.Substring(15),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
IsMerged = true;
|
||||||
|
Decorators.Add(new Decorator()
|
||||||
|
{
|
||||||
|
Type = DecoratorType.CurrentBranchHead,
|
||||||
|
Name = d.Substring(19),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (d.Equals("HEAD"))
|
||||||
|
{
|
||||||
|
IsMerged = true;
|
||||||
|
Decorators.Add(new Decorator()
|
||||||
|
{
|
||||||
|
Type = DecoratorType.CurrentCommitHead,
|
||||||
|
Name = d,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (d.StartsWith("refs/heads/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
Decorators.Add(new Decorator()
|
||||||
|
{
|
||||||
|
Type = DecoratorType.LocalBranchHead,
|
||||||
|
Name = d.Substring(11),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (d.StartsWith("refs/remotes/", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
Decorators.Add(new Decorator()
|
||||||
|
{
|
||||||
|
Type = DecoratorType.RemoteBranchHead,
|
||||||
|
Name = d.Substring(13),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Decorators.Sort((l, r) =>
|
||||||
|
{
|
||||||
|
if (l.Type != r.Type)
|
||||||
|
return (int)l.Type - (int)r.Type;
|
||||||
|
else
|
||||||
|
return string.Compare(l.Name, r.Name, StringComparison.Ordinal);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,7 @@ namespace SourceGit.Models
|
||||||
_penCount = colors.Count;
|
_penCount = colors.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CommitGraph Parse(List<Commit> commits)
|
public static CommitGraph Parse(List<Commit> commits, HashSet<string> canPushCommits, HashSet<string> canPullCommits)
|
||||||
{
|
{
|
||||||
double UNIT_WIDTH = 12;
|
double UNIT_WIDTH = 12;
|
||||||
double HALF_WIDTH = 6;
|
double HALF_WIDTH = 6;
|
||||||
|
@ -148,6 +148,9 @@ namespace SourceGit.Models
|
||||||
var isMerged = commit.IsMerged;
|
var isMerged = commit.IsMerged;
|
||||||
var oldCount = unsolved.Count;
|
var oldCount = unsolved.Count;
|
||||||
|
|
||||||
|
commit.CanPushToUpstream = canPushCommits.Remove(commit.SHA);
|
||||||
|
commit.CanPullFromUpstream = !commit.CanPushToUpstream && canPullCommits.Remove(commit.SHA);
|
||||||
|
|
||||||
// Update current y offset
|
// Update current y offset
|
||||||
offsetY += UNIT_HEIGHT;
|
offsetY += UNIT_HEIGHT;
|
||||||
|
|
||||||
|
|
|
@ -36,14 +36,9 @@ namespace SourceGit.ViewModels
|
||||||
get => Backend is Models.Branch;
|
get => Backend is Models.Branch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsUpstreamTrackStatusVisible
|
public string TrackStatus
|
||||||
{
|
{
|
||||||
get => Backend is Models.Branch { IsLocal: true } branch && !string.IsNullOrEmpty(branch.UpstreamTrackStatus);
|
get => Backend is Models.Branch { IsLocal: true } branch ? branch.TrackStatus.ToString() : string.Empty;
|
||||||
}
|
|
||||||
|
|
||||||
public string UpstreamTrackStatus
|
|
||||||
{
|
|
||||||
get => Backend is Models.Branch branch ? branch.UpstreamTrackStatus : "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FontWeight NameFontWeight
|
public FontWeight NameFontWeight
|
||||||
|
|
|
@ -451,7 +451,7 @@ namespace SourceGit.ViewModels
|
||||||
var fastForward = new MenuItem();
|
var fastForward = new MenuItem();
|
||||||
fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream);
|
fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream);
|
||||||
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
||||||
fastForward.IsEnabled = !string.IsNullOrEmpty(current.UpstreamTrackStatus) && current.UpstreamTrackStatus.IndexOf('↑') < 0;
|
fastForward.IsEnabled = current.TrackStatus.Ahead.Count == 0;
|
||||||
fastForward.Click += (_, e) =>
|
fastForward.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
if (PopupHost.CanCreatePopup())
|
if (PopupHost.CanCreatePopup())
|
||||||
|
|
|
@ -784,8 +784,20 @@ namespace SourceGit.ViewModels
|
||||||
limits += "--branches --remotes --tags";
|
limits += "--branches --remotes --tags";
|
||||||
}
|
}
|
||||||
|
|
||||||
var commits = new Commands.QueryCommits(FullPath, limits).Result();
|
var canPushCommits = new HashSet<string>();
|
||||||
var graph = Models.CommitGraph.Parse(commits);
|
var canPullCommits = new HashSet<string>();
|
||||||
|
var currentBranch = Branches.Find(x => x.IsCurrent);
|
||||||
|
if (currentBranch != null)
|
||||||
|
{
|
||||||
|
foreach (var sha in currentBranch.TrackStatus.Ahead)
|
||||||
|
canPushCommits.Add(sha);
|
||||||
|
|
||||||
|
foreach (var sha in currentBranch.TrackStatus.Behind)
|
||||||
|
canPullCommits.Add(sha);
|
||||||
|
}
|
||||||
|
|
||||||
|
var commits = new Commands.QueryCommits(_fullpath, limits).Result();
|
||||||
|
var graph = Models.CommitGraph.Parse(commits, canPushCommits, canPullCommits);
|
||||||
|
|
||||||
Dispatcher.UIThread.Invoke(() =>
|
Dispatcher.UIThread.Invoke(() =>
|
||||||
{
|
{
|
||||||
|
@ -1244,7 +1256,7 @@ namespace SourceGit.ViewModels
|
||||||
var fastForward = new MenuItem();
|
var fastForward = new MenuItem();
|
||||||
fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream);
|
fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream);
|
||||||
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
||||||
fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0;
|
fastForward.IsEnabled = branch.TrackStatus.Ahead.Count == 0;
|
||||||
fastForward.Click += (_, e) =>
|
fastForward.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
if (PopupHost.CanCreatePopup())
|
if (PopupHost.CanCreatePopup())
|
||||||
|
@ -1295,7 +1307,7 @@ namespace SourceGit.ViewModels
|
||||||
var fastForward = new MenuItem();
|
var fastForward = new MenuItem();
|
||||||
fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream.FriendlyName);
|
fastForward.Header = new Views.NameHighlightedTextBlock("BranchCM.FastForward", upstream.FriendlyName);
|
||||||
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
fastForward.Icon = App.CreateMenuIcon("Icons.FastForward");
|
||||||
fastForward.IsEnabled = !string.IsNullOrEmpty(branch.UpstreamTrackStatus) && branch.UpstreamTrackStatus.IndexOf('↑') < 0;
|
fastForward.IsEnabled = branch.TrackStatus.Ahead.Count == 0;
|
||||||
fastForward.Click += (_, e) =>
|
fastForward.Click += (_, e) =>
|
||||||
{
|
{
|
||||||
if (PopupHost.CanCreatePopup())
|
if (PopupHost.CanCreatePopup())
|
||||||
|
|
|
@ -88,8 +88,13 @@
|
||||||
CornerRadius="9"
|
CornerRadius="9"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Background="{DynamicResource Brush.Badge}"
|
Background="{DynamicResource Brush.Badge}"
|
||||||
IsVisible="{Binding IsUpstreamTrackStatusVisible}">
|
IsVisible="{Binding TrackStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
||||||
<TextBlock Classes="monospace" FontSize="10" HorizontalAlignment="Center" Margin="9,0" Text="{Binding UpstreamTrackStatus}" Foreground="{DynamicResource Brush.BadgeFG}"/>
|
<TextBlock Classes="monospace"
|
||||||
|
FontSize="10"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Margin="9,0"
|
||||||
|
Text="{Binding TrackStatus}"
|
||||||
|
Foreground="{DynamicResource Brush.BadgeFG}"/>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- Filter Toggle Button -->
|
<!-- Filter Toggle Button -->
|
||||||
|
|
|
@ -100,6 +100,16 @@
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|
||||||
|
<Ellipse Width="5" Height="5"
|
||||||
|
Margin="0,0,4,0"
|
||||||
|
Fill="{DynamicResource Brush.Accent}"
|
||||||
|
IsVisible="{Binding CanPushToUpstream}"/>
|
||||||
|
|
||||||
|
<Ellipse Width="5" Height="5"
|
||||||
|
Margin="0,0,4,0"
|
||||||
|
Fill="{DynamicResource Brush.FG1}"
|
||||||
|
IsVisible="{Binding CanPullFromUpstream}"/>
|
||||||
|
|
||||||
<TextBlock Classes="monospace"
|
<TextBlock Classes="monospace"
|
||||||
Text="{Binding Subject}"
|
Text="{Binding Subject}"
|
||||||
|
|
Loading…
Reference in a new issue