Matt 上面的观点是根本没有任何“未提交的更改”。虽然git status 报告“为提交而暂存的更改”和“未为提交而暂存的更改”,但这份报告本身就是一种谎言,旨在使 Git 更易于使用。(它是否真的实现了这完全是另一个问题。)
这最终意味着解决方案很简单:只需创建新分支,添加您喜欢的任何内容,然后运行git commit. (不要使用该-a选项git commit。)
理解所有这些的关键部分是:
最后一点的意思是,当您处理提交时,实际上每个文件都有三个“活动”副本:
一个副本是严格只读的,存储在当前提交中。你可以让 Git 读出它,但没有任何东西——甚至 Git 本身——都不能改变它。
第二个副本(见脚注 1)位于 Git 的索引中。
您在工作树中看到和使用的副本实际上并未由 Git 本身使用(但请参见下文)。Git 将在 上git checkout,将提交的文件复制到 Git 的索引和您的工作树。如果您更改了工作树副本,新的git commit将不会使用更新的文件。相反,它会提交 Git 索引中的副本。
这就是为什么你总是必须git add在提交之前归档。2 该git add步骤告诉 Git:将此文件的我的工作树副本复制到您的索引副本中。 作为这个复制过程的一部分,Git 实际上准备了要提交的文件版本(它是一种特殊的、Git 化的、只读的格式,并且预先去重,以便所有使用相同内容,只需重新使用准备好的文件)。
换句话说,索引中的内容,在任何时间点,都是将在您将进行的下一次提交中的文件集。这直接导致如何git status告诉您它告诉您的内容:
首先,git status打印诸如on branch master和其他有用的内容,我们将在此处忽略这些内容。
接下来,对于当前提交中的每个文件,Git将该文件与 index 中的文件进行比较。如果两者相同,则 Git 什么也不说。如果它们不同,Git 会说该文件是为 commit 暂存的。如果文件在索引中丢失,Git 说它已被删除,如果索引中的文件不在提交中,Git 说它是一个新文件。但无论哪种方式,这都是“暂存以进行提交”,因为索引包含建议的下一次提交。索引中的一些内容与当前提交中的内容相匹配,因此 Git 只是没有提及它。
第三,对于index 中的每个文件,Git 将该文件与工作树中的文件进行比较。如果两者相同,则 Git 什么也不说。如果它们不同,Git 会说该文件未暂存为 commit。如果文件丢失,Git 会说它已被删除。
最后,对于在您的工作树中但不在索引中的文件,有一种特殊情况。这些是您未跟踪的文件。Git 分别列出了它们。(如果它们被列在 中.gitignore,Git 会抑制这个关于文件未被跟踪的抱怨。)
因此,每当 Git 谈论更改时,无论是暂存的还是未暂存的,它的真正含义是文件的各种副本是不同的。提交中的副本实际上无法更改:它们一直被冻结,或者至少,只要提交本身存在。索引中的副本采用冻结和重复数据删除格式,但可以通过覆盖它们来更改;就是git add这样。您的工作树中的副本可以随心所欲地处理。除了git add将它们复制到 Git 的索引中,并git checkout用来自某个提交的文件替换您的工作树文件之外,工作树是您可以随意使用的。
1从技术上讲,索引包含对去重的内部 Git blob 对象的引用,而不是文件的副本。提交也在内部使用这些 blob 对象——所以这就是文件的索引“副本”准备提交的原因。
2当您使用 时git commit -a,Git 基本上会git add为您处理所有文件。这里的词本质上是关于 Git 执行此操作的方式,即使用额外的临时索引,以防提交本身失败。这个额外的索引允许 Git 为特定情况“撤消”添加。如果您使用git commit --only: ,事情会变得更加复杂:此时 Git 会生成两个临时索引文件。我们将在这里忽略这种情况。
请注意,git addfromgit commit -a大致相当于git add -u:它只对 Git索引中已有的文件进行操作。如果你有一个全新的文件,它还不存在于 Git 的索引中,你必须git add手动保留那个文件。
我在上面提到,提交包含快照,正如我们刚刚看到的,它是根据您运行 .git 文件时位于 Git 索引中的文件副本构建的git commit。但是关于每次提交还有更多的信息需要了解。每个都有编号,带有一个看起来随机(但实际上根本不是随机的)的哈希 ID。Git 在内部使用这些数字从其“所有内部散列对象”的大数据库中读取实际提交数据。
因此,要查找提交,Git 需要其哈希 ID。但是每个提交本身都有其前一个提交的哈希 ID,Git 将其称为提交的父项。这意味着如果我们有很长的提交链,我们可以这样绘制:
... <-F <-G <-H
Run Code Online (Sandbox Code Playgroud)
其中每个大写字母代表一个丑陋的大哈希 ID。此处链中的最后一次提交是 commit H,在 commit 中H,Git 拥有每个文件的完整快照,以及有关提交本身的一些信息:例如,谁提交以及何时提交,以及上一次提交的哈希 ID G.
H然后,通过读取 commit ,Git 可以获得较早的 commit 的 ID G。将快照输入与快照输入G进行比较H是 Git 向您展示这两次提交之间发生了什么变化的方式。同样重要的是,这让 Git 回到过去,因为 commitG包含较早 commit 的哈希 ID F。 F依次包含更早提交的哈希 ID,依此类推。
这里棘手的部分是 Git 必须以H某种方式找到提交。这就是分支名称的用武之地。分支名称仅包含链中最后一次提交的哈希 ID 。所以我们最终得到:
...--F--G--H <-- master
Run Code Online (Sandbox Code Playgroud)
这个名字 master让 GitH很容易找到提交。那个提交让 Git 找到每个更早的提交。在 Git 中,历史就是这样运作的:一切都是倒退的。我们只是从最后开始,然后根据需要向后工作。
要创建新的分支名称,您必须选择一些现有的提交。通常,您将从当前提交开始,这是当前分支上的最后一次提交。该git branch或git checkout -b使其选择相同的commit命令将创建一个新的分支名称:
...--F--G--H <-- master, newbranch
Run Code Online (Sandbox Code Playgroud)
现在我们还需要一件事,这是一种判断我们正在使用这两个名称中的哪一个的方法——git status当它说, for some时会打印出来。因此,我们会将特殊名称附加到一个分支名称上。如果我们在 branch ,我们有:on branch BBHEADmaster
...--F--G--H <-- master (HEAD), newbranch
Run Code Online (Sandbox Code Playgroud)
git checkout 还有一个技巧请注意,两个名称都选择了相同的提交!当git checkout newbranch我们得到:
...--F--G--H <-- master, newbranch (HEAD)
Run Code Online (Sandbox Code Playgroud)
但我们仍在使用 commit H。这仍然是当前提交,并且由于我们没有更改 commits,因此git checkout不会触及 Git 的索引或我们的工作树。这意味着我们可以创建一个新分支并切换到它,就像使用 一样git checkout -b,而不会弄乱任何其他状态。
现在我们有了这个新分支并正在使用它,现在我们可以使用git add(甚至git add -p)有选择地更新 Git 索引中的特定文件。当我们运行时git commit,Git 会打包它的索引并进行新的提交I:
...--F--G--H <-- master
\
I <-- newbranch (HEAD)
Run Code Online (Sandbox Code Playgroud)
一旦 Git 做出这个新提交(来自当时 Git 索引中的任何内容),Git 就会将新提交的哈希 ID 写入当前分支,即newbranch,因为HEAD附加到newbranch. 所以现在这个名字标识了分支上的最新提交。
(这是现在通常可以切换回master如果我们等。这里的关键是,通过写不分阶段的文件到Git的指数,使他们上演,然后写索引提交,所有三个这些文件相匹配。这只是文件韩元“T需要更换其中索引副本和工作树副本不同。有关这个有很多更详细,请参阅结帐时,有对当前分支未提交的更改另一个分支。)
| 归档时间: |
|
| 查看次数: |
180 次 |
| 最近记录: |