首页 > 解决方案 > git checkout HEAD 和 git checkout

问题描述

发出命令 'git fetch' 后,我需要使用 'git checkout origin/master file_xyz' 或 git checkout HEAD origin/master file_xyz,用来自 origin/ 的文件副本更新文件 'file_xyz' 中的更改掌握?我当前的工作分支与 origin/master 不同。'git fetch' 会更新本地存储库和暂存区吗?

标签: git

解决方案


下面的粗体和斜体是我的,我假设你的意思是你在问什么,但我也怀疑你不太明白你在这里暗示什么。

发出命令后git fetch,是否需要使用git checkout origin/master file_xyz或使用来自的文件副本git checkout HEAD origin/master file_xyz更新文件中更改file_xyzorigin/master

这些都不对,尽管第一个是最接近的。您可能想使用git merge,但这在提交级别而不是文件级别运行。

我当前的工作分支不同于origin/master. 会git fetch更新本地存储库和暂存区吗?

不。此外,您真的不希望它更新 Git 调用的任何条目、各种、索引暂存区域缓存(取决于 Git 的谁或哪个部分正在执行所说的调用)。要做的工作git fetch是调用其他 Git,然后从该 Git 下载,无论他们有什么提交,而你没有。下载了这些提交之后——现在你也有了它们——你自己的 Git 将更新你的远程跟踪名称,例如origin/master,以记住哪些提交它们的分支名称,例如它们的 master,标识。请参阅下面的分支描述。

更改与快照

您的问题首先是询问某些文件中的更改。值得强调(尽可能多次!)Git不存储更改。Git 存储快照。如果您随着时间的推移拍摄多个快照,并比较两个快照,您可以找出发生了什么变化。这就像那些“找出差异”难题之一,这里的关键实现是您必须拍摄两张快照才能找到一些差异。一个快照只是一个快照:没有变化

分支和提交

Git 中的分支这个词是模棱两可的,或者至少,这是人们使用它的方式。它可以引用name,例如master,或一个特定的提交(例如,32a38237f30759f18b72d069aebd81bbde47bbec如果那是 的 提示master),甚至可以引用以最后一次提交结尾的整个系列的提交。看看我们所说的“分支”到底是什么意思? 某人的意思通常很清楚,但如果不是,最好找出:他们的意思是nametip commit,还是可以从tip commit 获得的部分或全部提交?

您说我当前的工作分支不同于,origin/master如果“工作分支”是指报告时报告的分支名称,那是微不足道的:git status

On branch develop
Your branch is ...

例如。那是因为您不能“启用”任何远程跟踪名称:远程跟踪名称,例如origin/master,实际上并不是分支名称,并且git checkout不会让您“启用”这样的名称。相反,你最终会进入 Git 所说的“分离 HEAD”模式(我不会在这里介绍)。

如果你一个分支上——如果git status会说一些——那么这个名字会标识一个特定的提交,那个提交就是你当前的提交。这可能是识别之前和/或之后的相同提交。或者,它可能是一些不同的提交。On branch BBBorigin/mastergit fetch

Git 中的提交是一个唯一的实体,通过其哈希 ID 进行识别和访问。哈希 ID 是一个由字母和数字组成的大而难看的字符串,例如32a38237f30759f18b72d069aebd81bbde47bbec,它看起来是完全随机的(尽管它不是随机的:它实际上是提交内容的加密校验和)。每个提交都有自己的、不同的、唯一的哈希 ID。如果两个不同的 Git 存储库都有一个具有相同哈希 ID 的提交,则它们包含相同的提交。这就是你的 Git 知道的方式,当你调用另一个 Git 时,它是否需要下载一些提交:另一个 Git 说,例如,我有提交32a38237f30759f18b72d069aebd81bbde47bbec,你的 Git 检查并说:好的,给我或者不,我已经有那个了

这些哈希 ID 对人类几乎没有用处,因此,我们为特定的提交命名。这就是分支名称的来源:他们的 Git 调用他们最新的 master-branch commit master他们的名字master标识了他们最近的提交。此提交可能在您的存储库中,也可能不在。你运行git fetch,你的 Git 调用他们的 Git,你的 Git 找出他们的哈希 ID,如果需要,你的 Git会得到提交。现在你肯定拥有它,现在你的 Git 更新了你origin/master要记住的:他们的 Git 最后说他们master是提交 __(填空)__。

每次提交都会存储所有文件的完整快照。它们是一种特殊的、压缩的、冻结的、仅限 Git 的形式。因为它们被冻结,Git 可以在一系列提交中共享文件,只要它们与之前的提交相比没有变化。您不能更改 Git 中任何冻结实体的任何部分——包括提交。每个提交还存储一些元数据,例如谁提交、何时提交以及为什么提交(日志消息)。

提交还存储其父提交或先前提交的哈希 ID。这允许 Git 从最后开始——在分支中的最后一次提交——并向后工作:

... <-F <-G <-H   <--last

分支名称(例如)last包含我们将调用的提交的实际哈希 ID H。这让 Git 自己找到提交。提交保存其父级的哈希 ID G,让 Git 查找GG保存其 parent 的哈希 ID F,依此类推。

归根结底,这就是分支在 Git 中的工作方式和原因。该名称标识最后一次提交,其余的提交是通过向后工作找到的。

索引和工作树

