0

In a script (msbuild) on a windows machine, I have the SHA1 of a commit. What is the correct git command, to get the "next" (by date) commit SHA1 in the same branch?

Take a look at this example log from TortoiseGit:

Example log

In my script I currently have the 4b60a7e87762f421ddeee4ea0282a99c5db20e4a. Now I need a command to get c0fb4c86c354cfe32c6d0f1753958ab60db7e086.

Rico-E
  • 1,806
  • 2
  • 28
  • 47
  • So you want the next more recent commit, right? "By date" is not a natural choice in Git, though, because things can get rebased. Maybe you mean the commit that has that commit as a parent? – joanis Jun 14 '22 at 13:11
  • There is actually no direct way to say "the commit that has this commit as a parent". A commit stores its parent(s), but not its children. So you have to start from existing branches and "search" for the commit you want. – joanis Jun 14 '22 at 13:13
  • @Joe: No, because the answers are not giving a single commit. – Rico-E Jun 14 '22 at 13:15
  • @joanis: Yes, I want the next more recent commit in the same branch (`master`). It is okay, if there are commits that got rebased. – Rico-E Jun 14 '22 at 13:17
  • The branch name is actually the most important piece of additional information here, because the same commit can be part of any number of branches (and have different children in each one). But even with the branch it's possible to have multiple child commits (when a later merge commit reconnected two different branches). Only if you never have merge commits (basically if you use rebase-and-ff-merges) will the history always be as simple as the picture shows. – Joachim Sauer Jun 14 '22 at 13:18
  • The command `git log --format="%H %P"` will show the commit's sha1 in the left columns, and its parents' sha1's next. You could grep that. – joanis Jun 14 '22 at 13:18
  • @joanis: I am on a windows machine and don't have grep. What is an alternative on a windows machine? – Rico-E Jun 14 '22 at 13:20
  • @JoachimSauer: Yes, I understand all the edge cases, and that there might problems, but I already checked, that my script runs only against a linear history. So what could be a solution to get the next commit in the same branch? – Rico-E Jun 14 '22 at 13:21
  • Grep comes installed with Git Bash, so yes, if you have Git, you have grep... :) – joanis Jun 14 '22 at 13:23
  • But if you're running Git in `cmd`, the windows equivalent of grep is called `findstr` – joanis Jun 14 '22 at 13:23
  • `git log --format="%H %P" | findstr ".4b60a7e87762f42"` should get you what you want. – joanis Jun 14 '22 at 13:25
  • Better: `git log --format="%H %P" | findstr "/C: 4b60a7e87762f42"` – joanis Jun 14 '22 at 13:27
  • @joanis what is the difference between the two commands you suggested? – Rico-E Jun 14 '22 at 13:38
  • `/C:` tells findstr to treat what follows as a fixed string instead of a regular expression. By putting the space before the sha1, I'm forcing that string to be the beginning of a sha1, whereas the first command would give you a spurious hit in the (astronomically small) chance that that string occurs as the substring of another sha1. – joanis Jun 14 '22 at 13:43
  • @joanis: it's not quite so atronomically small, since some tools create revert commits labelled something like "revert commit 123abc...". So the change is actually very relevant. – Joachim Sauer Jun 14 '22 at 13:57
  • 1
    @JoachimSauer Yes, but I'm not searching though log messages, only through output that shows a commit's own sha1 and its parents'. But agreed, my second findstr command is the right one to use. – joanis Jun 14 '22 at 14:02

1 Answers1

0

I ended up writing a Custom MSBuild-Task which finds the predecessor. I post it in case it might come in handy for someone else.

Based on the comments of joanis I ended up looking in the git log for the next commit. So in my msbuild-script I now have a target which finds the next git commit and executes a specific target TargetToExecuteForNextGitCommit and sets the property CurrentCommitHash to the hash value of the next git commit. So this looks like:

<Target Name="DoTargetForNextGitCommit">
    <!-- 6. Nachfolger vom aktuellen Commit finden -->
    <!-- 6.a Tag des aktuellen Commits finden. Damit die Anzahl der zu durchsuchenden Commits einschränken.
             Denn ich suche ja nur nach Commits, die danach kamen. -->
    <Exec Command="git log --format=%25%25cd --date=iso-strict -n 1 $(CurrentCommitHash)"
          WorkingDirectory="$(GitRepoDir)"
          ConsoleToMSBuild="true">
        <Output TaskParameter="ConsoleOutput" PropertyName="GitCommitTimestamp"/>
    </Exec>
    <Message Text="Commit-Zeitstempel: $(GitCommitTimestamp)"/>
    <!-- 6.b Git-Log holen, in eine ItemGroup packen, den Index des Elements finden, das meinen Hash hat
             und dann den Nachfolger holen und dessen hash finden -->
    <!-- 6.b.i Git-Log holen -->
    <Exec Command="git log --date-order --format=%25%25H --after=$(GitCommitTimestamp) origin/master"
          WorkingDirectory="$(GitRepoDir)"
          ConsoleToMSBuild="true">
        <Output TaskParameter="ConsoleOutput" PropertyName="GitCommitHashesAfterCommitDay"/>
    </Exec>
    <ItemGroup>
        <GitCommitHashesAfterCommitDayEntries Include="$(GitCommitHashesAfterCommitDay)"/>
    </ItemGroup>
    <!-- 6.b.ii Nachfolger holen-->
    <GetNextCommit Items="@(GitCommitHashesAfterCommitDayEntries)" CurrentCommitHash="$(CurrentCommitHash)">
        <Output TaskParameter="NextCommitHash" PropertyName="NextCommitHashProperty"/>
    </GetNextCommit>
    <Message Text="Nächster Commit: $(NextCommitHashProperty)" />
    <!-- 7. Wenn es einen Nachfolger gibt, dann beginne mit diesem wieder bei 1. -->
    <MSBuild Projects="$(MSBuildThisFile)"
             Targets="$(TargetToExecuteForNextGitCommit)"
             Properties="CurrentCommitHash=$(NextCommitHashProperty)"
             Condition=" '$(NextCommitHashProperty)' != '0' "/>
</Target>

<UsingTask TaskName="GetNextCommit" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
    <ParameterGroup>
        <Items ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
        <CurrentCommitHash ParameterType="System.String" Required="true" />
        <NextCommitHash ParameterType="System.String" Output="true" Required="false" />
    </ParameterGroup>
    <Task>
        <Using Namespace="System.Linq"/>
        <Code Type="Fragment" Language="cs">
            <![CDATA[
                var hashes = Items.Select(i => i.ItemSpec).ToList();
                var currentCommitIndex = hashes.IndexOf(CurrentCommitHash);
                if (currentCommitIndex < 1)
                {
                    NextCommitHash = "0";
                }
                else
                {
                    var nextCommitIndex = currentCommitIndex - 1;
                    NextCommitHash = hashes[nextCommitIndex];
                }]]>
        </Code>
    </Task>
</UsingTask>
Rico-E
  • 1,806
  • 2
  • 28
  • 47