包含带有“git log”的子模块提交消息

时间:2012-05-24 16:43:57

标签: git

假设我的存储库中有两个版本...每个版本都标记如下:

  • Tag1中
  • 与Tag2

现在假设提交更新了一个子模块引用,指向Tag1和Tag2之间的新子模块提交。我运行以下命令,并得到这个:

# show commits between these two tags
git log Tag1..Tag2


commit be3d0357b93322f472e8f03285cb3e1e0592eabd
Author: James Johnston <snip>
Date:   Wed Jan 25 19:42:56 2012 +0000

    Updated submodule references.

在这种情况下,唯一的变化是子模块的更新。如何让子模块提交与父存储库提交交错?

具体来说,在此示例中,假设父存储库指向子模块中的SubTag5标记。子模块后面的两个提交是SubTag6标记。显示的提交更新了子模块指针,指向SubTag6而不是SubTag5。我想要做的是git log,除了它已经打印的提交之外,打印两个子模块提交以及将子模块从SubTag5带到SubTag6。

4 个答案:

答案 0 :(得分:13)

这是一个简单的bash命令,它创建一个ASCII提交图(类似于gitk),当子模块在超级项目中发生变化时,它会交错相关的子模块提交。它打印出每次提交的完整补丁,然后使用grep过滤掉补丁内容,只留下汇总行和子模块的更改。

git log --graph --oneline -U0 --submodule Tag1..Tag2 | grep -E '^[*| /\\]+([0-9a-f]{7} |Submodule |> |$)'

它产生类似于此的输出:

* 854407e Update submodule
| Submodule SUB 8ebf7c8..521fc49:
|   > Commit C
* 99df57c Commit B
* 79e4075 Commit A

答案 1 :(得分:10)

您可以显示子模块更改,但仅限于使用git log -p时。以下命令显示每个提交和子模块更改的完整差异。

git log -p --submodule=log

子模块提交消息将如下所示:

Submodule <submodule-name> <starting-commit>..<ending-commit>:
> Commit message 1
> Commit message 2
...
> Commit message n

答案 2 :(得分:1)

如果您正在使用bash,则可以使用以下脚本显示嵌入到超级项目日志中的子模块提交日志。

export interface IAppConfig {
    env: {
        name: string;
    }
    logging: {
        console: boolean;
    };
    apiServer: {
        url:string
        signalRUrl: string
        whiteListDomains: string[]
    };
}

该脚本与上面列出的PowerShell脚本相似,但可以解决一些问题并以更密集的格式输出。它可以处理新的子模块已删除的子模块

