git日志历史简化

时间:2011-07-18 22:19:10

标签: git history logging

假设我有以下历史

        D---E-------F
       /     \       \
      B---C---G---H---I---J
     /                     \
    A-------K---------------L--M

git log --ancestry-path D..M会给我

            E-------F
             \       \
              G---H---I---J
                           \
                            L--M

但是,我想要以下

            E
             \       
              G---H---I---J
                           \
                            L--M

或者

            E-------F
                     \
                      I---J
                           \
                            L--M

基本上,我想只走一条路,而不是两条路。

这可能吗?如果是这样,命令是什么?

修改

我尝试过使用--first-parent,但事实并非如此。 git log - 第一父母G..M给了我

                    F
                     \
                  H---I---J
                           \
                            L--M

它包括F,因为F是I的第一个父母。相反,我想

                  H---I---J
                           \
                            L--M

任何帮助将不胜感激

解决方案(对我有用):

正如@VonC所说,没有一个单行代表这样做。所以我最终使用了一个bash脚本。

  1. 对于'git log --ancestry-path G..M'
  2. 中的每次提交
  3. 确定$ commit的父级是否包含我们之前提交的提交
  4. 如果是,请继续。做一些有趣的事。
  5. 如果不是,请跳过该提交。
  6. 例如,git log --first-commit G..M是

    H - F - I - J - L - M
    

    然而,F的父母是E,而不是H.所以我们省略F,给我

    H - I - J - L - M
    

    耶!

3 个答案:

答案 0 :(得分:5)

我认为这不是直接可行的(除非事先知道要包含/排除的确切列表,这否定了行走DAG的目的)

实际上,OP Ken Hirakawa设法通过以下方式获得预期的线性历史记录:

git log --pretty=format:"%h%n" --ancestry-path --reverse $prev_commit..$end_commit

对于每次提交,确保它是前一次提交的直接子项。

这是script writtten by Ken Hirakawa


这是我的脚本,用于创建git日志手册页的History Simplification部分中提到的DAG,用于--ancestry-path

最后我会找到用于创建类似历史记录的bash脚本(使用根目录的名称和您的用户名调用它)。

我定义:

$ git config --global alias.lgg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"

我明白了:

$ git lgg
* d7c4459 - (HEAD, M, fromA) M <VonC>
*   82b011d - (L) Merge commit 'J' into fromA <VonC>
|\
| * 190265b - (J, master) J <VonC>
| *   ef8e325 - (I) Merge commit 'F' <VonC>
| |\
| | * 4b6d976 - (F, fromB) F <VonC>
| * | 45a5d4d - (H) H <VonC>
| * |   834b239 - (G) Merge commit 'E' <VonC>
| |\ \
| | |/
| | * f8e9272 - (E) E <VonC>
| | * 96b5538 - (D) D <VonC>
| * | 49eff7f - (C) C <VonC>
| |/
| * 02c3ef4 - (B) B <VonC>
* | c0d9e1e - (K) K <VonC>
|/
* 6530d79 - (A) A <VonC>

从那里,我不能排除提交I的父母之一。

祖先路径确实返回:

$ git lgg --ancestry-path D..M
* d7c4459 - (HEAD, M, fromA) M <VonC>
* 82b011d - (L) Merge commit 'J' into fromA <VonC>
* 190265b - (J, master) J <VonC>
*   ef8e325 - (I) Merge commit 'F' <VonC>
|\
| * 4b6d976 - (F, fromB) F <VonC>
* | 45a5d4d - (H) H <VonC>
* | 834b239 - (G) Merge commit 'E' <VonC>
|/
* f8e9272 - (E) E <VonC>

与日志手册页一致:

  

常规D..M计算作为M祖先的提交集,但不包括D祖先的提交。
  这有助于查看自M以来导致D的历史记录发生的情况,其中“M中存在D所不具备的内容”。<登记/>   此示例中的结果将是所有提交,但AB(当然还有D本身除外)。

     