提交中的文件被冻结并且仅限 Git,这对于归档来说很好,但对于完成工作毫无用处。因此,Git 必须有一种方法可以将提交中的文件提取到您可以处理和使用它们的区域中。该区域是工作树

这已经足够了:你有每个文件的当前冻结副本,保存在提交中,每个文件的工作副本,在你的工作树中,你可以使用它。你可以告诉 Git:从我的工作树中进行新的提交,Git 会重新压缩每个文件,将其与当前压缩的文件进行比较,并查看它是否需要保存新副本或可以重新使用旧副本。当它最终完成压缩每个文件时,它就可以进行新的提交了。这将是有效的,但速度很慢——例如,对于 Linus Torvalds 来说太慢了。所以这不是 Git 所做的。

相反,Git 保留每个文件的第三份副本。第三个副本已经压缩并准备好进入下一次提交。但与当前提交中的副本不同,它并没有完全冻结。它刚刚准备好冷冻。如果您对工作树中文件的可用副本进行了某些操作,则可以运行将工作树副本复制回“准备就绪”副本。该文件现在暂存于 commit。请注意,它之前已经存在,只是之前存在的副本与当前冻结提交中(仍然)的副本相同。该过程只是用新的更新的过程覆盖它。git add filenamegit add

这个区域保存了 Git 提取并放入工作树的提交中的所有文件,并准备好进入下一次提交(换句话说,暂存区)被称为索引暂存区缓存,这取决于谁或 Git 的哪个部分正在执行调用。因此索引/暂存区的目的是记住下一次提交的内容

保存新快照

在某些时候,您可能会file_xyz使用您的工作树并对其进行处理。您现在可能想要保存该内容以供将来参考。例如,您将能够将其与提交之前的提交版本进行比较,以查看您所做的更改。您拍摄两个快照——新提交的父级和新提交——并比较它们并发现差异。

为此,您运行git add file_xyz,然后git commit. commit 命令收集你的日志消息——<em>你为什么做了你所做的一切——并添加你的名字和当前时间等等,保存当前提交的哈希 ID,并从中进行新的提交:

...--F--G--H   <-- [before your new commit]
            \
             I   <-- [your new commit]

为了记住 的哈希 IDI,您的 Git 现在将该哈希 ID 写入您已签出的分支的名称中。如果是这样master,你现在有:

             I   <-- master (HEAD)
            /
...--F--G--H   <-- origin/master

该名称附加到HEAD当前分支,这是Git 知道它应该更新的方式。那也是怎么知道的说法。您大概创建了您的from —他们的Git 作为他们的提交 —所以您仍然记得 hash ID 。mastergit statusOn branch mastermasterorigin/master masterorigin/masterH

获取他们的新提交

现在你运行git fetch. 你的 Git 调用他们的 Git 并获得他们的新提交。假设他们有这样一个:

             I   <-- master (HEAD)
            /
...--F--G--H--J   <-- origin/master

由于他们master现在标识了提交J,因此 origin/master现在也标识了他们的新提交J

合并更改

此时可以运行:

git merge origin/master

这告诉你的 Git:找到共同的起点提交,我从中做出一些更改并提交,以及他们从中做出一些更改并提交。找出我们做了哪些更改,然后自动组合它们。

I所以你的 Git 从和 从向后走J,发现你们都是从 commit 开始的H。这是合并基地

然后你的 Git 或多或少地运行:

git diff --find-renames <hash-of-H> <hash-of-I>   # what we changed
git diff --find-renames <hash-of-H> <hash-of-J>   # what they changed

如果您更改file_xyz而他们没有更改,或者如果他们更改而您没有更改,那么组合更改很容易:Git 可以获取您的版本或他们的版本。如果您更改了文件,Git 会逐行比较文件的合并基本版本(来自提交H)与您的和他们的。然后它结合两组更改,将合并的更改应用于文件 from H,并将其用于合并结果。

如果所有文件的所有组合都顺利进行,Git 会根据结果进行新的提交:

             I--K   <-- master (HEAD)
            /  /
...--F--G--H--J   <-- origin/master

你的名字master现在指向这个新的提交,它结合了他们的更改和你的更改。新的提交有两个父项——提交I,你的前一个分支提示,和提交J,他们当前的分支提示——并且有作为它的内容,包括它的 frozen file_xyz合并的变化应用到H,合并基础。

如果您真的想放弃自己的更改

毕竟,如果您想放弃更改file_xyz并只使用它们的 s,那么您需要建议的第一个命令:

git checkout origin/master file_xyz

这告诉你的 Git:找到提交origin/master标识。进入该提交并获取冻结的内容。将这些冻结的内容放入我的索引中,以便它们将在我的下一次提交中,并将它们解冻/解压缩到我的工作树中,以便我可以查看和使用它们。 您当前的提交根本没有改变,但您的索引和工作树现在保存了该版本的文件。

请注意,如果您想从 commit 回到保存的版本,同样的技巧也有效H

git checkout <hash-of-H> -- file_xyz

由于 commit 没有明显的名称H,您可能希望使用它的哈希 ID。这里--不是必需的——我们以前不需要它——但这是一个好习惯,因为虽然file_xyz看起来不像分支名称或git checkout选项,但其他名称可能。如果您有一个名为 的文件-f,或一个名为 的文件master,则--说明git checkout您正在命名一个文件,而不是选项或分支。


推荐阅读