如何解决新分支只有一些未提交的更改 长:为什么这是答案要了解的分支名称 git checkout还有另一个窍门
我正在尝试使用一些未提交的更改来创建新分支,是否可以仅将某些更改导入到新分支中?
解决方法
Matt's point above是,根本没有任何“未提交的更改”。 git status
报告“为登台所做的更改”和“没有登台进行的更改”,该报告本身就是一种谎言,旨在使Git更加可用。 (是否真的实现了这完全是另一个问题。)
最后,这意味着解决方案很简单:只需创建新分支,添加所需的内容,然后运行git commit
。 (请勿对-a
使用git commit
选项。)
长:为什么这是答案
了解所有这些的关键部分是:
- Git与提交有关。
- 商品包含快照,而不是差异。
- Git从建议的快照进行新提交,该快照位于Git所谓的 index ,临时区域,和(很少有这些天)缓存。这三个名称都是同一件事。
- 在工作树中可以查看和使用的文件与Git索引中的副本 1 分开。
最后一点是,当您进行提交时,每个“活动”文件实际上有三个副本:
-
一个副本严格是只读的,存储在当前提交中。您可以让Git读出来,但是没有任何东西-甚至Git本身都无法更改。
-
第二个副本(请参见脚注1)位于Git的索引中。
-
您在工作树中看到并使用的副本实际上并未由Git本身使用(但请参见下文)。 Git将在
git checkout
上将已提交的文件复制到Git的索引和您的工作树中。如果您更改了工作树副本,则新的git commit
将不会使用更新后的文件。相反,它将提交Git索引中的副本。
这就是为什么在提交之前始终必须git add
个文件的原因。 2 git add
步骤告诉Git:将此文件的工作树副本复制到您的索引副本。作为此复制过程的一部分,Git实际上准备了该文件的即将提交的版本(它是一种特殊的,Git格式的只读格式,并且已预先删除重复数据)因此,所有使用相同内容的提交,只需重新使用准备好的文件即可。
换句话说,在任何时候索引中的内容是将在您提交的 next 提交中的文件集。这直接导致git status
告诉您的内容:
-
首先,
git status
打印出on branch master
之类的内容以及其他有用的内容,在这里我们将忽略它们。 -
接下来,对于当前提交中的每个文件,Git 将该文件与索引中的文件进行比较。如果两者相同,Git则什么也没说。如果它们不同,则Git会说该文件已被暂存以进行提交。如果索引中缺少该文件,则Git表示已将其删除;如果索引中的文件不在提交中,则Git表示这是一个新文件。但是这两种方式都是“暂存提交”的,因为索引包含建议的下一个提交。索引中的某些内容与当前提交中的内容匹配,因此Git只是不提及它。
-
第三,对于 index 中的每个文件,Git将该文件与工作树中的文件进行比较。如果两者相同,Git则什么也没说。如果它们不同,则Git表示该文件未上演提交。如果文件丢失,Git会说它已被删除。
-
最后,在您的工作树中但不在索引中的文件有一种特殊情况。这些是您的未跟踪文件。 Git分别列出了它们。 (如果它们在
.gitignore
中列出,则Git会阻止有关未跟踪文件的投诉。)
因此,每当Git谈论更改时,无论是分阶段的还是非分阶段的,这实际上意味着文件的各种副本是不同的。从字面上看,提交中的副本是无法更改的:它们一直冻结,或者至少在提交本身存在的时间内被冻结。索引中的副本采用冻结和重复数据删除的格式,但是可以通过覆盖来更改 ; git add
就是这样做的。您可以根据需要处理工作树中的副本。除了git add
将这些文件复制到Git的索引中,以及git checkout
用一些提交中的文件替换您的工作树文件之外,您还可以根据需要使用该工作树。
1 从技术上讲,索引包含对已删除重复项的内部Git blob对象的引用,而不是文件的副本。提交还在内部使用这些Blob对象,因此这就是文件的索引“副本”准备就绪的原因。
2 当您使用git commit -a
时,Git本质上为您完成了所有文件的git add
。本文中的 essentially 一词是Git这样做的方式,即在提交本身失败的情况下使用额外的临时索引。这个额外的索引使Git可以“撤消”该特定情况的添加。如果使用git commit --only
,事情会变得更加复杂:这时,Git生成了两个临时索引文件。在这里,我们将忽略这种情况。
请注意,git add
中的git commit -a
与git add -u
大致相同:它仅对Git 已经在其索引中的文件起作用。如果您有一个全新的文件,但Git的索引中还不存在,则您仍然必须手动git add
进行该操作。
要了解的分支名称
我在上面提到,提交包含快照,正如我们刚刚看到的那样,快照是根据运行git commit
时Git索引中文件的副本构建的。但是还有更多关于每次提交的知识。每个数字都有编号,并具有随机外观(但实际上根本不是随机的)哈希ID 。 Git在内部使用这些数字从其“所有内部哈希对象”的大型数据库中读取实际的提交数据。
因此,要查找提交,Git需要其哈希ID。但是每个提交本身都具有其前一个直接提交的哈希ID,Git将其称为提交的 parent 。这意味着如果我们有很长的提交链,我们可以像这样绘制它:
... <-F <-G <-H
其中每个大写字母代表一个大的丑陋哈希ID。链中的 last 提交是提交H
,在提交H
内部,Git拥有每个文件的完整快照, plus 一些信息有关提交本身的信息:提交者和时间,例如,上一个提交G
的哈希ID。
通过读取提交H
,Git可以获得早期提交G
的ID。将G
中的快照与H
中的快照进行比较,Git会向您显示在这两次提交之间发生了什么更改。同样重要的是,由于提交G
包含较早提交F
的哈希ID,因此Git可以使时间回到过去。 F
依次包含更早提交的哈希ID,依此类推。
这里最棘手的部分是Git必须以某种方式找到提交H
。这是分支名称的来源。分支名称仅保存链中 last 提交的哈希ID。所以我们最终得到:
...--F--G--H <-- master
名称 master
使Git可以轻松找到提交H
。该提交使Git可以找到每个较早的提交。在Git中,历史就是这样工作的:都是后退。我们只是从头开始,然后根据需要向后工作。
要创建新的分支名称,必须选择一些现有的提交。通常,您将从当前提交开始,这是当前分支上的最后一次提交。 git branch
或git checkout -b
命令将创建新的分支名称,以便选择相同的提交:
...--F--G--H <-- master,newbranch
现在我们还需要一件事,那就是一种方法来告诉我们正在使用的这两个名称中的哪个-git status
会在显示on branch B
时显示,某些 B
。因此,我们将特殊名称HEAD
附加到一个分支名称。如果我们在分支机构master
上,我们将:
...--F--G--H <-- master (HEAD),newbranch
git checkout
还有另一个窍门
请注意,两个名称选择相同的提交!当我们git checkout newbranch
时,我们得到:
...--F--G--H <-- master,newbranch (HEAD)
,但我们仍在使用提交H
。这仍然是当前的提交,并且由于我们没有更改提交,因此git checkout
不会触及Git的索引或工作树。这意味着我们可以像创建git checkout -b
一样创建一个新分支并切换到该分支,而不会弄乱其他任何状态。
现在我们有了这个新分支,并且正在使用它,现在,我们可以使用git add
(甚至是git add -p
)来有选择地更新Git索引中的特定文件。当我们运行git commit
时,Git将打包其索引并进行新的提交I
:
...--F--G--H <-- master
\
I <-- newbranch (HEAD)
一旦Git进行了新提交(从当时Git索引中的内容开始),Git便将新提交的哈希ID写入当前分支,即newbranch
,因为HEAD
是附加到newbranch
。现在,该名称标识了分支上的最新提交。
(如果愿意,现在通常可以切换回master
。这里的关键是通过将未暂存的文件写入Git的索引以使其暂存,然后将索引写入到提交中,这三个这些文件匹配。只有索引副本和工作树副本不同的不需要文件需要替换。有关更多详细信息,请参见{ {3}}。)
其中一些命令有些不透明(其中两个具有破坏性)。如果您不确定他们会做什么,那么在尝试之前,请先阅读本部分内容。
git add some_file some_directory
git add只能按文件或目录添加(实际上,常见的git add .
只是将当前目录添加为目录)。
git commit -m "temp"
创建一个我们将丢弃的临时提交。
git add .
添加其他所有内容,以便我们可以将其隐藏起来。
git stash
将当前的工作状态存储在缓冲区中(我们将在稍后将其恢复)。
git checkout -b some-branch
将当前的工作树(包括“ temp”提交)带到所需的新分支。
git checkout -
“-”是用于返回到您所在的上一个分支的快捷方式。
git rev-parse HEAD^^ | pbcopy
这是一堆。 。 。并且最好是单独尝试一下。
git rev-parse HEAD ^将返回当前分支的最新提交信息。
添加第二个类似HEAD ^^的^然后将引用第二个^。 。 。等
管道到pbcopy(MacOS上的系统剪贴板)只是为了方便。您可以省略此部分,而只需突出显示终端的输出即可。
git reset --hard <some_sha>
:警告:破坏性行动:警告:
这会将当前的工作状态恢复为所需的提交(删除所有其他内容)。
在这种情况下,我们将其他所有内容都存储在存储中,这样就可以了。
git stash pop
带回我们存储在储藏物中的东西。
在Mac上,这是一种方法(虽然非常手动):
git add <file(s)>
git commit -m "temp"
git add .
git stash
git checkout -b new-branch
git checkout -
git rev-parse HEAD^^ | pbcopy
git reset --hard <paste from clipboard>
git stash pop
将您留在最后的状态:
您现在有了一个分支“ new-branch”,其中在“临时”提交下提交了代码更改。
您的主分支没有新分支中的更改。
如果我决定我在分支上所做的工作不是我要开发的功能的方向,通常我会使用类似这样的流程。 。 。但是也许我不想完全删除代码(以防万一以后需要)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。