当前位置: 首页 > 科技观察

说明Git是如何工作的

时间:2023-03-19 12:15:40 科技观察

这篇文章说明了Git中最常用的命令。如果你对Git的工作原理有一点了解,这篇文章可以让你更好地理解。基本用法上面的四个命令在工作目录、暂存目录(也称为索引)和存储库之间复制文件。gitaddfiles将当前文件放入暂存区。gitcommit生成暂存区的快照并提交。gitreset-files用于撤消上次gitadd的文件,也可以使用gitreset撤消所有暂存区文件。gitcheckout-files将文件从暂存区复制到工作目录,丢弃本地更改。您可以使用gitreset-p、gitcheckout-p或gitadd-p进入交互模式。也可以跳过暂存区直接从仓库取文件或者直接提交代码。gitcommit-a相当于运行gitadd将当前目录下的所有文件添加到暂存区,然后运行。gitcommitfiles进行一次提交,其中包含最后一次提交以及工作目录中文件的快照。并将文件添加到暂存区。gitcheckoutHEAD-回滚文件以复制上次提交。同意在下文中按以下形式使用图片。绿色的5位字符表示提交的ID,分别指向父节点。分支以橙色显示并指向特定的提交。当前分支由附加到它的HEAD标识。这张图显示了最后5次提交,ed489是最新的提交。master分支指向本次提交,另一个maint分支指向祖父提交节点。命令解释Diff查看两次提交之间变化的方法有很多种,这里举几个例子。Commit提交时,Git会用暂存区的文件创建一个新的提交,并将此时的节点设置为父节点。然后将当前分支指向新的提交节点。下图中,当前分支为master。运行命令前master指向ed489,提交后master指向新节点f0cec,并以ed489为父节点。即使当前分支是提交的祖父分支,git也会这样做。下图中,在master分支的grandparent的maint分支上进行了commit,生成了1800b。这样maint分支就不再是master分支的祖父分支了。在这种情况下,merge[1](或rebase[2])是必要的。如果要更改提交,请使用gitcommit--amend。Git将使用与当前提交相同的父提交进行新提交,而旧提交将被取消。另一个例子是分离HEAD提交[3],稍后描述。CheckoutCheckout命令用于将文件从历史提交(或暂存区)复制到工作目录,也可用于切换分支。当给定文件名时(打开-p选项,或同时打开-p选项),Git会将文件从指定的提交复制到暂存区和工作目录。例如gitcheckoutHEAD~foo.c会将提交节点HEAD~(即当前提交节点的父节点)中的foo.c复制到工作目录,并添加到暂存区。(如果命令中没有指定提交节点,内容将从暂存区复制。)注意当前分支不会改变。当没有指定文件名,但是给出了一个(本地)分支时,那么HEAD标志被移动到那个分支(也就是我们“切换”到那个分支),然后暂存区和工作目录的内容就会与HEAD对应的提交节点一致。新提交节点(下图中的a47c3)中的所有文件将被复制(到暂存区和工作目录);仅存在于旧提交节点(ed489)中的文件将被删除;以上文件均未被忽略且不受影响。如果既没有指定文件名也没有指定分支名称,而是指定了标记、远程分支、SHA-1值或类似master~3的内容,您将得到一个名为detachedHEADlogo的匿名分支。这使得在历史版本之间切换变得容易。比如你要编译1.6.6.1版本的Git,可以运行gitcheckoutv1.6.6.1(这是一个标签,不是分支名),编译安装,然后切换回另一个分支,这样作为gitcheckoutmaster。但是,当提交操作涉及“分离的HEAD”时,行为会略有不同,如下所述。当HEAD处于分离状态时提交操作当HEAD处于分离状态(未附加到任何分支)时,提交操作正常进行,但不会更新任何命名分支。(你可以认为这是更新一个匿名分支。)一旦你切换到另一个分支,比如master,那么这个提交节点(可能)将永远不会被再次引用,然后它就会被丢弃。请注意,在此命令之后没有任何内容引用2eecb。但是,如果你想保存这个状态,你可以使用命令gitcheckout-bname创建一个新的分支。Reset命令将当前分支指向另一个位置,并可选择更改工作目录和索引。还用于在不触及工作目录的情况下将文件从历史存储库复制到索引。如果没有给出选项,则当前分支指向该提交。如果使用–hard选项,工作目录也会更新,如果使用–soft选项,则保持不变。如果没有给出提交点版本号,则默认使用HEAD。这样分支指向相同,但索引会回滚到最后一次提交,如果使用--hard选项,工作目录也会相同。如果给出了文件名(或-p选项),那么就像使用文件名检出一样工作,除了更新索引。MergeMerge命令将不同的分支合并在一起。在合并之前,索引必须与当前提交相同。如果另一个分支是当前提交的祖父分支,则合并命令将不执行任何操作。另一种情况是当前提交是另一个分支的祖父母,导致快进合并。指向只是简单的移动一下,就产生了一个新的commit。否则就是真正的合并。默认情况下,当前提交(如下所示的ed489)在三向合并[4]中与另一个提交(33104)及其共同的祖父母(b325c)合并。结果是先保存当前目录和索引,然后与父节点33104进行新提交。CherryPickcherry-pick命令“复制”一个提交节点,并在当前分支上进行相同的新提交。Rebase是合并命令的替代方法。Merge将两个父分支合并为一个提交,并且提交历史是非线性的。Rebasing在当前分支上重放另一个分支的历史,commit历史是线性的。本质上,这是线性化的自动挑选。上面的命令都是在topic分支执行的,不是master分支,在master分支上replay,把branch指向新的节点。请注意,旧提交不会被引用并将被回收。要限制回滚范围,请使用--onto选项。以下命令在master分支上重播当前分支从169a6开始的最后几次提交,即2c33a。还有gitrebase--interactive可以让你更轻松地完成一些复杂的操作,例如丢弃、重新排列、修改和合并提交。没有图片来反映这些,详情请看这里:git-rebase(1)[5]。技术描述文件的内容实际上并不存储在索引(.git/index)或commit对象中,而是以blob的形式存储在数据库(.git/objects)中,并通过SHA校验-1值。索引文件按标识符列出相关的blob文件和其他数据。对于提交,它以树的形式存储,也由其哈希值标识。树对应工作目录下的文件夹,树中包含的树或blob对象对应对应的子目录和文件。每个提交存储其父树的ID。如果使用分离的HEAD提交,则最后一次提交将由HEAD的reflog引用。但它会在一段时间后失效并最终被回收,很像gitcommit--amend或gitrebase。