要正确显示不再属于超级项目的子模块(已删除的子模块)的日志信息,至少子模块根目录(可以为空)必须保留在存储库中。否则,Git(在Windows上用2.19.0版进行测试)将在log命令中失败(例如在#!/bin/bash # regular expressions related to git log output # when using options -U0 and --submodule=log kREGEXP_ADD_SUBMODLE='0+\.\.\.[0-9a-f]+' kREGEXP_REM_SUBMODLE='[0-9a-f]+\.\.\.0+' # -------------------------------------------------------------------- # function submodule_log # -------------------------------------------------------------------- # # print a log of submodule changes for a range of commits # # arguments : see start of function body for details # function submodule_log { sm_present=$1; # presence 0: no, 1: yes sm_status=$2 # status 0: as is, 1: added submodule, 2: removed submodule sm_name=$3 # name sm_id_base=$4 # base commit id added changes sm_id_now=$5 # final commit id added changes cur_dir=`pwd` # commits cannot be accessed if sbumodule working tree was removed, # show submodule commits in details only if directory exists # # note: As of git 1.9, in .git/modules/<submodule-name> # still the entire gitdir is present, just git won't successfully # run something like 'git --git-dir .git/modules/<submodule-name> log f374fbf^!' # from the superproject root dir. It fails as it want's to change directory to # to the submodule working tree at '../../../<submodule-name>' to get the log. # If one just creates it as an empty directory the command succeeds, but # we cannot force the user to leave an empty directory. So just a hint # is output to suggest creation of directory to get full log. #echo " $submod_entry" if [ -e $sm_name ] then cd $sm_name # if submodule not present in current version of superproject # can retrieve git log info only by using option '--git-dir' # -> use always option --git-dir git_dir_opt="--git-dir $cur_dir/.git/modules/$sm_name" git_cmd_base="git $git_dir_opt log --format=\" %Cred%h %s%Creset\"" if [ $sm_status -eq 0 ] then # modified module: output info on added commit(s) eval "$git_cmd_base ${sm_id_base}..${sm_id_now}" fi if [ $sm_status -eq 1 ] then # new module: output only info on base commit eval "$git_cmd_base ${sm_id_now}^!" fi if [ $sm_status -eq 2 ] then # removed module: output only info on last commit eval "$git_cmd_base ${sm_id_base}^!" fi cd $cur_dir else echo " Skip info on submodule $sm_name (not present in current working tree)" echo " For full log, please add empty directory $sm_name for full log." fi } # -------------------------------------------------------------------- # main script # -------------------------------------------------------------------- # Get the log of the parent repository (only SHA1 and parent's SHA1), # use files as amount of data might be huge in older repos # get commit ids as array readarray -t log_commitids < <(git log --format="%H") # get commit ids of parent commits readarray -t log_parents < <(git log --format="%P") for ((c_idx=0; $c_idx<${#log_commitids[@]}; c_idx=$c_idx+1)) do # Can only be one commit id, but remove trailing newline and linefeed commit="${log_commitids[$c_idx]//[$'\r\n']}" # Can be more than one parent if it's a merge # remove trailing newline and linefeed parents="${log_parents[$c_idx]//[$'\r\n']}" parents_a=($(echo $parents)) num_parents=${#parents_a[@]} # check if merge commit, prefix next commit with M as they are merge merge_prefix="" if [ $num_parents -ge 2 ] then merge_prefix="M$num_parents" fi # Print the two-line summary for this commit git log --format="%Cgreen%h (%cI %cN)%Creset%n %Cgreen$merge_prefix%Creset %s" $commit^! #echo "found $num_parents parents" if [ "$parents" = "" ] then unset parents else for parent in $parents do # Find entires like # "Submodule libA 0000000...f374fbf (new submodule)" or # "Submodule libA e51c470...0000000 (submodule deleted)" or # "Submodule libA f374fbf..af648b2e:" # in supermodules history in order to determine submodule's # name and commit range describing the changes that # were added to the supermodule. Two regular expressions # kREGEXP_ADD_SUBMODLE and kREGEXP_REM_SUBMODLE are used # to find added and removed submodules respectively. readarray -t submod < <(git log -U0 --submodule=log ${parent}..${commit} \ | grep -U -P '^Submodule \S+ [0-9a-f]+') for ((s_idx=0; $s_idx<${#submod[@]}; s_idx=$s_idx+1)) do # remove trailing newline and linefeed submod_entry="${submod[$s_idx]//[$'\r\n']}" #echo mainly unfiltered as to show submod name and its #commit range stored in repo's log echo " $submod_entry" # remove preceding info 'Submodule ' as we already know that :-) submod_entry="${submod_entry/Submodule }" # if viewing repository version for which submodules do not exist # they are reported with correct commit ids but trailing text # is different, first assume it is present then check submod_entry submod_present=1 if [[ "$submod_entry" =~ "commits not present" ]] then submod_present=0 # remove trailing info about deleted submodule, if any submod_entry="${submod_entry/'(commits not present)'}" fi # find with submodule got added/modified/removed by this superproject commit # assume 'modified' submodule, then check if commit range indicates # special cases like added/removed submodule sub_status=0 if [[ "$submod_entry" =~ $kREGEXP_ADD_SUBMODLE ]] then sub_status=1 # remove trailing info about new submodule, if any submod_entry="${submod_entry/'(new submodule)'}" fi if [[ "$submod_entry" =~ $kREGEXP_REM_SUBMODLE ]] then sub_status=2 # remove trailing info about deleted submodule, if any submod_entry="${submod_entry/'(submodule deleted)'}" fi # create log output for submod_entry # - pass contents in submod_entry as separate arguments # by expanding variable and using eval to execute resulting code #replace dots by spaces as to split apart source and destination commit id submod_entry="${submod_entry//./ }" #remove colon behind last commit id, if any submod_entry="${submod_entry//:/}" eval "submodule_log $submod_present $sub_status $submod_entry" done done fi done 中),因为它总是将工作目录更改为子模块根目录(无论出于何种原因)。

答案 3 :(得分:0)

如果您正在使用Windows,则可以使用此PowerShell脚本:

function Parse-SubmoduleDiff($rawDiffLines) {
    $prefix = "Subproject commit "
    $oldCommitLine = $($rawDiffLines | where { $_.StartsWith("-" + $prefix) } | select -First 1)
    $newCommitLine = $($rawDiffLines | where { $_.StartsWith("+" + $prefix) } | select -First 1)

    if ($newCommitLine -eq $null) {
        return $null
    }

    $oldCommit = $null
    if ($oldCommitLine -ne $null) {
        $oldCommit = $oldCommitLine.Substring($prefix.Length + 1)
    }
    $newCommit = $newCommitLine.Substring($prefix.Length + 1)
    return @{ OldCommit = $oldCommit; NewCommit = $newCommit }
}

# Get the paths of all submodules
$submodulePaths = $(git submodule foreach --quiet 'echo $path')
if ($submodulePaths -eq $null) {
    $submodulePaths = @()
}

# Get the log of the parent repository (only SHA1)
$log = $(git log --format="%H %P" $args)
foreach ($line in $log) {

    $parts = $line.Split()
    $commit = $parts[0]
    $parents = $parts[1..$parts.Length]

    # Print the summary for this commit
    git show --format=medium --no-patch $commit
    echo ""

    # Can be more than one parent if it's a merge
    foreach ($parent in $parents) {
        # List the paths that changed in this commit
        $changes = $(git diff --name-only $parent $commit)

        if ([System.String]::IsNullOrWhiteSpace($parent)) {
            continue;
        }

        foreach ($path in $changes) {

            if ($submodulePaths.Contains($path)) {
                # if it's a submodule, the diff should look like this:
                # -Subproject commit 1486adc5c0c37ad3fa2f2e373e125f4000e4235f
                # +Subproject commit a208e767afd0a51c961654d3693893bbb4605902
                # from that we can extract the old and new submodule reference

                $subDiff = $(git diff $parent $commit -- $path)
                $parsed = Parse-SubmoduleDiff($subDiff)
                if ($parsed -eq $null) {
                    continue;
                }

                # Now get the log between the old and new submodule commit
                $oldCommit = $parsed.OldCommit
                $newCommit = $parsed.NewCommit
                echo "Submodule '$path'"
                if ($oldCommit -ne $null) {
                    $range = $($oldCommit + ".." + $newCommit)
                } else {
                    $range = $newCommit
                }

                git --git-dir $path/.git log $range | foreach { "  |  " + $_ }
                echo ""
            }
        }
    }
}

显然它可以被翻译成bash以便在Linux上使用。一般原则是:

for each commit in the parent repo
    print the commit summary
    for each submodule that has changed in this commit
        get the old and new commit hashes of the submodule
        print the log of the submodule between those commits
    end for
end for