合并2个分支,但不删除文件

时间:2019-03-15 14:25:38

标签: git github

我从master分支创建了一个分支,比方说,A。在A分支上,我删除了一些文件并对其他文件进行了一些更改。现在,我希望在不删除文件的情况下将A合并到master中。我该如何实现?

2 个答案:

答案 0 :(得分:3)

您在这里有很多选择。有些方法对某些目的更好,而其他方法对其他目的更好。我要猜测,哪个是最适合您的,但这是一个猜测。我将向您展示一些选项,但不是所有选项。

请记住,Git完全是关于 commits 而不是文件。提交 hold 文件-每个提交都具有该提交中每个文件的完整快照-但是Git与提交有关

每个提交都有唯一的哈希ID,这是一个由字母和数字组成的丑陋字符串。该哈希ID表示那个提交。没有其他提交具有相同的字母和数字字符串。这些就是Git关心的-分支{em>名称,例如masterbranch-A,是给 you 的,而不是给Git的。 Git关心 commits

一旦作出,就不能更改任何提交。每个冻结所有时间。但是您可以进行您已经做出的(或者当然是其他人做出的)提交,并将其提取到工作区中,对工作区进行更改,然后告诉Git进行来自工作区的 新提交。实际上,这就是您使用Git的方式

在正常使用Git的过程中,您可以从以下内容开始:

git checkout master

这告诉Git:让我提交其哈希ID以我的名字master 存储的提交。记住,这个名字是给你的。 Git关心哈希ID。 Git从提交中获取所有冻结的文件,将它们放在工作区(您的工作树)中,现在您可以查看并使用文件,因为它们是不再冻结为Git无法看到的某些内部仅Git格式。

现在您要做一些工作,git add一些文件,依此类推,然后通常将运行git commit。现在是时候观察每个提交的另一个功能了。

每个提交都存储其 parent 提交的原始哈希ID。因此,如果您只提取了master所标识的提交,那么该提交具有一个 parent 提交,该提交具有另一个父级,依此类推。如果我们绘制这些提交,我们将看到类似以下的内容:

... <-F <-G <-H   <-- master

您提取的提交具有一些丑陋的哈希ID H,该ID存储在您的名字master下。该提交(我们称为H的提交)会记住其 parent 提交G的哈希ID。

您刚刚进行了一些更改,然后运行git commit。 Git现在将打包一个 all 文件的新快照,并为其赋予一个新的唯一哈希ID。我们将其简称为哈希ID I。新的提交I将记住提交H作为其父项。让我们来画一下:

... <-F <-G <-H   <-- master
               \
                I

现在是棘手的部分!哈希ID太难让人记住了,因此为了帮助我们,Git将以某种名称存储I的哈希ID。我们将使用的名称是给git checkout的名称。因此,Git将更新我们的master以指向新的提交I

...--F--G--H
            \
             I   <-- master

这不是您想要的,所以那不是您要做的。首先,您签出了master。然后您告诉Git:给我起一个新名字branch-A 。 Git做到了,所以现在您有了:

...--F--G--H   <-- master, branch-A

要记住您给git checkout的名字,Git在您使用的名字后附加了一个特殊的名称HEAD(用大写字母表示):

...--F--G--H   <-- master, branch-A (HEAD)

然后您进行新的提交I时,Git知道要更新附加HEAD的名称。所以现在您有了:

...--F--G--H   <-- master
            \
             I   <-- branch-A (HEAD)

现在,我不知道您在这个branch-A分支上所做的很多提交方式。一会儿我画三张。但是在您告诉Git的某个地方:删除一些文件。因此,提交快照IJK或所有三个快照,会完全丢失某些文件:

...--F--G--H   <-- master
            \
             I--J--K   <-- branch-A (HEAD)

不能更改任何这些提交。无论哪个提交缺少那些文件,那些提交都会永远缺少那些文件

但是,如果您没有给予这些承诺给其他人,那么这里有个不错的选择。您可以忘记所有有关这些提交的信息。您可以进行新的 replacement 提交,这些提交是新的和改进的:它们与原始提交非常相似,除了新的替换文件之外,您不要删除文件。< / p>

此选项的主要缺点是您必须为每个此类提交替换一个。但是,有一种简单的方法可以做到这一点。此选项的另一个主要缺点是捕获:如果您没有给予这些内容,则会提交给其他人。记住,Git关心哈希ID。如果您将这些提交提交给其他人,则它们会在存储库中拥有这些提交以及这些哈希ID。

如果其他人确实拥有这些提交,那么这对这里的提议并不完全致命,这仅意味着“其他人”可能必须从旧的提交切换到新的提交。或者,您可能希望完全使用其他选项,在此不再赘述。

无论如何,让我们再次看看您现在所拥有的。记住,我猜测。您将必须拥有自己的外观-我建议运行git log --decorate --oneline --graph branch-A master来做到这一点-但这又是我的画图:

...--F--G--H   <-- master
            \
             I--J--K   <-- branch-A (HEAD)

比方说,您在提交JI中删除了文件。现在您要他们回来。您当前正在提交K,因为您仍在branch-A上,这就是HEAD仍附加到branch-A的原因。

您首先要做的是再进行一次新提交。它将获得一个新的哈希ID,我们将其称为L(实际ID将像通常一样是一些丑陋的字母和数字字符串)。要进行此新提交,请在K结束且所有内容都已清除的情况下保持干净-因此git status说“在分支branch-A上,没有要提交的内容”,您可以将工作区域更改为放回文件

还请记住,每个提交都具有其文件 all 的完整快照。因此,我们将从提交H中取回文件,毕竟,提交git show仍然与往常完全相同-所有提交都会及时冻结!我们只需要查看这些文件。

有几种方法可以做到这一点。一种是使用git show master:path/to/file ,如下所示:

path/to/file

这将在屏幕上显示保存在提交H中的master的内容(您的名字path/to/file指向的提交)。当然,您不想看到它,而是想将其保存回git show master:path/to/file > path/to/file git add path/to/file 。因此,您可以这样做:

git checkout

不过,git checkout master -- path/to/file 有一个奇怪的特征。它具有一种为我们执行此操作的模式,没有实际切换提交和/或分支。所以我们可以这样写:

git show

这等效于git add,因为它创建或替换我们工作区中的文件,git add ,因为它像git commit一样更新 index

(我们没有讨论过索引。索引,也称为暂存区或有时称为 cache ,在Git中非常重要。但是对于现在我没有时间将它放入这个答案。)

无论如何,对所有不应该删除的文件重复此操作后,现在可以运行...--F--G--H <-- master \ I--J--K--L <-- branch-A (HEAD) 制作新的快照:

L

提交J将文件放回。

我们刚刚说过您在I中删除了它们。您必须确保此时 did 哪个提交将其删除,因为现在我们将使用一个巧妙的技巧。我们无法更改任何现有提交的任何内容-JKLI永远都具有这种形式,但是我们可以提取提交,更改工作区,然后进行 new 提交。因此,现在我们将让Git提取提交J,也许进行一些更改,然后再次提交。然后,我们将让Git与K一起工作,这是我们之前通过删除文件来搞砸的地方。此时,我们将Git从H中添加 add ,以将文件 back 放回原处,然后让Git进行新的提交

从提交 I'-J'-K' <-- HEAD / ...--F--G--H <-- master \ I--J--K--L <-- branch-A 开始,我们将让Git完成所有这些。结果如下所示:

I'

I是我们的I的新副本,如果没有其他要求,也许带有不同的日期戳。 (如果我们真的根本不需要更改任何东西,我们可以直接重复使用J',Git会为我们解决这个问题。)然后J是我们的{{1 }} ,但对L进行了更改,将文件放回去,并添加到其中。然后K'是我们的J'的副本。 K'类似于K,但其父级为J',并且其中还有我们在J意外删除的文件。

达到这个必杀技(或其他可能)

实现所有这些,我们使用git rebase选项运行 -i,告诉它从当前分支复制提交({ {1}}),但也位于branch-A上的提交除外,并将新副本放置在master的末尾:

master

Git查看我们的图形:

git rebase -i master

看到这里...--F--G--H <-- master \ I--J--K--L <-- branch-A (HEAD) 上有四个 only 提交。 (提交branch-AF-G-H上,但是它们也在branch-A上。)因此,Git现在将组成一个工作清单:

master

Git将在其中包含这些说明的文件上打开您选择的编辑器(与您选择的用于生成提交消息的编辑器相同)。您的工作是修改指令,并写出修改后的指令并退出编辑器。

由于提交pick <hash-of-I> <subject-line-from-I> pick <hash-of-J> <subject-line-from-J> pick <hash-of-K> <subject-line-from-K> pick <hash-of-L> <subject-line-from-L> 是我们通过删除文件而搞砸的地方,所以我们要做的是通过添加修复 J > J。因此,我们重新安排了工作清单,将L移到了L之后:

J

然后,将关于提交pick <hash-of-I> <subject-line-from-I> pick <hash-of-J> <subject-line-from-J> pick <hash-of-L> <subject-line-from-L> pick <hash-of-K> <subject-line-from-K> 的行上的单词pick更改为单词L。 (如果愿意,您可以在此处改用fixup一词-它们几乎是同一回事,如果这是一个正确的教程,我们将首先在此处进行压扁,但是在这种情况下,您需要squash

如果错误是在fixup上,则在选择提交I的行之后,将fixup使用L。如果错误是I,我们将其行保持原始顺序,只需将最后一个L更改为pick。这里的想法是,提交fixup(将文件放入后退的提交)是针对 fix 的提交,它通过删除破坏了工作首先是文件。

无论如何,我们现在已经在此说明表上使用了我们的编辑器。我们已经完成了说明的更新,因此我们写完工作表并退出编辑器。 Git现在执行指令,给我们:

L

(在拾取和修复过程中,名称 I'-J'-K' <-- HEAD / ...--F--G--H <-- master \ I--J--K--L <-- branch-A 与任何分支名称都是暂时分离。)

如果一切顺利,则此重新设置过程的最后一步现在开始。 Git采用名称 HEAD,这是Git为我们记住提交branch-A的提交哈希ID的方式。它将分支名称附加到上次复制的副本或固定的上,在本例中为L。然后,它重新连接我们的K'。所以现在我们有了这个:

HEAD

我们不再需要原始提交 I'-J'-K' <-- branch-A (HEAD) / ...--F--G--H <-- master \ I--J--K--L [abandoned] 。它们已替换为闪亮的新提交I-J-K-L名称 I'-J'-K'为我们记住了branch-A。如果我们运行K',它看起来 就会以某种方式将错误的提交更改为良好的提交。

我们没有-原始提交仍然存在-但是我们是唯一知道git log用来识别错误提交的人,并且我们拥有唯一的 Git存储库错误的提交。我们从不把它们交给其他人,就宇宙的其余部分而言,它们甚至不存在。

现在我们有了 good 提交,并且如果我们从一开始就不会犯错,我们可以按照合并的方式进行合并。

答案 1 :(得分:0)

您可以使用

  1. git checkout -- <file>... to discard changes in working directory

  2. git add <fileName>git add .(针对当前目录中的所有更改)添加要提交的文件。

  3. git commit -m "Your message"

  4. git push origin master将更改推送到主版本

相关问题