gpt4 book ai didi

git - Git历史记录作为常规文件

转载 作者:行者123 更新时间:2023-12-04 15:57:27 24 4
gpt4 key购买 nike

我想知道Git本身或文本编辑器中是否有一种方法可以将Git历史记录直接显示为文件树中的常规文件。

就像我的文件landing.html具有几次以前的提交一样,我想将完整的landind.html.old.v0landind.html.old.v1landind.html.old.v2等历史记录文件直接放在当前文件旁边。在文件名中也可能包含提交消息。就像landind.html.old.v0.initial-commit

可以通过在提交后添加一个钩子在Git本身中完成此操作吗?或者,也许作为文本编辑器的扩展?我主要使用VSCodeSublime。我想避免使用其他工具,例如gitk

最佳答案

Git没有内置任何功能,因此您必须编写代码。

在运行git clone之后立即尝试对任何特定文件执行此操作存在一个巨大问题,但是您添加了以下注释:


总的历史会更好,但是我只能和我一个分支来解决。我希望能够在不与Git交互的情况下查找总历史记录。


在这种情况下,有一条明显的前进之路。我将为您概述一个想法,但是您将不得不编写代码。如果您对Git有很多了解,请跳至有关使用提交后挂钩的底部。如果没有,请先通读其余部分。通过编写提交后的钩子,您将学到很多有关Git的知识,但是您可能还需要其他部分。

首先,请记住什么是未跟踪的文件

如果您完全要使用Git,则Git会迫使您了解其三个部分:


工作树。这很简单:在这里工作。工作树中的文件以通常的形式存储,您可以在其中查看和使用它们。
索引,还有另外两个名称,因为它在Git中是如此重要:它也称为暂存区,有时也称为缓存。索引中的文件采用特殊的仅Git格式。这里的关键是您可以替换索引中的文件,因此它们是可写的。
提交。提交是永久的,只读的且不会损坏的。1Git中的提交是历史记录:没有“文件历史记录”之类的东西;每个提交都是一个完整的快照,其内容独立于其他所有快照。 Git通过保存(提交)索引的内容来制作新的快照。


未跟踪的文件是不在索引中的文件。这是Git简单明了的罕见情况。 :-)如果您在工作树中有一个不在索引中的文件,则该文件不会被跟踪。您所有的landing.html.suffix文件都将被取消跟踪。



1提交的持久性取决于其可达性。如以下有关提交的部分所述,Git通过从分支名称(或其他任何标识提交的名称)开始查找提交。这些提交通过其哈希ID标识其父代,因此可以从分支提示中访问父代。父母会确定更多的父母,因此这些父母也是可以达到的。 Git很少会(因为花费很长时间)在可到达的提交集(实际上是可到达的对象)上计算传递性闭包,并将其与对象数据库的整个内容进行比较。此时,根据其他条件,无法到达的对象可能会被垃圾回收(丢弃)。

廉洁性取决于它们是只读和散列的事实。如果对象内部发生某种改变,它将停止匹配其(加密的)哈希ID,Git将知道它已损坏。



关于提交的一些注意事项

(这些都不是直接相关的,但是牢记所有这些都是有用的。)

像所有Git内部对象一样,提交由其哈希ID进行标识(命名)。对象的哈希ID(包括每次提交)是其内容的加密校验和。每次提交的实际内容很小,因为存储的快照是通过称为树的单独Git对象完成的:Git将索引变成一棵树,然后保存树的哈希ID以及提交的元数据(您的姓名和电子邮件地址) ,一些时间戳,您的日志消息以及该提交的父哈希ID)作为提交对象。

存在分支,因此存在存储库中的历史记录,因为提交会存储父ID。像master这样的分支名称仅包含一(1)个提交哈希ID。 Git将此称为尖端提交,根据定义,它是分支上的最后一个提交,即最新的提交。要查找历史记录,Git会查看提示提交的父提交,这是倒数第二个。然后,Git查看父母的父母,即倒数第二位。等等。因此,最终的提交链就是通过分支名称找到的分支,该分支仅标识最尖端的提交:

        D--E   <-- master
/
A--B--C
\
F--G <-- develop


提交 AE都在分支 master上,提交 ACFG都在分支 develop上。请注意,某些提交位于多个分支上。存储在存储库中的历史记录只是存储在存储库中的所有提交的总和。请注意,这里的名称 masterdevelop分别仅标识一个提交。

