在Git中,提交是用来记录版本库的变更的。
当提交时,Git会记录索引的快照并将快照放进对象库,该快照不包含该索引中任何文件或记录中的副本。Git会将当前索引的状态与之前的快照做一个比较,并派生出一个受影响的文件和目录列表,并会为任何有变化的文件创建新blob对象,对有变化的目录创建新的树对象,对于未改动的文件和目录则会沿用之前的blob与树对象。
提交的快照是串联到一起的,每张新的快照指向其前驱,随着时间的推移,一系列变更就表示为一系列提交。
版本库中的变更和提交时一一对应的关系,提交则是将变更引入版本库的唯一方法,任何版本库中的变更都必须由一个提交引入。
原子变更集
每个Git提交都代表一个相对于之前状态的单个原子变更集合。即一个提交中的改动,无论包含多少文件,多少修改内容,要么全部应用,要么全部拒绝。
这点和数据库操作是一致的,不能十行修改因为某个不知名的原因只成功提价了九行,那如何定位未提交的一行很显然也是一个问题。
而在底层对象模型方面,原子性也是有意义的:一张提交快照就代表所有文件和目录的变更,其表示了一棵树的状态,而两张提交快照之间的变更集就代表一个完整的树到树的转换。
识别提交
在Git中,可以通过显式或隐式引用来指代每一个提交。如某次提交的散列值是显式提交,而始终指向最新提交的HEAD则是隐式提交。
同时Git提供了不同的命名机制来提交命名,
绝对提交名
在Git中,最严格的识别提交方式肯定是散列值,毕竟Git中的对象都是通过散列值识别的。
$ git log -1 --pretty=oneline HEAD9f1d1da871fe53fcaa5bdaa8417b47ab7b1b740f (HEAD -> master) commit file$ git log -1 --pretty=oneline 9f1d1da871fe53fcaa5bdaa8417b47ab7b1b740f9f1d1da871fe53fcaa5bdaa8417b47ab7b1b740f (HEAD -> master) commit file
比如上面的命令会识别出散列值对应的提交,并打印。
引用和符号引用
引用(ref)是一个散列值,指向Git对象库中的对象。虽然一个引用可以指向任何Git对象,但是其通常指向提交对象。符号引用(symbolic reference),或称为symref,间接指向Git对象。
本地特性分支名称,远程跟踪分支名称和标签名都是引用。
每一个符号引用都有一个以ref/开始的明确全称,并且都分层存储在版本库的.git/refs/目录中。目录中基本有三种不同的命名空间代表不同的引用:
refs/heads/ref代表本地分支refs/remotes/ref代表远程跟踪分支refs/tags/ref代表标签
比如当前版本库的引用目录为:
$ find .git/refs/.git/refs/.git/refs/heads.git/refs/heads/master.git/refs/tags
同时Git会自动维护几个用于特定目的的特殊符号引用,这些引用可以在使用提交的任何地方使用。
HEAD:HEAD始终指向当前分支的最近提交,当切换分支时,HEAD会更新为指向新分支的最近提交。ORIG_HEAD:某些操作,如merge或reset,会把调整为新值之前的先前版本的HEAD记录到ORIG_HEAD中。可以使用ORIG_HEAD来恢复或回滚到之前的状态或者做一个比较。FETCH_HEAD:当时用远程库时,git getch命令将所有抓取分支的头记录到.git/FETCH_HEAD中。FETCH_HEAD是最近抓取的分支的HEAD的缩写,并且仅在刚刚抓取操作之后才有效。MERGE_HEAD:当一个合并操作正在进行时,其它分支的头暂时记录在MERGE_HEAD中。换言之,MERGE_HEAD是正在合并进HEAD的提交。
所有的这些符号引用都可以使用底层命令git symbolic-ref进行管理。
相对提交名
Git还提供一种机制来确定相对于另一个引用的提交,通常是分支的头。
开发中关于提交可能会出现master、master^、master~2之类的符号,这就是相对提交名。
除了第一个根提交外,所有的提交都来自至少一个比其更早的提交,即父提交。而若一个提交存在多个父提交,那么其必定是由合并操作产生的。
在同一代提交中,插入符号^是用来选择不同的父提交的。比如给定提交A,A^1表示其第一个父提交,A^2表示其第二个父提交。而波浪线~用于返回父提交之前并选择上一代提交,比如给定提交A,A~1表示其第一个父提交,A~2表示其第一个父提交的第一个父提交。
同时还存在一些特殊写法,比如A~1、A^1、A~、A^含义相同,都是A第一个父提交,A^^、A^1^1含义也相同,实际使用需要灵活运用。
这里以git的源码为例,看下其提交的历史:
$ git show-branch master --more=35 | tail -10fatal: bad sha1 reference --more=35$ git show-branch --more=35 | tail -10[master~9^2~3] t2107: test 'git update-index --verbose'[master~7^2~6] Git 2.37-rc0[master~10] Merge branch 'jk/perf-lib-test-titles'[master~10^2] perf-lib: fix missing test titles in output[master~10^2^] t/perf: add iteration setup mechanism to perf-lib[master~11] builtin/rebase: remove a redundant space in l10n string[master~12] Fixes and updates post -rc0[master~13] Merge branch 'fs/ssh-default-key-command-doc'[master~13^2] gpg docs: explain better use of ssh.defaultKeyCommand[master~14] Merge branch 'po/rebase-preserve-merges'
其中的一次提交为:
$ git rev-parse master~13^2ce18a30bb78720d90df42b9d9ee6b8b7dd33d7e6$ git cat-file -p ce18a30bb78720d90df42b9d9ee6b8b7dd33d7e6tree ff62097391dd860007845ec05624eb1decfc740eparent dc8c8deaa6b5847733bd7df011a4c7b7d1a64e0aauthor Fabian Stelzer <fs@gigacodes.de> 1654701877 +0200committer Junio C Hamano <gitster@> 1654731220 -0700gpg docs: explain better use of ssh.defaultKeyCommandUsing `ssh-add -L` for gpg.ssh.defaultKeyCommand is not a goodrecommendation. It might switch keys depending on the order of knownkeys and it only supports ssh-* and no ecdsa or other keys.Clarify that we expect a literal key prefixed by `key::`, give validexample use cases and refer to `user.signingKey` as the preferredoption.Signed-off-by: Fabian Stelzer <fs@gigacodes.de>Signed-off-by: Junio C Hamano <gitster@>
从中可以看出,该次提交对应的散列值正是对应的本次提交,这样说起来可能有点绕,但是相对提交名确实是有效的。
而Git中的git rev-parse命令会将任何形式的提交名,包括标签,相对名,简写或绝对名称都转换为对象库中的散列值。
提交历史记录
查看旧提交
显式提交历史记录的命令主要是git log,该命令还存在很多参数。
不过首先git log的形式和git log HEAD的作用一样,输出每一个可以从HEAD找到的历史记录中的提交日志信息。变更从HEAD提交开始进行回溯,并且Git是基于其内部的提交图进行回溯的,跟提交时间并不完全一致。
而如果在git log命令中显式提供了一个提交名,那么该日志将从该提交开始回溯。
$ git logcommit 5b71c59bc3b9365075e2a175aa7b6f2b0c84ce44 (HEAD -> master, tag: v2.37.0-rc1, origin/master, origin/main, origin/HEAD)Author: Junio C Hamano <gitster@>Date: Fri Jun 17 17:15:13 -0700Git 2.37-rc1Signed-off-by: Junio C Hamano <gitster@>commit 694c0cc0fb531b17750ac6e8194f193f8eb8Merge: b4eda05d58 6b11e3d52eAuthor: Junio C Hamano <gitster@>Date: Fri Jun 17 17:12:31 -0700Merge branch 'cb/path-owner-check-with-sudo-plus'"sudo git foo" used to consider a repository owned by the originaluser a safe one to access; it now also considers a repository ownedby root a safe one, too (after all, if an attacker can craft amalicious repository owned by root, the box is 0wned already).* cb/path-owner-check-with-sudo-plus:git-compat-util: allow root to access both SUDO_UID and root ownedcommit 6b11e3d52e919cce91011f4f9025e6f4b61375f2Author: Carlo Marcelo Arenas Belón <carenas@>Date: Fri Jun 17 13:23:38 -0700git-compat-util: allow root to access both SUDO_UID and root ownedPrevious changes introduced a regression which will prevent root foraccessing repositories owned by thyself if using sudo because SUDO_UIDtakes precedence.Loosen that restriction by allowing root to access repositories ownedby both uid by default and without having to add a safe.directoryexception.A previous workaround that was documented in the tests is no longerneeded so it has been removed together with its specially craftedprerequisite.Helped-by: Johanness Schindelin <Johannes.Schindelin@gmx.de>Signed-off-by: Carlo Marcelo Arenas Belón <carenas@>Signed-off-by: Junio C Hamano <gitster@>commit b4eda05d58ca3e4808d3d86ab5826c77995a06f7Author: Jiang Xin <zhiyou.jx@alibaba->Date: Fri Jun 17 18:03:09 +0800i18n: fix mismatched camelCase config variablesSome config variables are combinations of multiple words, and wetypically write them in camelCase forms in manpage and translatablestrings. It's not easy to find mismatches for these camelCase configvariables during code reviews, but occasionally they are identifiedduring localization translations.To check for mismatched config variables, I introduced a new featurein the helper program for localization[^1]. The following mismatchedconfig variables have been identified by running the helper program,such as "git-po-helper check-pot".Lowercase in manpage should use camelCase:* Documentation/config/http.txt: http.pinnedpubkeyLowercase in translable strings should use camelCase:* builtin/fast-import.c: pack.indexversion* builtin/gc.c: gc.logexpiry* builtin/index-pack.c: pack.indexversion* builtin/pack-objects.c: pack.indexversion* builtin/repack.c: pack.writebitmaps* commit.c:mitencoding* gpg-interface.c: user.signingkey* http.c: http.postbuffer* submodule-config.c:submodule.fetchjobsMismatched camelCases, choose the former:* Documentation/config/transfer.txt: transfer.credentialsInUrlremote.c:transfer.credentialsInURL[^1]: /git-l10n/git-po-helperSigned-off-by: Jiang Xin <zhiyou.jx@alibaba->Signed-off-by: Junio C Hamano <gitster@>commit b81b98f818fdacdc472f2afed2ae67d9d0893fe2Author: Junio C Hamano <gitster@>Date: Fri Jun 17 10:33:42 -0700Another batch of fixes before -rc1Signed-off-by: Junio C Hamano <gitster@>commit aa11b94ef87050af9e4e0aab64f1ab89636c5be4Merge: 7f5a382aa5 f8535596aaAuthor: Junio C Hamano <gitster@>Date: Fri Jun 17 10:33:32 -0700Merge branch 'jk/bug-fl-va-list-fix'$ git log 6b11e3d52e919cce91011f4f9025e6f4b61375f2commit 6b11e3d52e919cce91011f4f9025e6f4b61375f2Author: Carlo Marcelo Arenas Belón <carenas@>Date: Fri Jun 17 13:23:38 -0700git-compat-util: allow root to access both SUDO_UID and root ownedPrevious changes introduced a regression which will prevent root foraccessing repositories owned by thyself if using sudo because SUDO_UIDtakes precedence.Loosen that restriction by allowing root to access repositories ownedby both uid by default and without having to add a safe.directoryexception.A previous workaround that was documented in the tests is no longerneeded so it has been removed together with its specially craftedprerequisite.Helped-by: Johanness Schindelin <Johannes.Schindelin@gmx.de>Signed-off-by: Carlo Marcelo Arenas Belón <carenas@>Signed-off-by: Junio C Hamano <gitster@>commit b9063afda17a2aa6310423c9f7b776c41f753091Author: Carlo Marcelo Arenas Belón <carenas@>Date: Thu May 12 18:00:19 -0700t0034: add negative tests and allow git init to mostly work under sudoAdd a support library that provides one function that can be usedto run a "scriplet" of commands through sudo and that helps invokingsudo in the slightly awkward way that is required to ensure it doesn'tblock the call (if shell was allowed as tested in the prerequisite)and it doesn't run the command through a different shell than the onewe intended.Add additional negative tests as suggested by Junio and that use anew workspace that is owned by root.Document a regression that was introduced by previous commits whereroot won't be able anymore to access directories they own unlessSUDO_UID is removed from their environment.The tests document additional ways that this new restriction couldbe worked around and the documentation explains why it might be insteadconsidered a feature, but a "fix" is planned for a future change.Helped-by: Junio C Hamano <gitster@>Helped-by: Phillip Wood <phillip.wood123@>Signed-off-by: Carlo Marcelo Arenas Belón <carenas@>Signed-off-by: Junio C Hamano <gitster@>
上面的例子也说明历史记录并不是按照时间打印的,具体的要看提交图。
但这样从头开始看很难定位某个提交,此时可以使用since..until的形式指定提交的范围:
$ git log --pretty=short --abbrev-commit master~12..master~10commit 2fec2d2895Merge: 3b9a5a33c2 55d9d4bbd0Author: Junio C Hamano <gitster@>Merge branch 'jk/perf-lib-test-titles'commit 55d9d4bbd0Author: Jeff King <peff@>perf-lib: fix missing test titles in outputcommit 3b9a5a33c2Author: Fangyi Zhou <me@fangyi.io>builtin/rebase: remove a redundant space in l10n string
上面的代码就是打印了master~12到master~10之间的所有提交,也就是该分支上之前10次和第11次的提交。而同时--pretty=short调整打印的信息数量,--abbrev-commit则是以缩写散列值打印。
git log -p则可以输出提交引进的补丁或变更:
$ git log -1 -p mastercommit 5b71c59bc3b9365075e2a175aa7b6f2b0c84ce44 (HEAD -> master, tag: v2.37.0-rc1, origin/master, origin/main, origin/HEAD)Author: Junio C Hamano <gitster@>Date: Fri Jun 17 17:15:13 -0700Git 2.37-rc1Signed-off-by: Junio C Hamano <gitster@>diff --git a/Documentation/RelNotes/2.37.0.txt b/Documentation/RelNotes/2.37.0.txtindex f1b93f3c59..99dc7e32f8 100644--- a/Documentation/RelNotes/2.37.0.txt+++ b/Documentation/RelNotes/2.37.0.txt@@ -234,9 +234,8 @@ Fixes since v2.36* With a recent update to refuse access to repositories of otherpeople by default, "sudo make install" and "sudo git describe"- stopped working. This series intends to loosen it while keeping- the safety.- (merge b9063afda1 cb/path-owner-check-with-sudo later to maint).+ stopped working, which has been corrected.+ (merge 6b11e3d52e cb/path-owner-check-with-sudo-plus later to maint).* The tests that ensured merges stop when interfering local changesare present did not make sure that local changes are preserved; nowdiff --git a/GIT-VERSION-GEN b/GIT-VERSION-GENindex c0b5e722dd..22e76c2a59 100755--- a/GIT-VERSION-GEN+++ b/GIT-VERSION-GEN@@ -1,7 +1,7 @@#!/bin/sh
而上边的-1则会限制输出为一个提交。
而git log中的--stat选项将会列举出提交中所更改的文件以及每个更改的文件中有多少行存在改动。
$ git log --pretty=short --stat master~12..master~10commit 2fec2d289588a70f8683bfe8f429d9e3d3d31ef5Merge: 3b9a5a33c2 55d9d4bbd0Author: Junio C Hamano <gitster@>Merge branch 'jk/perf-lib-test-titles'commit 55d9d4bbd044afa004c6962aa50635158dc8719eAuthor: Jeff King <peff@>perf-lib: fix missing test titles in outputt/perf/perf-lib.sh | 2 +-1 file changed, 1 insertion(+), 1 deletion(-)commit 3b9a5a33c2986522736d484da497ccd99d715220Author: Fangyi Zhou <me@fangyi.io>builtin/rebase: remove a redundant space in l10n stringbuiltin/rebase.c | 2 +-1 file changed, 1 insertion(+), 1 deletion(-)
而另一个查看对象库中对象信息的命令是git show,可以使用其来查看某个提交:
$ git show HEADcommit 5b71c59bc3b9365075e2a175aa7b6f2b0c84ce44 (HEAD -> master, tag: v2.37.0-rc1, origin/master, origin/main, origin/HEAD)Author: Junio C Hamano <gitster@>Date: Fri Jun 17 17:15:13 -0700Git 2.37-rc1Signed-off-by: Junio C Hamano <gitster@>diff --git a/Documentation/RelNotes/2.37.0.txt b/Documentation/RelNotes/2.37.0.txtindex f1b93f3c59..99dc7e32f8 100644--- a/Documentation/RelNotes/2.37.0.txt+++ b/Documentation/RelNotes/2.37.0.txt@@ -234,9 +234,8 @@ Fixes since v2.36* With a recent update to refuse access to repositories of otherpeople by default, "sudo make install" and "sudo git describe"- stopped working. This series intends to loosen it while keeping- the safety.- (merge b9063afda1 cb/path-owner-check-with-sudo later to maint).+ stopped working, which has been corrected.+ (merge 6b11e3d52e cb/path-owner-check-with-sudo-plus later to maint).* The tests that ensured merges stop when interfering local changesare present did not make sure that local changes are preserved; nowdiff --git a/GIT-VERSION-GEN b/GIT-VERSION-GENindex c0b5e722dd..22e76c2a59 100755--- a/GIT-VERSION-GEN+++ b/GIT-VERSION-GEN@@ -1,7 +1,7 @@#!/bin/shGVF=GIT-VERSION-FILE-DEF_VER=v2.37.0-rc1 ;# not quite+DEF_VER=v2.37.0-rc1LF=''
上面的代码具体打印了本次提交对应提交对象的详细信息,也可以查看某个特定的对象信息:
$ git show b1a8c144e073ba9c5f5d085fa864b5bfb99892fa#ifndef TR2_DST_H#define TR2_DST_Hstruct strbuf;#include "trace2/tr2_sysenv.h"struct tr2_dst {enum tr2_sysenv_variable sysenv_var;int fd;unsigned int initialized : 1;unsigned int need_close : 1;unsigned int too_many_files : 1;};/** Disable TRACE2 on the destination. In TRACE2 a destination (DST)* wraps a file descriptor; it is associated with a TARGET which* defines the formatting.*/void tr2_dst_trace_disable(struct tr2_dst *dst);/** Return the file descriptor for the DST.* If 0, the dst is closed or disabled.*/int tr2_dst_get_trace_fd(struct tr2_dst *dst);/** Return true if the DST is opened for writing.*/int tr2_dst_trace_want(struct tr2_dst *dst);/** Write a single line/message to the trace file.*/void tr2_dst_write_line(struct tr2_dst *dst, struct strbuf *buf_line);#endif /* TR2_DST_H */
提交图
Git中通过有向无环图来实现版本库的提交历史记录。
在提交图中,每个节点都代表一个单独的提交,所有边都从子节点指向父节点,形成祖先关系。
并且在提交图中,时间并不是提交图排序的决定因素,因为如果全球协作开发的项目,不同地点的时间加上不同用户主机实际的时间总是不一致的。
提交图具体可以使用gitk工具看一下大概是什么东西:
上图对应的就是gitk的界面,也很好理解,就不多解释了。
而对于提交图来说:
一般的提交有且只有一个父提交,也就是提交历史记录中的上一次提交通常一种提交没有父提交,就是初始提交,该提交一般在图的最低端合并提交会拥有多个父提交
提交范围
通常情况下,可以使用提交范围检查分支或分支的某一部分。
之前使用master~12..master~10就是指明了提交的范围,前边的master~12不包含,master~10包含,该范围标记为start..end,就是end可达,start不可达的范围。而使用git log就是打印HEAD可达的范围。
也可以通过^A排除可达提交集中特定的提交A,git log ^A B就等同于git log A..B。
当如果上面的提交范围设计到分支就会不太容易理解。这里看几个例子:
上图的topic..master范围为W、X、Y、Z。
上图的topic..master范围为V之前的提交和V、W、X、Y、Z。
上图的topic..master范围为W、X、Y、Z。
而提交范围start..end的形式如果没有指定start或end,则缺省项均默认为HEAD。
而与start..end类似的还有start...end,就是两个点和三个点的区别。这种形式表示为对称差,也就是start或end可达但又不是start和end同时可达的提交集合。
上图的topic...master范围为D、E、F、G、H、I、X、Y、Z。
可以使用如下命令计算A和B的对称差:
$ git rev-list A B --not `(git merge-base --all B)`
而有时也会遇到很抽象的命令,如:
git log ^dev ^topic master
上述命令表示在master分支,不在dev或topic分支上的所有提交。
查找提交
git bisect
该命令一般基于任一搜索条件查找特定的错误提交。
该命令一般用于定位某个错误的提交,比如某次提交存在bug,但之后隔了好久这个bug才出现,那么如果定位该bug处于哪一次提交呢,此时就可以用到git bisect命令
该命令使用二分法来定位bad提交,而用户只需要判断本地提交是good或是bad即可。这里简单看一下该过程。
$ git bisect startxxx@xxxxxx ~/Desktop/GIT/git (master|BISECTING)$ git bisect badxxx@xxxxxx ~/Desktop/GIT/git (master|BISECTING)$ git bisect good 1f8496c65f963d2b75ef57dc4f09dbc2f5646bf3Bisecting: 20 revisions left to test after this (roughly 5 steps)[99bbf4739d927a3d8183d1fc3f1ee7871aac9fb9] Merge branch 'jc/cocci-cleanup'xxx@xxxxxx ~/Desktop/GIT/git ((99bbf4739d...)|BISECTING)$ git bisect goodBisecting: 9 revisions left to test after this (roughly 3 steps)[d0d96b8280faf7c22c115374732f50972689c0d2] Merge branch 'js/ci-github-workflow-markup'xxx@xxxxxx ~/Desktop/GIT/git ((d0d96b8280...)|BISECTING)$ git bisect goodBisecting: 4 revisions left to test after this (roughly 2 steps)[aa11b94ef87050af9e4e0aab64f1ab89636c5be4] Merge branch 'jk/bug-fl-va-list-fix'xxx@xxxxxx ~/Desktop/GIT/git ((aa11b94ef8...)|BISECTING)$ git bisect badBisecting: 2 revisions left to test after this (roughly 1 step)[7281c196b1166f1c00df33948c67b0ef81ba63a9] transfer doc: move fetch.credentialsInUrl to "transfer" config namespacexxx@xxxxxx ~/Desktop/GIT/git ((7281c196b1...)|BISECTING)$ git bisect goodBisecting: 1 revision left to test after this (roughly 1 step)[f8535596aa7bd7f6862af3fe6420ac12b17c9912] bug_fl(): correctly initialize trace2 va_listxxx@xxxxxx ~/Desktop/GIT/git ((f8535596aa...)|BISECTING)$ git bisect badf8535596aa7bd7f6862af3fe6420ac12b17c9912 is the first bad commitcommit f8535596aa7bd7f6862af3fe6420ac12b17c9912Author: Jeff King <peff@>Date: Thu Jun 16 16:04:25 -0400bug_fl(): correctly initialize trace2 va_listThe code added 0cc05b044f (usage.c: add a non-fatal bug() function to gowith BUG(), -06-02) sets up two va_list variables: one to output tostderr, and one to trace2. But the order of initialization is wrong:va_list ap, cp;va_copy(cp, ap);va_start(ap, fmt);We copy the contents of "ap" into "cp" before it is initialized, meaningit is full of garbage. The two should be swapped.However, there's another bug, noticed by Johannes Schindelin: we forgetto call va_end() for the copy. So instead of just fixing the copy'sinitialization, let's do two separate start/end pairs. This is allowedby the standard, and we don't need to use copy here since we have accessto the original varargs. Matching the pairs with the calls makes it moreobvious that everything is being done correctly.Note that we do call bug_fl() in the tests, but it didn't trigger thisproblem because our format string doesn't have any placeholders. So eventhough we were passing a garbage va_list through the stack, nobody everneeded to look at it. We can easily adjust one of the trace2 tests totrigger this, both for bug() and for BUG(). The latter isn't broken, butit's nice to exercise both a bit more. Without the fix in this patch(but with the test change), the bug() case causes a segfault.Signed-off-by: Jeff King <peff@>Signed-off-by: Junio C Hamano <gitster@>t/helper/test-trace2.c | 4 ++--usage.c| 8 +++++---2 files changed, 7 insertions(+), 5 deletions(-)
上面的过程为:
首先使用git bisect start命令启动该过程启动该过程后,Git会进入二分模式,并为自己设置一些状态信息,然后使用一个分离的HEAD来管理版本库的当前检出版本。该HEAD本质上是个匿名分支,可用于版本库中来回移动并视需要指定不同的修订版本。然后执行一个bad提交,因为此时默认在分离HEAD处,可用git bisect bad指定当前HEAD为bad然后利用git bisect good或git bisect bad对提示的提交指明其为good或为bad指明后Git会打印当前还有多少提交要确认,因为是二分搜索,每次确认数目都会减少一半当确认完毕,就会打印bad提交的具体信息
当然这过程中还有其它额外命令。
git bisect log可以打印该过程的log:
$ git bisect loggit bisect start# bad: [5b71c59bc3b9365075e2a175aa7b6f2b0c84ce44] Git 2.37-rc1git bisect bad 5b71c59bc3b9365075e2a175aa7b6f2b0c84ce44# good: [1f8496c65f963d2b75ef57dc4f09dbc2f5646bf3] push: fix capitalisation of the option name autoSetupMergegit bisect good 1f8496c65f963d2b75ef57dc4f09dbc2f5646bf3# good: [99bbf4739d927a3d8183d1fc3f1ee7871aac9fb9] Merge branch 'jc/cocci-cleanup'git bisect good 99bbf4739d927a3d8183d1fc3f1ee7871aac9fb9# good: [d0d96b8280faf7c22c115374732f50972689c0d2] Merge branch 'js/ci-github-workflow-markup'git bisect good d0d96b8280faf7c22c115374732f50972689c0d2# bad: [aa11b94ef87050af9e4e0aab64f1ab89636c5be4] Merge branch 'jk/bug-fl-va-list-fix'git bisect bad aa11b94ef87050af9e4e0aab64f1ab89636c5be4# good: [7281c196b1166f1c00df33948c67b0ef81ba63a9] transfer doc: move fetch.credentialsInUrl to "transfer" config namespacegit bisect good 7281c196b1166f1c00df33948c67b0ef81ba63a9# bad: [f8535596aa7bd7f6862af3fe6420ac12b17c9912] bug_fl(): correctly initialize trace2 va_listgit bisect bad f8535596aa7bd7f6862af3fe6420ac12b17c9912# first bad commit: [f8535596aa7bd7f6862af3fe6420ac12b17c9912] bug_fl(): correctly initialize trace2 va_list
如果某条bad或good的判定不正确,可以结合git bisect reset和git bisect replay file使用某个文件作为输入重新执行该过程。
也可以使用git bisect visualize命令可视化地检查提交范围内的内容。
如果一切都结束了,则可以使用git bisect reset结束该过程,回到原来的分支上。
$ git bisect resetPrevious HEAD position was f8535596aa bug_fl(): correctly initialize trace2 va_listSwitched to branch 'master'Your branch is up to date with 'origin/master'.
git blame
该命令能够识别特定提交,通过用户一个文件的每一行最后是谁修改的和哪些提交做出了修改。
$ git blame -L 1, apply.h71501a71d04 (Christian Couder -08-08 23:02:59 +0200 1) #ifndef APPLY_H71501a71d04 (Christian Couder -08-08 23:02:59 +0200 2) #define APPLY_H71501a71d04 (Christian Couder -08-08 23:02:59 +0200 3)97b989ee3a9 (Denton Liu -09-25 01:20:53 -0700 4) #include "hash.h"ef3ca95475c (Elijah Newren -08-15 10:54:05 -0700 5) #include "lockfile.h"ef3ca95475c (Elijah Newren -08-15 10:54:05 -0700 6) #include "string-list.h"4e9a3252531 (René Scharfe -01-07 13:16:53 +0100 7) #include "strmap.h"ef3ca95475c (Elijah Newren -08-15 10:54:05 -0700 8)82ea77eca7a (Nguyễn Thái Ngọc Duy -08-13 18:14:39 +0200 9) struct repository;82ea77eca7a (Nguyễn Thái Ngọc Duy -08-13 18:14:39 +0200 10)71501a71d04 (Christian Couder -08-08 23:02:59 +0200 11) enum apply_ws_error_action {71501a71d04 (Christian Couder -08-08 23:02:59 +0200 12)nowarn_ws_error,71501a71d04 (Christian Couder -08-08 23:02:59 +0200 13)warn_on_ws_error,71501a71d04 (Christian Couder -08-08 23:02:59 +0200 14)die_on_ws_error,71501a71d04 (Christian Couder -08-08 23:02:59 +0200 15)correct_ws_error71501a71d04 (Christian Couder -08-08 23:02:59 +0200 16) };71501a71d04 (Christian Couder -08-08 23:02:59 +0200 17)71501a71d04 (Christian Couder -08-08 23:02:59 +0200 18) enum apply_ws_ignore {
不过该打印结果看起来似乎太冗余了。
git log -Sstring
git log -Sstring根据给定的string沿着文件的差异历史搜索。通过搜索修订版本间的实际差异,该命令可以找到那些执行改变的提交。
$ git log -Sinclude --pretty=oneline --abbrev-commit apply.h4e9a325253 apply: use strsets to track symlinks97b989ee3a apply.h: include missing headeref3ca95475 Add missing includes and forward declarations7e1bad24e3 apply: refactor `git apply` option parsing13b5af22f3 apply: move libified code from builtin/apply.c to apply.{c,h}71501a71d0 apply: move 'struct apply_state' to apply.h
打印结果对应的左侧每个提交都添加或删除了包含include的行。但是如果某个提交添加和删除了相同数量的含关键词的行,它将不会显示出来。该提交必须有添加和删除数量上的变化才能计数。