假设我的存储库中有两个版本...每个版本都标记如下:
现在假设提交更新了一个子模块引用,指向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。
答案 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