您可以根据需要创建一个具有单个线性分支的存储库,其中每个提交都与上一个提交完全无关。更有用(但还是故意变态)的是,您可以创建一个存储库,其中每个其他提交都具有一个不同的项目,这样,如果您签出第一个提交,则可以得到Project A的初始尝试。如果签出第二个提交,则将获得Project B的初始尝试。第三次提交是A的第二次提交;第四次提交是B的第二次提交;等等。换句话说,偶数提交N是项目B,提交N / 2;奇数编号的提交是ProjectA,提交楼层((N + 1)/ 2)。

这里的关键点是提交不是变更集。如果同一文件连续多次出现在同一行中多次,则每个提交都有该文件的独立副本。确实,在Git的深处,它们都共享一个文件的“真实副本”(对于相同的对象,这对于Git来说确实很容易;对于一些细微的变化,Git必须将这些对象放入压缩成一个打包文件以进行增量压缩)。

这的真正含义是,为了谈论某个文件或一组文件所发生的事情,您必须选择一些提交进行比较,一次比较一对提交。显而易见的事情是比较每个父母/孩子对。只要提交是线性的,就可以使用:

... G--H--I--J   <-- develop


在这里, G-H对, H-I对和 I-J对进行了有用的比较。但这是其中的一部分:

        D--E
/ \
A--B--C M <-- master
\ /
F--G--H--I--J <-- develop


其中commit Mmaster上的合并提交,此时有人将 develop合并到 master中。提交 M有两个父母,而不仅仅是一个父母:您会将 ME还是与 G进行比较?同时,分支在 C处分叉,因此 C到现在为止;我们可以随时添加更多!-两个孩子。您将 CD进行比较,还是将 CF进行比较?这些是真正棘手的部分,您可以通过“仅设置我的一个分支”来避免。

进行提交

众所周知,提交过程包括以下步骤:


签出一些分支名称:这使其尖端提交成为当前提交。关于此有一些重要的事实:特别是,这如何影响索引和工作树。我们待会儿再讲。
在工作树中进行更改。工作树中的文件具有其普通的读/写格式,因此这很容易。
运行 git add。这实际上是将更新的文件从工作树复制到索引中,从而替换未编辑的索引文件。
运行 git commit。这将收集您的提交日志消息,然后创建实际的提交对象。


提交的棘手部分是将索引变成一个树对象(对此,有一个单独的命令 git write-tree,如果您要手动执行所有操作,则可以运行该命令)。一旦Git具有树对象,它就可以写出提交的文本:

tree <hash>
parent <hash>
author <name> <email> <timestamp>
committer <name> <email> <timestamp>
<log message>


然后将其转换为提交对象(如果愿意,您也可以使用 git hash-object -w -t commit手动完成此部分)。通过计算文本的加密校验和,创建对象会创建对象的哈希ID。只要此提交与其他所有提交都不同(并且时间戳加上其余内容即可确保它确实存在),因为时间总是在增加2,它将获得一个新的,不同于其他所有提交的哈希ID。请注意, parent <hash>行使用当前提交的哈希ID,即您在步骤1中检出的哈希ID。

然后,Git只需将新提交的哈希ID写入分支名称中,以便当前分支(您在步骤1中检出的分支)现在将新提交标识为其提示。最后,这是您可以执行所需操作的地方, git commit运行后提交挂钩。

上面的内容可能会造成混淆,因此让我们举个例子,将一个简单的三提交存储库变成一个四提交存储库:

A--B--C   <-- master (HEAD)


名称 master指向提交 C。您 git checkout master,进行一些更改, git addgit commit,然后创建新的提交 D。新的提交指向 C作为其父级:

A--B--C   <-- master (HEAD)
\
D


然后Git迅速将名称 master正确地上下滑动,使其指向新的提交 D

A--B--C
\
D <-- master (HEAD)


之后,我们通常将图形拉直,使其再次看起来像一条简单的线。

请注意,您可以运行 git commit --amend,这将使新提交将当前提交的父级作为其父级。也就是说,除了让 D指向 C之外,我们还可以让 D指向 B

A--B--C
\
D <-- master (HEAD)


这使历史记录变为 D -> B -> A,跳过 C(已变得不可访问,最终将被垃圾回收)。换句话说,我们实际上并没有更改历史记录- C仍在其中,它不再存在于我们的历史记录链接中-但看起来我们已经拥有了。如果要使用 git commit --amend,请稍后在Git挂钩中记住这一点。

