dotnet으로 git 커밋 구문 분석

23645 단어 gitdotnet
내 블로그https://friendlyuser.github.io/posts/tech/parsing_git_commits_with_dotnet/에서 이 게시물 보기

이 문서에서는 dotnet을 사용하여 git 커밋을 구문 분석하는 방법을 설명합니다. 이 코드는 관심 있는 사람들을 위해 이 github 저장소에서 액세스할 수 있습니다.

git log에서 모든 기사를 가져오려는 주된 이유는 릴리스 정보 때문입니다. 제 경우에는 어제 한 일을 추적하고 싶습니다.

즉, 커밋 메시지에서 JIRA 티켓을 추출할 수 있어야 합니다. 또한 커밋 메시지와 커밋 해시를 추출할 수 있기를 원합니다.

정규식을 사용하여 커밋 메시지를 구문 분석하는 것이 좋은 방법입니다. 다음 정규식을 사용하여 커밋 메시지를 구문 분석할 것입니다.

foreach (Match match in Regex.Matches(c.Message, @"([\S]+)-\d+",
                                    RegexOptions.None,
                                    TimeSpan.FromSeconds(2)))
{
    entries.Add(match.Value);
}


티켓 번호 앞의 모든 단어를 잡습니다. 예를 들어 다음 커밋 메시지가 있는 경우:

git commit -m "ABC-1234: This is a commit message"


커밋 메시지에서 ABC-1234를 추출합니다.

일반적인 구문 분석 논리의 경우 이 기사의 코드를 수정했습니다.

dotnet 6.0을 사용하면 모든 논리를 단일 파일에 넣을 수 있습니다.

이 모든 논리를 함께 결합하면 이후로 명령줄 인수를 구문 분석하여 모든 커밋을 가져온 다음 커밋 메시지를 구문 분석하여 JIRA 티켓을 얻을 수 있습니다.
git log 를 사용하면 표준 git 커밋은 헤더가 콜론, 커밋 sha sha1 해시 및 커밋 메시지로 정의됩니다.

commit 226198d2f8e61206ad9eb47b32124f77801ca026
Author: David Li <[email protected]>
Date:   Tue Aug 23 22:26:45 2022 -0700

    feat: adding media_nlp post closes #16


dotnet 6.0을 사용하고 있으므로 새로운 최상위 문을 사용하여 코드를 더 간결하게 만들 수 있습니다. 명령줄 인수를 가져오려면 CommandLine 라이브러리를 사용해야 합니다.

var cmdArgs = Environment.GetCommandLineArgs();
Parser.Default.ParseArguments<CommandLineOptions>(cmdArgs)
    .WithParsed<CommandLineOptions>(o =>{});

public class CommandLineOptions
{
    [Option('s', "since", Required = false, Default = "yesterday", HelpText = "Since Time")]
    public string Since { get; set; }
    [Option('a', "author", Required = false, Default = "David Li", HelpText = "Author to search git logs for")]
    public string Author { get; set; }
    [Option('d', "dir", Required = false, HelpText = "local path to repository to parse")]
    public string Repo { get; set; }
}

o에는 CommandLineOptions 유형의 인수가 있습니다. 그런 다음 이후 인수를 사용하여 모든 커밋을 가져올 수 있습니다. 우리는 어제부터 내 커밋을 분석하기 위해 통과할 것입니다.

명령줄 인수를 가져온 후 C#에서 git을 실행하는 다음 코드를 사용할 수 있습니다. Python에서는 subprocess라고 합니다. 그런 다음 git의 응답을 구문 분석하여 커밋 해시와 커밋 메시지를 가져와야 합니다.

    public static string RunProcess(string command)
    {
        // Start the child process.
        Process p = new Process();
        // Redirect the output stream of the child process.
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.FileName = "git";
        p.StartInfo.Arguments = command;
        p.Start();
        // Read the output stream first and then wait.
        string output = p.StandardOutput.ReadToEnd();
        p.WaitForExit();
        return output;

    }


    public static string AllLogs(string since, string author)
    {
        var args_string = string.Format("log --all --since=\"{0}\" --before=0am --author=\"{1}\"", since, author);
        var output = RunProcess(args_string);
        return output;
    }


그런 다음 모든 결과를 구문 분석하기 위해 다음 코드를 사용할 수 있습니다. 행이 헤더인지 확인하기 위해 행 길이가 0보다 큰지, for 문자에 대한 문자 및 콜론 문자가 있는지 찾습니다.

    static bool StartsWithHeader(string line)
    {
        if (line.Length > 0 && char.IsLetter(line[0]))
        {
            var seq = line.SkipWhile(ch => Char.IsLetter(ch) && ch != ':');
            return seq.FirstOrDefault() == ':';
        }
        return false;
    }


결과를 구문 분석하기 위해 "커밋 메시지"를 찾는 모든 코드 라인을 반복합니다.

 public static List<GitCommit> ParseResults(string output)
    {
        GitCommit commit = null;
        var commits = new List<GitCommit>();
        bool processingMessage = false;
        using (var strReader = new StringReader(output))
        {
            do
            {
                var line = strReader.ReadLine();
                if (line == null) {
                    continue;
                } 
                if (line.StartsWith("commit "))
                {
                    if (commit != null)
                        commits.Add(commit);
                    commit = new GitCommit();
                    commit.Sha = line.Split(' ')[1];
                }

                if (StartsWithHeader(line))
                {
                    var header = line.Split(':')[0];
                    var val = string.Join(":", line.Split(':').Skip(1)).Trim();

                    // headers
                    commit.Headers.Add(header, val);
                }

                if (string.IsNullOrEmpty(line) && commit.Message != null)
                {
                    // commit message divider
                    processingMessage = !processingMessage;
                }

                if (line.Length > 0 && processingMessage)
                {
                    // commit message.
                    commit.Message += line;
                }
            }
            while (strReader.Peek() != -1);
        }
        if (commit != null)
            commits.Add(commit);

        return commits;
    }


이 모든 논리를 결합하여 어제의 모든 커밋을 구문 분석한 다음 커밋 메시지를 구문 분석하여 JIRA 티켓을 얻을 수 있습니다.

var cmdArgs = Environment.GetCommandLineArgs();
Parser.Default.ParseArguments<CommandLineOptions>(cmdArgs)
    .WithParsed<CommandLineOptions>(o =>
    {
        string output = Utils.AllLogs(o.Since, o.Author);
        Console.WriteLine(output);

        var commits = Utils.ParseResults(output);
        Console.WriteLine(commits);
        // pull entries with #{number} and JIRA-1 project regex
        var entries = new List<String>();
        // iterate across all commmits and print out the commit message
        Console.WriteLine("Messages: ");
        foreach (var c in commits)
        {
            Console.WriteLine(c.Message);
            foreach (Match match in Regex.Matches(c.Message, @"([\S]+)-\d+",
                                               RegexOptions.None,
                                               TimeSpan.FromSeconds(2)))
            {
                // Console.WriteLine("Found '{0}' at position {1}", match.Value, match.Index);
                entries.Add(match.Value);
            }
        }
        Console.WriteLine("----------------");

        Console.WriteLine("Issues found: ");
        // print all entries
        foreach (var e in entries)
        {
            Console.WriteLine(e);
        }
    });


참조


  • https://github.com/FriendlyUser/git_log_parser
  • https://gist.github.com/Erikdegroot89/a242f0a836de3ed669dac315e1a28c04
  • 좋은 웹페이지 즐겨찾기