mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-11-01 13:13:21 -07:00
275 lines
9.7 KiB
C#
275 lines
9.7 KiB
C#
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Windows;
|
||
using System.Windows.Media;
|
||
|
||
namespace SourceGit.Helpers {
|
||
|
||
/// <summary>
|
||
/// Tools to parse commit graph.
|
||
/// </summary>
|
||
public class CommitGraphMaker {
|
||
/// <summary>
|
||
/// Sizes
|
||
/// </summary>
|
||
public static readonly double UNIT_WIDTH = 12;
|
||
public static readonly double HALF_WIDTH = 6;
|
||
public static readonly double DOUBLE_WIDTH = 24;
|
||
public static readonly double UNIT_HEIGHT = 24;
|
||
public static readonly double HALF_HEIGHT = 12;
|
||
|
||
/// <summary>
|
||
/// Colors
|
||
/// </summary>
|
||
public static Brush[] Colors = new Brush[] {
|
||
Brushes.Orange,
|
||
Brushes.ForestGreen,
|
||
Brushes.Gold,
|
||
Brushes.Magenta,
|
||
Brushes.Red,
|
||
Brushes.Gray,
|
||
Brushes.Turquoise,
|
||
Brushes.Olive,
|
||
};
|
||
|
||
/// <summary>
|
||
/// Helpers to draw lines.
|
||
/// </summary>
|
||
public class LineHelper {
|
||
private double lastX = 0;
|
||
private double lastY = 0;
|
||
|
||
/// <summary>
|
||
/// Parent commit id.
|
||
/// </summary>
|
||
public string Next { get; set; }
|
||
|
||
/// <summary>
|
||
/// Is merged into this tree.
|
||
/// </summary>
|
||
public bool IsMerged { get; set; }
|
||
|
||
/// <summary>
|
||
/// Points in line
|
||
/// </summary>
|
||
public List<Point> Points { get; set; }
|
||
|
||
/// <summary>
|
||
/// Brush to draw line
|
||
/// </summary>
|
||
public Brush Brush { get; set; }
|
||
|
||
/// <summary>
|
||
/// Current horizontal offset.
|
||
/// </summary>
|
||
public double HorizontalOffset => lastX;
|
||
|
||
/// <summary>
|
||
/// Constructor.
|
||
/// </summary>
|
||
/// <param name="nextCommitId">Parent commit id</param>
|
||
/// <param name="isMerged">Is merged in tree</param>
|
||
/// <param name="colorIdx">Color index</param>
|
||
/// <param name="startPoint">Start point</param>
|
||
public LineHelper(string nextCommitId, bool isMerged, int colorIdx, Point startPoint) {
|
||
Next = nextCommitId;
|
||
IsMerged = isMerged;
|
||
Points = new List<Point>() { startPoint };
|
||
Brush = Colors[colorIdx % Colors.Length];
|
||
|
||
lastX = startPoint.X;
|
||
lastY = startPoint.Y;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Line to.
|
||
/// </summary>
|
||
/// <param name="x"></param>
|
||
/// <param name="y"></param>
|
||
/// <param name="isEnd"></param>
|
||
public void AddPoint(double x, double y, bool isEnd = false) {
|
||
if (x > lastX) {
|
||
Points.Add(new Point(lastX, lastY));
|
||
Points.Add(new Point(x, y - HALF_HEIGHT));
|
||
} else if (x < lastX) {
|
||
Points.Add(new Point(lastX, lastY + HALF_HEIGHT));
|
||
Points.Add(new Point(x, y));
|
||
}
|
||
|
||
lastX = x;
|
||
lastY = y;
|
||
|
||
if (isEnd) {
|
||
var last = Points.Last();
|
||
if (last.X != lastX || last.Y != lastY) Points.Add(new Point(lastX, lastY));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Short link between two commits.
|
||
/// </summary>
|
||
public struct ShortLink {
|
||
public Point Start;
|
||
public Point Control;
|
||
public Point End;
|
||
public Brush Brush;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Dot
|
||
/// </summary>
|
||
public struct Dot {
|
||
public double X;
|
||
public double Y;
|
||
public Brush Color;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Independent lines in graph
|
||
/// </summary>
|
||
public List<LineHelper> Lines { get; set; } = new List<LineHelper>();
|
||
|
||
/// <summary>
|
||
/// Short links.
|
||
/// </summary>
|
||
public List<ShortLink> Links { get; set; } = new List<ShortLink>();
|
||
|
||
/// <summary>
|
||
/// All dots.
|
||
/// </summary>
|
||
public List<Dot> Dots { get; set; } = new List<Dot>();
|
||
|
||
/// <summary>
|
||
/// Highlight commit id.
|
||
/// </summary>
|
||
public string Highlight { get; set; }
|
||
|
||
/// <summary>
|
||
/// Parse commits.
|
||
/// </summary>
|
||
/// <param name="commits"></param>
|
||
/// <returns></returns>
|
||
public static CommitGraphMaker Parse(List<Git.Commit> commits) {
|
||
CommitGraphMaker maker = new CommitGraphMaker();
|
||
|
||
List<LineHelper> unsolved = new List<LineHelper>();
|
||
List<LineHelper> ended = new List<LineHelper>();
|
||
Dictionary<string, LineHelper> currentMap = new Dictionary<string, LineHelper>();
|
||
double offsetY = -HALF_HEIGHT;
|
||
int colorIdx = 0;
|
||
|
||
for (int i = 0; i < commits.Count; i++) {
|
||
Git.Commit commit = commits[i];
|
||
LineHelper major = null;
|
||
bool isMerged = commit.IsHEAD || commit.IsMerged;
|
||
int oldCount = unsolved.Count;
|
||
|
||
// 更新Y坐标
|
||
offsetY += UNIT_HEIGHT;
|
||
|
||
// 找到当前的分支的HEAD,用于默认选中
|
||
if (maker.Highlight == null && commit.IsHEAD) {
|
||
maker.Highlight = commit.SHA;
|
||
}
|
||
|
||
// 找到第一个依赖于本提交的树,将其他依赖于本提交的树标记为终止,并对已存在的线路调整(防止线重合)
|
||
double offsetX = -HALF_WIDTH;
|
||
foreach (var l in unsolved) {
|
||
if (l.Next == commit.SHA) {
|
||
if (major == null) {
|
||
offsetX += UNIT_WIDTH;
|
||
major = l;
|
||
|
||
if (commit.Parents.Count > 0) {
|
||
major.Next = commit.Parents[0];
|
||
if (!currentMap.ContainsKey(major.Next)) currentMap.Add(major.Next, major);
|
||
} else {
|
||
major.Next = "ENDED";
|
||
ended.Add(l);
|
||
}
|
||
|
||
major.AddPoint(offsetX, offsetY);
|
||
} else {
|
||
ended.Add(l);
|
||
}
|
||
|
||
isMerged = isMerged || l.IsMerged;
|
||
} else {
|
||
if (!currentMap.ContainsKey(l.Next)) currentMap.Add(l.Next, l);
|
||
offsetX += UNIT_WIDTH;
|
||
l.AddPoint(offsetX, offsetY);
|
||
}
|
||
}
|
||
|
||
// 处理本提交为非当前分支HEAD的情况(创建新依赖线路)
|
||
if (major == null && commit.Parents.Count > 0) {
|
||
offsetX += UNIT_WIDTH;
|
||
major = new LineHelper(commit.Parents[0], isMerged, colorIdx, new Point(offsetX, offsetY));
|
||
unsolved.Add(major);
|
||
colorIdx++;
|
||
}
|
||
|
||
// 确定本提交的点的位置
|
||
Point position = new Point(offsetX, offsetY);
|
||
if (major != null) {
|
||
major.IsMerged = isMerged;
|
||
position.X = major.HorizontalOffset;
|
||
position.Y = offsetY;
|
||
maker.Dots.Add(new Dot() { X = position.X - 3, Y = position.Y - 3, Color = major.Brush });
|
||
} else {
|
||
maker.Dots.Add(new Dot() { X = position.X - 3, Y = position.Y - 3, Color = Brushes.Orange });
|
||
}
|
||
|
||
// 处理本提交的其他依赖
|
||
for (int j = 1; j < commit.Parents.Count; j++) {
|
||
var parent = commit.Parents[j];
|
||
if (currentMap.ContainsKey(parent)) {
|
||
var l = currentMap[parent];
|
||
var link = new ShortLink();
|
||
|
||
link.Start = position;
|
||
link.End = new Point(l.HorizontalOffset, offsetY + HALF_HEIGHT);
|
||
link.Control = new Point(link.End.X, link.Start.Y);
|
||
link.Brush = l.Brush;
|
||
maker.Links.Add(link);
|
||
} else {
|
||
offsetX += UNIT_WIDTH;
|
||
unsolved.Add(new LineHelper(commit.Parents[j], isMerged, colorIdx, position));
|
||
colorIdx++;
|
||
}
|
||
}
|
||
|
||
// 处理已终止的线
|
||
foreach (var l in ended) {
|
||
l.AddPoint(position.X, position.Y, true);
|
||
maker.Lines.Add(l);
|
||
unsolved.Remove(l);
|
||
}
|
||
|
||
// 加入本次提交
|
||
commit.IsMerged = isMerged;
|
||
commit.GraphOffset = System.Math.Max(offsetX + HALF_WIDTH, oldCount * UNIT_WIDTH);
|
||
|
||
// 清理临时数据
|
||
ended.Clear();
|
||
currentMap.Clear();
|
||
}
|
||
|
||
// 处理尚未终结的线
|
||
for (int i = 0; i < unsolved.Count; i++) {
|
||
var path = unsolved[i];
|
||
path.AddPoint((i + 0.5) * UNIT_WIDTH, (commits.Count - 0.5) * UNIT_HEIGHT, true);
|
||
maker.Lines.Add(path);
|
||
}
|
||
unsolved.Clear();
|
||
|
||
// 处理默认选中异常
|
||
if (maker.Highlight == null && commits.Count > 0) {
|
||
maker.Highlight = commits[0].SHA;
|
||
}
|
||
|
||
return maker;
|
||
}
|
||
}
|
||
}
|