我想在使用一些未提交的更改的同时创建一个新分支,能否只将其中一部分更改导入到新分支中?
我想在使用一些未提交的更改的同时创建一个新分支,能否只将其中一部分更改导入到新分支中?
Matt's point above是没有任何“未提交的更改”。虽然git status
报告“已暂存提交的更改”和“未暂存提交的更改”,但这个报告本身就是一种谎言,旨在使Git更易用。(它是否真正实现了这一点是另一个问题。)
最终意味着解决方案很简单:只需创建新分支,添加任何你喜欢的内容,然后运行git commit
。(不要使用-a
选项来执行git commit
。)
理解所有这些的关键部分是:
最后一点的意思是,当您使用提交时,实际上每个文件有“三个”副本处于“活动”状态:
一个副本是严格的只读的,存储在当前提交中。您可以让Git读取它,但没有什么(甚至不是Git本身)可以更改它。
第二个副本(参见脚注1)驻留在Git的索引中。
您在工作树中看到和使用的副本实际上并不被Git本身使用(但请参见下文)。在git checkout
时,Git会将已提交的文件复制到Git的索引和您的工作树中。如果您更改了工作树副本,则新的git commit
不会使用更新后的文件,而是提交Git索引中的副本。
这就是为什么您总是需要在提交之前git add
文件。2 git add
步骤告诉Git:将我的工作树副本复制到您的索引副本中。 作为此复制过程的一部分,Git实际上准备了要提交的文件版本(它以一种特殊的、Git化的只读格式存在,并且进行了预先去重处理,因此所有使用相同内容的提交都会简单地重用准备好的文件)。
git status
如何告诉你它告诉你的内容。
首先,git status
会输出一些东西,比如 on branch master
,和其他有用的信息,在这里我们将忽略它们。
其次,对于当前提交中的每个文件,Git 会将该文件与索引中的文件进行比较。如果两者相同,Git 不会做任何提示。如果它们不同,Git 会说该文件已经被 暂存以备提交。如果索引中缺少该文件,Git 会说它已被删除,如果索引中的文件不在提交中,Git 会说它是一个新文件。但无论哪种情况,这都是“暂存以备提交”,因为索引包含了建议的下一次提交的内容。索引中包含的内容与当前提交中的一些内容匹配,因此 Git 就不会提到它们。
第三,对于索引中的每个文件,Git 将该文件与您的工作目录中的文件进行比较。如果两者相同,Git 不会做任何提示。如果它们不同,Git 会说该文件未被暂存以备提交。如果该文件已丢失,Git 会说它已被删除。
最后,对于工作目录中存在但不在索引中的文件,有一个特殊情况。这些是您的 未跟踪文件。Git 会单独列出它们。(如果它们在 .gitignore
中列出,Git 就不会提到这些未跟踪文件。)
git add
的作用。工作树中的副本则可以按照您的喜好处理。除了 git add
将其复制到 Git 的索引中,以及 git checkout
用某个提交中的文件替换您的工作树文件之外,工作树由您自己随意操作。
1技术上讲,索引包含对去重后的内部Git blob对象的引用,而不是文件的副本。提交也使用这些blob对象内部,因此索引中的文件“副本”可以准备提交。
2当您使用git commit -a
时,Git基本上会为您添加所有文件。 这里的基本上掩盖了Git执行此操作的方式,即使用额外的临时索引,以防提交本身失败。 这个额外的索引允许Git“撤消”该特定情况下的添加。 如果您使用git commit --only
,事情会变得更加复杂:此时Git会制作两个临时索引文件。 我们将在此处忽略此情况。
git commit -a
中的 git add
大致相当于 git add -u
:它仅对 Git 索引中已有的文件进行操作。如果您有一个全新的文件,它还不存在于 Git 的索引中,则仍需手动添加该文件:git add
。
如上所述,提交包含快照,正如我们刚刚看到的那样,它们是从 Git 索引中的文件副本构建的,当您运行 git commit
时。但是,关于每个提交还有更多需要了解的内容。每个提交都有一个编号,具有看似随机(但实际上并不随机)的哈希 ID。Git 在内部使用这些数字来从其“所有内部散列对象”的大型数据库中读取实际的提交数据。
因此,要查找提交,Git 需要其哈希 ID。但是,每个提交本身都包含其直接前身提交的哈希 ID,Git 将其称为提交的父级。这意味着,如果我们有一长串提交,我们可以像这样绘制它:
... <-F <-G <-H
H
,在提交H
中,Git拥有每个文件的完整快照,以及关于提交本身的一些信息:例如,谁在什么时候做了它,以及前一个提交G
的哈希ID。H
,Git可以获取先前提交G
的ID。将G
中的快照与H
中的快照进行比较,这是Git显示在这两个提交之间发生了什么变化。同样重要的是,这使Git可以回溯到过去,因为提交G
包含更早提交F
的哈希ID。而F
又包含更早提交的哈希ID,以此类推。H
。这就是分支名称的作用。分支名称仅保存链中最后提交的哈希ID。因此我们最终会得到:...--F--G--H <-- master
< p > < em > 名称 master
让 Git 轻松找到提交 H
。该提交使 Git 找到每个早期提交。这就是 Git 中历史记录的工作方式:它全部是 向后 的。我们只是从结尾开始,然后根据需要向后工作。
要创建新的分支名称,您必须选择某个现有的提交。通常,您将从当前提交开始,这是当前分支上的最后一个提交。 git branch
或 git checkout -b
命令将创建新的分支名称,以便选择相同的提交:
...--F--G--H <-- master, newbranch
git status
将打印出一个名字,其中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)
newbranch
),因为HEAD
指向newbranch
。现在,该名称标识了该分支上最新的提交。master
。关键在于通过将未暂存的文件写入Git的索引中以使它们处于暂存状态,然后将索引写入提交,所有这三个文件都匹配。只有在不需要替换的文件中,索引副本和工作树副本才会有所不同。有关更多详细信息,请参见 在当前分支存在未提交更改时检出另一个分支。)git checkout
通过拥有太多模式和选项来搞乱它:如果您开始使用新的 git switch
,则所有内容在 Git 2.23 中都更加清晰)。特别是,在从提交 a12345(或其他)切换到提交 9876f(或其他)之前,checkout/switch 将确保,如果它要替换工作树或索引中的文件 F,则文件 F 在提交中被保存。如果没有,您将收到一个投诉,说切换到另一个提交不安全。有关详细信息,请参见“带有未提交更改的检出另一个分支”发布。 - torek这些命令有些比较晦涩难懂(其中两个是破坏性的)。如果您不确定它们的作用,最好在尝试之前先阅读本部分。
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^ 将返回当前分支最近的提交 sha。
添加第二个 ^,如 HEAD^^,将引用第二个提交。等等。
将其导入到 pbcopy(MacOS 上的系统剪贴板)只是为了方便。您也可以省略此部分,只需在终端中突出显示输出即可。
git reset --hard <some_sha>
:warning: 破坏性操作 :warning:
这将使当前工作状态返回到所需的提交(删除其他所有内容)。
在这种情况下,我们将其他所有内容存储在 stash 中,所以没问题。
git stash pop
取回我们存储在 stash 中的东西。
以下是在 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”的分支,其中包含提交的代码更改,这些更改位于“temp”提交下。
而且您的主分支没有新分支中包含的更改。
如果我觉得在分支上工作的方向不是我正在开发的功能所需要的,那么我通常会使用类似于这样的流程……但也许我不想完全删除代码(以防万一以后还需要它)。
git stash pop
只是在清理我们在第三行创建的临时存储。 - j7hoenixapply
这个函数,真是太棒了,谢谢。 - j7hoenix