Created
April 27, 2018 02:31
-
-
Save 303248153/0e3945d4ce50d6162a717d80418c232f to your computer and use it in GitHub Desktop.
Find function author and last changed date from git blame
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using System.Reflection; | |
using System.Runtime.CompilerServices; | |
namespace ConsoleApp1 | |
{ | |
public class Program | |
{ | |
[ProvideSourceLocation] | |
public static void A() | |
{ | |
int a = 0; | |
a += 1; | |
} | |
public static void Main(string[] args) | |
{ | |
var methodInfo = typeof(Program).GetMethod("A"); | |
var sourceLocation = methodInfo.GetCustomAttributes().OfType<ProvideSourceLocation>().First(); | |
var processInfo = new ProcessStartInfo(); | |
processInfo.WorkingDirectory = Path.GetDirectoryName(sourceLocation.File); | |
processInfo.FileName = "git.exe"; | |
processInfo.Arguments = $"blame \"{Path.GetFileName(sourceLocation.File)}\""; | |
processInfo.UseShellExecute = false; | |
processInfo.LoadUserProfile = false; | |
processInfo.RedirectStandardInput = true; | |
processInfo.RedirectStandardOutput = true; | |
processInfo.RedirectStandardError = true; | |
var process = Process.Start(processInfo); | |
process.StandardInput.Flush(); | |
process.StandardInput.Close(); | |
var blameResult = ""; | |
while (!process.StandardOutput.EndOfStream) | |
{ | |
var chunk = process.StandardOutput.ReadToEnd(); | |
blameResult += chunk; | |
} | |
process.WaitForExit(); | |
var sourceInfos = blameResult | |
.Split('\n') | |
.Select(x => x.Trim()) | |
.Where(x => x != "") | |
.Select(x => new SourceInfo(x)) | |
.ToList(); | |
var sourceIndex = sourceInfos.FindIndex(x => x.LineNumber == sourceLocation.Line); | |
var functionBegin = sourceIndex; | |
var functionEnd = 0; | |
var braceLevel = 0; | |
while (true) | |
{ | |
++sourceIndex; | |
var sourceLine = sourceInfos[sourceIndex].SourceLine.Trim(); | |
if (sourceLine == "{") | |
{ | |
++braceLevel; | |
} | |
else if (sourceLine == "}") | |
{ | |
--braceLevel; | |
if (braceLevel == 0) | |
{ | |
functionEnd = sourceIndex; | |
break; | |
} | |
} | |
} | |
var functionInfos = sourceInfos.Skip(functionBegin).Take(functionEnd - functionBegin + 1).ToList(); | |
foreach (var functionInfo in functionInfos) | |
{ | |
Console.WriteLine(functionInfo); | |
} | |
Console.WriteLine(); | |
Console.WriteLine("last change:"); | |
var lastChange = functionInfos | |
.Where(x => x.Author != "Not Committed Yet") | |
.OrderByDescending(x => x.Date) | |
.FirstOrDefault(); | |
Console.WriteLine(lastChange?.Date); | |
Console.WriteLine(lastChange?.Author); | |
} | |
} | |
public class SourceInfo | |
{ | |
public string Author { get; set; } | |
public int LineNumber { get; set; } | |
public DateTime Date { get; set; } | |
public string SourceLine { get; set; } | |
public SourceInfo(string line) | |
{ | |
var braceBegin = line.IndexOf('('); | |
var braceEnd = line.IndexOf(')'); | |
var braceText = line.Substring(braceBegin + 1, braceEnd - braceBegin - 1); | |
var columns = braceText.Split(' ', StringSplitOptions.RemoveEmptyEntries); | |
Author = string.Join(" ", columns.Take(columns.Length - 4)); | |
LineNumber = int.Parse(columns.Last()); | |
Date = DateTime.Parse(string.Join(" ", columns.Skip(columns.Length - 4).Take(3))); | |
SourceLine = line.Substring(braceEnd + 1); | |
} | |
public override string ToString() | |
{ | |
return $"{Author} {LineNumber} {Date}: {SourceLine}"; | |
} | |
} | |
/// <summary> | |
/// https://stackoverflow.com/questions/126094/how-to-get-the-source-file-name-and-the-line-number-of-a-type-member | |
/// </summary> | |
public class ProvideSourceLocation : Attribute | |
{ | |
public readonly string File; | |
public readonly string Member; | |
public readonly int Line; | |
public ProvideSourceLocation( | |
[CallerFilePath] string file = "", | |
[CallerMemberName] string member = "", | |
[CallerLineNumber] int line = 0) | |
{ | |
File = file; | |
Member = member; | |
Line = line; | |
} | |
public override string ToString() { return File + "(" + Line + "):" + Member; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
output: