using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace SourceGit.Git {
///
/// Git branch
///
public class Branch {
private static readonly string PRETTY_FORMAT = @"$%(refname)$%(objectname)$%(HEAD)$%(upstream)$%(upstream:track)$%(contents:subject)";
private static readonly Regex PARSE = new Regex(@"\$(.*)\$(.*)\$([\* ])\$(.*)\$(.*?)\$(.*)");
private static readonly Regex AHEAD = new Regex(@"ahead (\d+)");
private static readonly Regex BEHIND = new Regex(@"behind (\d+)");
///
/// Branch type.
///
public enum Type {
Normal,
Feature,
Release,
Hotfix,
}
///
/// Branch name
///
public string Name { get; set; }
///
/// Full name.
///
public string FullName { get; set; }
///
/// Head ref
///
public string Head { get; set; }
///
/// Subject for head ref.
///
public string HeadSubject { get; set; }
///
/// Is local branch
///
public bool IsLocal { get; set; }
///
/// Branch type.
///
public Type Kind { get; set; } = Type.Normal;
///
/// Remote name. Only used for remote branch
///
public string Remote { get; set; }
///
/// Upstream. Only used for local branches.
///
public string Upstream { get; set; }
///
/// Track information for upstream. Only used for local branches.
///
public string UpstreamTrack { get; set; }
///
/// Is current branch. Only used for local branches.
///
public bool IsCurrent { get; set; }
///
/// Is this branch's HEAD same with upstream?
///
public bool IsSameWithUpstream => string.IsNullOrEmpty(UpstreamTrack);
///
/// Enable filter in log histories.
///
public bool IsFiltered { get; set; }
///
/// Load branches.
///
///
public static List Load(Repository repo) {
var localPrefix = "refs/heads/";
var remotePrefix = "refs/remotes/";
var branches = new List();
var remoteBranches = new List();
repo.RunCommand("branch -l --all -v --format=\"" + PRETTY_FORMAT + "\"", line => {
var match = PARSE.Match(line);
if (!match.Success) return;
var branch = new Branch();
var refname = match.Groups[1].Value;
if (refname.EndsWith("/HEAD")) return;
if (refname.StartsWith(localPrefix, StringComparison.Ordinal)) {
branch.Name = refname.Substring(localPrefix.Length);
branch.IsLocal = true;
} else if (refname.StartsWith(remotePrefix, StringComparison.Ordinal)) {
var name = refname.Substring(remotePrefix.Length);
branch.Remote = name.Substring(0, name.IndexOf('/'));
branch.Name = name;
branch.IsLocal = false;
remoteBranches.Add(refname);
}
branch.FullName = refname;
branch.Head = match.Groups[2].Value;
branch.IsCurrent = match.Groups[3].Value == "*";
branch.Upstream = match.Groups[4].Value;
branch.UpstreamTrack = ParseTrack(match.Groups[5].Value);
branch.HeadSubject = match.Groups[6].Value;
branches.Add(branch);
});
// Fixed deleted remote branch
foreach (var b in branches) {
if (!string.IsNullOrEmpty(b.Upstream) && !remoteBranches.Contains(b.Upstream)) {
b.Upstream = null;
}
}
return branches;
}
///
/// Create new branch.
///
///
///
///
public static void Create(Repository repo, string name, string startPoint) {
var errs = repo.RunCommand($"branch {name} {startPoint}", null);
if (errs != null) App.RaiseError(errs);
}
///
/// Rename branch
///
///
///
public void Rename(Repository repo, string name) {
var errs = repo.RunCommand($"branch -M {Name} {name}", null);
if (errs != null) App.RaiseError(errs);
}
///
/// Delete branch.
///
///
public void Delete(Repository repo) {
string errs = null;
if (!IsLocal) {
errs = repo.RunCommand($"-c credential.helper=manager push {Remote} --delete {Name.Substring(Name.IndexOf('/')+1)}", null);
} else {
errs = repo.RunCommand($"branch -D {Name}", null);
}
if (errs != null) App.RaiseError(errs);
}
private static string ParseTrack(string data) {
if (string.IsNullOrEmpty(data)) return "";
string track = "";
var ahead = AHEAD.Match(data);
if (ahead.Success) {
track += ahead.Groups[1].Value + "↑ ";
}
var behind = BEHIND.Match(data);
if (behind.Success) {
track += behind.Groups[1].Value + "↓";
}
return track.Trim();
}
}
}