但是,如果我们想找出M中的提交受D引入的错误并需要修复的内容,那么我们可能只想查看子集D..M的{​​{1}}实际上是D的后代,即排除CK
  这正是--ancestry-path选项的作用。


#!/bin/bash

function makeCommit() {
  local letter=$1
  if [[ `git tag -l $letter` == "" ]] ; then
    echo $letter > $root/$letter
    git add .
    git commit -m "${letter}"
    git tag -m "${letter}" $letter
  else
    echo "commit $letter already there"
  fi
}

function makeMerge() {
  local letter=$1
  local from=$2
  if [[ `git tag -l $letter` == "" ]] ; then
    git merge $from
    git tag -m "${letter}" $letter
  else
    echo "merge $letter already done"
  fi
}

function makeBranch() {
  local branch=$1
  local from=$2
  if [[ "$(git branch|grep $1)" == "" ]] ; then
    git checkout -b $branch $from
  else
    echo "branch $branch already created"
    git checkout $branch
  fi
}

root=$1
user=$2
if [[ ! -e $root/.git ]] ; then
  git init $root
fi
export GIT_WORK_TREE="./$root"
export GIT_DIR="./$root/.git"
git config --local user.name $2

makeCommit "A"
makeCommit "B"
makeCommit "C"
makeBranch "fromB" "B"
makeCommit "D"
makeCommit "E"
makeCommit "F"
git checkout master
makeMerge "G" "E"
makeCommit "H"
makeMerge "I" "F"
makeCommit "J"
makeBranch "fromA" "A"
makeCommit "K"
makeMerge "L" "J"
makeCommit "M"

答案 1 :(得分:1)

我必须承认我不理解您的解决方案-该示例不适用于我的示例-但是如果我正确理解了您的用例(考虑到两次提交,您想要一个它们之间的任意线性路径,没有分裂),我遇到了同样的问题,以下解决方案似乎可行:

  • 使用--ancestry-path运行日志,并确保记下每次提交的子项
  • 遍历结果,跟踪“最后一个被接受的孩子”,并在每次提交引用一个被接受的孩子(或尚无被接受的孩子-初始情况)时进行更新
  • 实际上以某种有用的方式打印出结果“已接受”的条目

生成的脚本如下:

#!/bin/bash
output_set=""; child_to_match=""; # init
while read -r; do
  if { [ -n "$REPLY" ]; } && { [[ "${REPLY:41}" =~ "$child_to_match" ]] || [ -z "$child_to_match" ]; }; then
    child_to_match=${REPLY:0:40}
    output_set="$output_set $child_to_match"
  fi
done <<<  "$(git rev-list --ancestry-path --children $1)"
if [[ -n $output_set ]]; then
  git show -s $output_set "${@:2}"
fi

它可以像single-ancestry-path.sh RANGE_EXPRESSION DECORATION_ARGS那样被调用,通常支持与git log相同的修饰参数(实际上是git show,每次提交被调用一次),因此采用著名的{{来自https://stackoverflow.com/a/9074343/74296的1}}示例,调用可能如下所示:例如:

lg2

已经9年了,所以我希望会有一个更简单的答案,但我找不到。

答案 2 :(得分:0)

我也非常讨厌合并带来的问题,并已将其排除在我的主流历史之外。每当在主分支上进行大型合并时,我都会使用相同的内容(作为一次提交)重新提交它。

    D---E--------F                           Co-Developer
   /               
  B---C---G'---H---I'--J                     Team Leader
 /                       
A-------K----------------L'--M               Main Stream

在这里,G',I'和L'将成为我重新提交合并结果的点。 分支描述只是描述了一个场景,在该场景中我可以可视化出现的问题树。因此,G和G'(类似I和I')的内容将是相同的,团队负责人在开发人员的最新工作中已经合并。并且L'与L相同,该功能已集成到主流中。

I totally understand that avoiding a problem is not the same as solving it, and sympathize with those facing the problem now.