(Git的 git rebase具有类似的效果,但效果更为剧烈:它将多个提交复制到新提交中,而放弃了原始文件。)



2如果通过欺骗和欺骗手段(或仅通过运行使用欺骗和欺骗手段的 git filter-branch)设法进行与现有提交一点一点相同的新提交,则该提交具有相同的作者和提交者,相同的时间戳记,相同的父代,相同的源快照和相同的日志消息,那么您将重复使用旧提交的哈希ID。但是那又怎样呢?您刚刚进行了与旧提交完全相同的新提交。它具有相同的作者,相同的时间,相同的历史记录和相同的日志消息。这是旧的提交。

这里有一个奇怪的情况,当两个分支名称都指向相同的提示提交时,在两个不同的分支名称检出上非常快地(在一秒钟之内)进行两次相同的提交。这将导致分支名称最终指向一个共享的新提交,即使您希望它们指向两个不同的提交,并且如果该过程跨越了一个时钟周期,它们也将具有该名称。从图形理论的意义上讲,结果是正确的,并且可行;但令人惊讶。



填空,或者填入索引和工作树

我提到了上面的步骤1- git checkout branch-name步骤-对索引和工作树有重要影响。请注意,当Git在上面进行新提交时,它首先使用 git write-tree写出索引以创建树对象。这意味着索引必须从当前提交开始匹配。3

git checkout命令通过将当前(检出前)提交与目标(检出后)提交进行比较来实现此目的。当前提交具有​​一组文件,而目标提交具有另一组文件,大概至少有些不同。签出将从当前索引和工作树中删除那些必须删除的文件。它将必须添加的任何文件添加到当前索引和工作树中。它将在索引和工作树中替换必须换出的所有文件,以从旧提交转到新提交。

结果,在 git checkout之后,索引和工作树将与目标提交(刚成为当前提交)匹配(除了完全不在索引中的未跟踪文件)。

还要注意,当您运行 git commit时,这将使用当前索引进行新的提交。结果是,一旦完成新的提交,当前的提交和索引将再次匹配。因此,我们得到了有关Git的基本信息(尽管稍有灵活性,请参见脚注3):索引通常与当前提交匹配,直到您开始从工作树复制文件为止。



3实际上,在结帐时可以保留一些差异。有关详细信息,请参见 Checkout another branch when there are uncommitted changes on the current branch



使用提交后钩子获取所需内容

Git在 git add成功完成后立即运行您的提交后钩子。此 git commit进行了新的提交,例如在我们将三提交存储库转换为四提交存储库的示例中的提交 git commit

新提交具有一个父级,例如 D。现在您有机会比较父母与孩子:

git diff --name-status HEAD^ HEAD


例如。 ( C是当前的,即child,commit,而 HEAD表示查看 HEAD^的第一个父级。请记住这里有多个父级的合并提交:您可以使用 HEAD来查找例如,在合并的第二个父节点上。我不确定,当 HEAD^2进行合并提交时, git merge是否运行后提交钩子,尽管我怀疑是这样。) git merge的输出告诉您打印的每个文件发生了什么;有关详细信息,请参见 the git diff --name-status documentation。4

此时,如果某些文件(例如 git diff)已更改(状态 landing.html),或者已创建新文件(状态 M),则可以在下一个版本号下复制该文件,并使用提交日志消息主题( A)。如果文件没有更改,您将无输出- git log -1 --pretty=format:%s HEAD什么也没说,因为无话可说-因此您就不会复制。

随着时间的流逝,结果是您将在工作树中构建想要作为历史记录的未跟踪文件,并按提交顺序进行编号。为了使编号有意义,您甚至可以检查您所在的分支(如果有的话-在“分离的HEAD”模式下,例如,当您查看历史提交时, git diff根本没有附加到分支名称上) )。请注意,您可以使用 HEADgit rev-parse --abbrev-ref HEAD来获取分支名称。5



4对于脚本编写,您应该真正使用 git symbolic-ref --short HEAD,它更容易预测。例如,它不遵循每个用户的配置控件,因此每个人的行为都相同。 git diff-tree将查看您的 git diff设置,您的 diff.renames等,以及diff-output着色选项,所有这些都可能与脚本混淆。

5两者之间的区别在于,如果HEAD被分离, diff.renameLimit将失败(退出非零值),并且不产生标准输出(但默认情况下将写入stderr)。在这种情况下, git symbolic-ref只会打印 git rev-parse

关于git - Git历史记录作为常规文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49142384/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com