简体   繁体   English

git 日志历史简化

[英]git log history simplification

Let's say I have the following history假设我有以下历史

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

git log --ancestry-path D..M will give me git log --ancestry-path D..M 会给我

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

However, I would like just the following但是,我只想要以下内容

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

Or或者

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

Essentially, I would like to traverse down only one path, not two.本质上,我只想遍历一条路径,而不是两条。

Is this possible?这可能吗? And if so, what is the command?如果是这样,命令是什么?

Edit:编辑:

I've tried using --first-parent, but this isn't exactly it.我试过使用--first-parent,但这不完全是。 git log --first-parent G..M gives me git 日志 --first-parent G..M 给我

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

It includes F, because F is the first parent of I. Instead I'd like它包括 F,因为 F 是 I 的第一个父母。相反,我想

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

Any help would be appreciated任何帮助,将不胜感激

Solution (that worked for me):解决方案(对我有用):

As @VonC stated, there isn't a single one-liner that does this.正如@VonC 所说,没有一个单线可以做到这一点。 So I ended up using a bash script.所以我最终使用了 bash 脚本。

  1. For each commit in 'git log --ancestry-path G..M'对于“git log --ancestry-path G..M”中的每个提交
  2. Determine if $commit's parent includes the commit we were previously on确定 $commit 的父级是否包含我们之前的提交
  3. If yes, continue.如果是,请继续。 do something interesting.做一些有趣的事情。
  4. If no, skip that commit.如果不是,请跳过该提交。

For example, git log --first-commit G..M is例如,git log --first-commit G..M 是

H - F - I - J - L - M

However, F's parent is E, not H. So we omit F, giving me然而,F 的父母是 E,而不是 H。所以我们省略 F,给我

H - I - J - L - M

Yay!耶!

I don't think this is directly possible (unless you know in advance the exact list to include/exclude, which negates the purpose of walking the DAG)我不认为这是直接可能的(除非您事先知道要包含/排除的确切列表,这否定了走 DAG 的目的)

Actually, the OP Ken Hirakawa managed to get the expected linear history by:实际上, OP Ken Hirakawa通过以下方式获得了预期的线性历史:

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

And for each commit, making sure it is a direct child of the previous commit.对于每个提交,确保它是前一个提交的直接子级。

Here is the script writtten by Ken Hirakawa .这是平川健写的剧本


Here is my script to create the DAG mentioned in the History Simplification section of the git log man page, for --ancestry-path :这是我的脚本,用于创建 git 日志手册页的历史简化部分中提到的 DAG,用于--ancestry-path

You will find at the end the bash script I used to create a similar history (call it with the name of the root dir, and your username).你会在最后找到我用来创建类似历史记录的 bash 脚本(使用根目录的名称和您的用户名调用它)。

I define:我定义:

$ 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"

I get:我得到:

$ 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>

From there, I cannot exclude one of the parents of commit I.从那里,我不能排除提交 I 的父母之一。

The ancestry-path does return:祖先路径确实返回:

$ 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>

which is consistent with the log man page:这与日志手册页一致:

A regular D..M computes the set of commits that are ancestors of M , but excludes the ones that are ancestors of D .常规D..M计算作为M祖先的提交集,但不包括作为D祖先的提交。
This is useful to see what happened to the history leading to M since D , in the sense that "what does M have that did not exist in D ".这有助于了解自D以来导致M的历史发生了什么,从某种意义上说,“ M有什么在D中不存在”。
The result in this example would be all the commits, except A and B (and D itself, of course).此示例中的结果将是所有提交,除了AB (当然还有D本身)。

When we want to find out what commits in M are contaminated with the bug introduced by D and need fixing , however, we might want to view only the subset of D..M that are actually descendants of D , ie excluding C and K .但是,当我们想找出M中的哪些提交被D引入的错误污染并需要修复时,我们可能只想查看D..M实际上是D后代的子集,即不包括CK
This is exactly what the --ancestry-path option does.这正是--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"

I have to admit I didn't understand your solution - it didn't work for my example - but if I understood your use-case correctly (given a pair of commits, you want an arbitrary linear path between them, with no splits), I have the same problem, and the following solution seems to work:我不得不承认我不理解您的解决方案-它不适用于我的示例-但是如果我正确理解了您的用例(给定一对提交,您需要它们之间的任意线性路径,没有拆分) ,我有同样的问题,以下解决方案似乎有效:

  • Run the log with --ancestry-path, and making sure you take note of the children of each commit使用 --ancestry-path 运行日志,并确保记下每个提交的子项
  • Iterate through the results, keeping track of the "last child accepted", and updating it every time a commit references an accepted child (or there is no accepted child yet - initial case)遍历结果,跟踪“最后一个接受的孩子”,并在每次提交引用一个接受的孩子时更新它(或者还没有接受的孩子——初始情况)
  • Actually print the resulting "accepted" entries in some useful way实际上以某种有用的方式打印生成的“已接受”条目

A resulting script looks like:生成的脚本如下所示:

#!/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

It can be called like single-ancestry-path.sh RANGE_EXPRESSION DECORATION_ARGS , supporting generally the same decoration arguments as git log (it is in fact git show , being called once per commit), so taking the famous lg2 example from https://stackoverflow.com/a/9074343/74296 , the call might look like this: eg: It can be called like single-ancestry-path.sh RANGE_EXPRESSION DECORATION_ARGS , supporting generally the same decoration arguments as git log (it is in fact git show , being called once per commit), so taking the famous lg2 example from https:// stackoverflow.com/a/9074343/74296 ,调用可能如下所示:例如:

single-ancestry-path.sh master..MyBranch --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'

It's been 9 years, so I would have hoped there would be an easier answer, but I can't find one.已经 9 年了,所以我希望有一个更简单的答案,但我找不到。

I too dislike the problems that result from merging and have dispensed with having it in my mainstream history.我也不喜欢合并带来的问题,并且已经放弃了在我的主流历史中出现的问题。 Whenever there is a large merge onto a main branch I will recommit it with identical contents but as a single commit.每当有一个大的合并到一个主分支上时,我都会用相同的内容重新提交它,但作为一个单一的提交。

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

Here, G', I' and L' would be points where I have re-commited merge results.在这里,G'、I' 和 L' 将是我重新提交合并结果的点。 The branch descriptions simply describe a scenario where I can visualize the problem tree occurring.分支描述只是描述了我可以可视化发生的问题树的场景。 So the contents of G and G' (similarly I and I') would be the same, the team leader having merged in the work-to-date of the developer.因此,G 和 G'(类似于 I 和 I')的内容将是相同的,团队负责人已合并到开发人员的最新工作中。 And L' the same as L, the feature integrated onto the mainstream.和 L' 一样,L 的特点融入了主流。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM