如何理解git log --graph

34

我对Git日志图表的输出结果感到相当困惑。

我确实理解每个*代表一个提交,无论是分叉、共同或合并提交。我也知道管道符代表分支。

让我们看一个简单的图形日志:

enter image description here

首先,最左边的红色管道(红线)代表哪个分支?我不认为它是当前所在的分支,因为切换到其他分支后,图形看起来一样。此外,它也不代表主分支。

其次,如果最左边的管道符代表单独的一个分支,为什么在提交“0e5b5”后会改变颜色?

我搜索了有关如何阅读Git日志图表的教程,可惜没有找到。如果有关于此主题的优秀教程,请随时分享。


2
可能是 如何阅读git log图表 的重复问题。 - Andy
3
添加 --decorate 选项以使用分支名称给每个分支头加上标签。 - chepner
边缘图在 Git 2.25/2.26(2020 年第一季度)已经发生了变化。请查看下面的答案 - VonC
3个回答

7
在 Git 2.25(2020年第一季度), "git log --graph" 的实现得到了重构,其输出也得到了简化。这反过来可以修复某些情况下的颜色。
以下说明了如何使用git log --graph管理颜色和边缘。

请查看提交 d784d97(2019年11月12日)由Denton Liu (Denton-L)提交的内容。
请查看提交 bbb13e8, 提交 92beecc, 提交 479db18, 提交 0195285, 提交 d62893e, 提交 0f0f389, 提交 458152c, 提交 ee7abb5, 提交 46ba2ab, 提交 a551fd5, 提交 9157a2a, 提交 210179a, 提交 fbccf25(2019年10月15日)由James Coglan (jcoglan)提交的内容。
(由Junio C Hamano -- gitster --合并于提交 0be5caf,2019年12月1日)

graph: fix coloring of octopus dashes

Signed-off-by: James Coglan

In 04005834ed ("log: fix coloring of certain octopus merge shapes", 2018-09-01, Git v2.20.0-rc0 -- merge listed in batch #6), there is a fix for the coloring of dashes following an octopus merge.
It makes a distinction between the case where all parents introduce a new column, versus the case where the first parent collapses into an existing column:

| *-.           | *-.
| |\ \          | |\ \
| | | |         |/ / /

The latter case means that the columns for the merge parents begin one place to the left in the new_columns array compared to the former case.

However, the implementation only works if the commit's parents are kept in order as they map onto the visual columns, as we get the colors by iterating over new_columns as we print the dashes.
In general, the commit's parents can arbitrarily merge with existing columns, and change their ordering in the process.

For example, in the following diagram, the number of each column indicates which commit parent appears in each column.

| | *---.
| | |\ \ \
| | |/ / /
| |/| | /
| |_|_|/
|/| | |
3 1 0 2

If the columns are colored (red, green, yellow, blue), then the dashes will currently be colored yellow and blue, whereas they should be blue and red.

To fix this, we need to look up each column in the mapping array, which before the GRAPH_COLLAPSING state indicates which logical column is displayed in each visual column.
This implementation is simpler as it doesn't have any edge cases, and it also handles how left-skewed first parents are now displayed:

| *-.
|/|\ \
| | | |
0 1 2 3

The color of the first dashes is always the color found in mapping two columns to the right of the commit symbol. Because commits are displayed after all edges have been collapsed together and the visual columns match the logical ones, we can find the visual offset of the commit symbol using commit_index.


关于边缘(仍然是 Git 2.25,2020年第一季度):

graph: smooth appearance of collapsing edges on commit lines

Signed-off-by: James Coglan

When a graph contains edges that are in the process of collapsing to the left, but those edges cross a commit line, the effect is that the edges have a jagged appearance:

*
|\
| *
|  \
*-. \
|\ \ \
| | * |
| * | |
| |/ /
* | |
|/ /
* |
|/
*

We already takes steps to smooth edges like this when they're expanding; when an edge appears to the right of a merge commit marker on a GRAPH_COMMIT line immediately following a GRAPH_POST_MERGE line, we render it as a \:

* \
|\ \
| * \
| |\ \

We can make a similar improvement to collapsing edges, making them easier to follow and giving the overall graph a feeling of increased symmetry:

*
|\
| *
|  \
*-. \
|\ \ \
| | * |
| * | |
| |/ /
* / /
|/ /
* /
|/
*

To do this, we introduce a new special case for edges on GRAPH_COMMIT lines that immediately follow a GRAPH_COLLAPSING line.
By retaining a copy of the mapping array used to render the GRAPH_COLLAPSING line in the old_mapping array, we can determine that an edge is collapsing through the GRAPH_COMMIT line and should be smoothed.


更一般地说:

graph: commit and post-merge lines for left-skewed merges

Signed-off-by: James Coglan

Following the introduction of "left-skewed" merges, which are merges whose first parent fuses with another edge to its left, we have some more edge cases to deal with in the display of commit and post-merge lines.

The current graph code handles the following cases for edges appearing to the right of the commit (*) on commit lines.

A 2-way merge is usually followed by vertical lines:

| | |
| * |
| |\ \

An octopus merge (more than two parents) is always followed by edges sloping to the right:

| |  \          | |    \
| *-. \         | *---. \
| |\ \ \        | |\ \ \ \

A 2-way merge is followed by a right-sloping edge if the commit line immediately follows a post-merge line for a commit that appears in the same column as the current commit, or any column to the left of that:

| *             | * |
| |\            | |\ \
| * \           | | * \
| |\ \          | | |\ \

This commit introduces the following new cases for commit lines. If a 2-way merge skews to the left, then the edges to its right are always vertical lines, even if the commit follows a post-merge line:

| | |           | |\
| * |           | * |
|/| |           |/| |

A commit with 3 parents that skews left is followed by vertical edges:

| | |
| * |
|/|\ \

If a 3-way left-skewed merge commit appears immediately after a post-merge line, then it may be followed the right-sloping edges, just like a 2-way merge that is not skewed.

| |\
| * \
|/|\ \

Octopus merges with 4 or more parents that skew to the left will always be followed by right-sloping edges, because the existing columns need to expand around the merge.

| |  \
| *-. \
|/|\ \ \

On post-merge lines, usually all edges following the current commit slope to the right:

| * | |
| |\ \ \

However, if the commit is a left-skewed 2-way merge, the edges to its right remain vertical.
We also need to display a space after the vertical line descending from the commit marker, whereas this line would normally be followed by a backslash.

| * | |
|/| | |

If a left-skewed merge has more than 2 parents, then the edges to its right are still sloped as they bend around the edges introduced by the merge.

| * | |
|/|\ \ \

To handle these new cases, we need to know not just how many parents each commit has, but how many new columns it adds to the display; this quantity is recorded in the edges_added field for the current commit, and prev_edges_added field for the previous commit.

Here, "column" refers to visual columns, not the logical columns of the columns array.
This is because even if all the commit's parents end up fusing with existing edges, they initially introduce distinct edges in the commit and post-merge lines before those edges collapse.

For example, a 3-way merge whose 2nd and 3rd parents fuse with existing edges still introduces 2 visual columns that affect the display of edges to their right.

| | |  \
| | *-. \
| | |\ \ \
| |_|/ / /
|/| | / /
| | |/ /
| |/| |
| | | |

This merge does not introduce any logical columns; there are 4 edges before and after this commit once all edges have collapsed. But it does initially introduce 2 new edges that need to be accommodated by the edges to their right.


经典输出已简化:

graph: example of graph output that can be simplified

Signed-off-by: James Coglan

The commits following this one introduce a series of improvements to the layout of graphs, tidying up a few edge cases, namely:

  • merge whose first parent fuses with an existing column to the left
  • merge whose last parent fuses with its immediate neighbor on the right
  • edges that collapse to the left above and below a commit line

This test case exemplifies these cases and provides a motivating example of the kind of history I'm aiming to clear up.

The first parent of merge E is the same as the parent of H, so those edges fuse together.

* H
|
| *-.   E
| |\ \
|/ / /
|
* B

We can "skew" the display of this merge so that it doesn't introduce additional columns that immediately collapse:

* H
|
| *   E
|/|\
|
* B

The last parent of E is D, the same as the parent of F which is the edge to the right of the merge.

* F
|
 \
. \   E
 \ \
 / /
| /
|/
* D

The two edges leading to D could be fused sooner: rather than expanding the F edge around the merge and then letting the edges collapse, the F edge could fuse with the E edge in the post-merge line:

* F
|
 \
. | E
 \|
 /
|
* D

If this is combined with the "skew" effect above, we get a much cleaner graph display for these edges:

* F
|
| E
|
|
* D

Finally, the edge leading from C to A appears jagged as it passes through the commit line for B:

| * | C
| |/
* | B
|/
* A

This can be smoothed out so that such edges are easier to read:

| * | C
| |/
* / B
|/
* A

自从日志图形渲染代码的最新更新,绘制某些合并操作开始触发断言条件不再成立,这已在Git 2.25(2020年第一季度)中得到了纠正。

查看 提交 a1087c9提交 0d251c3(2020年1月7日)由Derrick Stolee (derrickstolee)完成。
(由Junio C Hamano -- gitster --合并于提交1f5f3ff,2020年1月8日)

graph: drop assert() for merge with two collapsing parents

Helped-by: Jeff King
Reported-by: Bradley Smith
Signed-off-by: Derrick Stolee

When "git log --graph" shows a merge commit that has two collapsing lines, like:

| | | | *
| |_|_|/|
|/| | |/
| | |/|
| |/| |
| * | |
* | | |

we trigger an assert():

graph.c:1228: graph_output_collapsing_line: Assertion
              `graph->mapping[i - 3] == target' failed.

The assert was introduced by eaf158f8 ("graph API: Use horizontal lines for more compact graphs", 2009-04-21, Git v1.6.4-rc0 -- merge), which is quite old.
This assert is trying to say that when we complete a horizontal line with a single slash, it is because we have reached our target.

It is actually the _second_ collapsing line that hits this assert.
The reason we are in this code path is because we are collapsing the first line, and in that case we are hitting our target now that the horizontal line is complete.
However, the second line cannot be a horizontal line, so it will collapse without horizontal lines. In this case, it is inappropriate to assert that we have reached our target, as we need to continue for another column before reaching the target.
Dropping the assert is safe here.

The new behavior in 0f0f389f12 ("graph: tidy up display of left-skewed merges", 2019-10-15, Git v2.25.0-rc0 -- merge listed in batch #2) caused the behavior change that made this assertion failure possible.
In addition to making the assert possible, it also changed how multiple edges collapse.

In a larger example, the current code will output a collapse as follows:

| | | | | | *
| |_|_|_|_|/|\
|/| | | | |/ /
| | | | |/| /
| | | |/| |/
| | |/| |/|
| |/| |/| |
| | |/| | |
| | * | | |

However, the intended collapse should allow multiple horizontal lines as follows:

| | | | | | *
| |_|_|_|_|/|\
|/| | | | |/ /
| | |_|_|/| /
| |/| | | |/
| | | |_|/|
| | |/| | |
| | * | | |

This behavior is not corrected by this change, but is noted for a later update.


最近更新后,“git log --graph”渲染合并提交的祖先行存在一些垂直空间的浪费,这已经在Git 2.26(2020年第一季度)中得到了修复。

查看提交c958d3b提交8588932(2020年1月8日)由Derrick Stolee (derrickstolee)
(由Junio C Hamano -- gitster --提交d52adee中合并,2020年1月30日)

graph: fix collapse of multiple edges

Signed-off-by: Derrick Stolee

This fix resolves the previously-added test_expect_failure in t4215-log-skewed-merges.sh.

The issue lies in the "else" condition while updating the mapping inside graph_output_collapsing_line().
In 0f0f389f ("graph: tidy up display of left-skewed merges", 2019-10-15, Git v2.25.0-rc0 -- merge listed in batch #2), the output of left-skewed merges was changed to allow an immediate horizontal edge in the first parent, output by graph_output_post_merge_line() instead of by graph_output_collapsing_line().
This condensed the first line behavior as follows:

Before 0f0f389f:

| | | | | | *-.
| | | | | | |\ \
| |_|_|_|_|/ | |
|/| | | | | / /

After 0f0f389f:

| | | | | | *
| |_|_|_|_|/|\
|/| | | | |/ /
| | | | |/| /

However, a very subtle issue arose when the second and third parent edges are collapsed in later steps. The second parent edge is now immediately adjacent to a vertical edge. This means that the condition

} else if (graph->mapping[i - 1] < 0) {

in graph_output_collapsing_line() evaluates as false. The block for this condition was the only place where we connected the target column with the current position with horizontal edge markers.

In this case, the final "else" block is run, and the edge is marked as horizontal, but did not back-fill the blank columns between the target and the current edge.
Since the second parent edge is marked as horizontal, the third parent edge is not marked as horizontal.
This causes the output to continue as follows:

Before this change:

| | | | | | *
| |_|_|_|_|/|\
|/| | | | |/ /
| | | | |/| /
| | | |/| |/
| | |/| |/|
| |/| |/| |
| | |/| | |

By adding the logic for "filling" a horizontal edge between the target column and the current column, we are able to resolve the issue.

After this change:

| | | | | | *
| |_|_|_|_|/|\
|/| | | | |/ /
| | |_|_|/| /
| |/| | | |/
| | | |_|/|
| | |/| | |

This output properly matches the expected blend of the edge behavior before 0f0f389f and the merge commit rendering from 0f0f389f.


这个非常深入研究、富有教育意义和实用价值的回答居然从未得到过任何投票?我不理解,但是我给你点了个赞。 - Don Hatch
@DonHatch 感谢您的好话。这是基于 James Coglan (jcoglan) 和 Denton Liu (Denton-L) 的工作。 - VonC

5

Git从当前提交向上查看祖先。分支不是“实体”,它们是(可移动的)引用。git log(或gitk,其颜色方案不同但类似于git log --graph或tig)无法知道当前分支是否是分支A或分支B的后代。它只知道父级。来自man git-log:

   git log -p -m --first-parent
       Shows the history including change diffs, but only from the "main 
       branch"   perspective, skipping commits that come from merged
       branches, and showing full diffs of changes introduced by the merges. 
       This makes sense only when following a strict policy of merging
       all topic branches when staying on a single integration branch.

希望可以解决你的疑惑。git log默认使用当前检出的提交作为参考(与执行 git log HEAD相同)。

虽然我个人认为git的手册非常清晰,但你可能需要查看gitk或tig。前者是一个图形界面,后者是类似终端的最小化gitk工具。我根据需要使用两者。


4
OP问为什么提交之后它的颜色改变了,有任何想法吗? - Levon
1
gitk中的git在每个分叉处更改颜色。这是一种视觉辅助工具。正如我在我的答案中所说,svn/cvs中不存在“分支”的概念,只有“父节点”。在这里,提交701fe...既是0e5b5...提交的子节点,也是ba051...提交的子节点。Git无法“看到”701fe..的“分支”比0e5b5...分支更多,也无法“看到”它比ba051...分支更多,因此它们是3个不同的“历史记录”,因此具有自己的颜色。如果您查看更多的分支,则颜色会更改。 - Sebastien

0
首先,最左边的红色管道代表哪个分支?我认为它不是我当前所在的分支,因为当我切换到其他分支时,图形看起来一样。此外,它也不代表主分支。
红色管道显示您上一个提交的来源以及与该来源的任何偏差。因此,当您使用“git log --graph --all”命令时,它可能不指向当前分支或主分支上的提交。
其次,如果最左边的分支表示单个分支,为什么在提交“0e5b5”后会改变颜色?
请参阅我上面的答案。最左边的线表示您最后一次提交的分支。该线可能有许多不同的颜色,因为当出现偏差时,它会改变颜色。在顶部部分,它只会变成红色。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接