Git仅将文件添加到特定分支

Ast*_*stm -2 git git-branch

如何停止其他分支中的某些文件以在master分支中合并?我有一个在特定分支中添加的文件。我想将此分支合并到master,但我希望master分支忽略与它合并的新文件。

如何通知Git该文件仅与此分支相关?

tor*_*rek 5

Git中没有这样的东西。

分支不是永久的,在许多方面都不重要。恐怕这个答案会很长,因为在这里您需要了解很多背景,并且许多Git简介都不善于传达它。

背景:提交和分支名称

提交很重要:提交是永久的且不可更改1 每个提交都保存一些文件的快照;该组文件那次提交,它们的内容一起,是因为持久不变的承诺本身。

任何一次提交的“真实名称”是其较大的丑陋哈希ID。Git为您添加到存储库中的每个新的唯一提交组成一个新的,唯一的,丑陋的哈希ID。添加后,该提交将是永久的且不可更改(尽管再次参见脚注1)。提交将以您当时所说的形式完全保存您说时Git 跟踪的所有文件git commit(我们稍后会再讨论)。


1提交可以在各种条件下删除,这些条件基本上可以归结为没有人可以找到它们。这比看起来要棘手,最好只是从一开始就将它们视为Git永久保存的东西,该东西可以保存文件快照。尽管一旦您找不到它,提交就可以消失,但是它实际上不能更改,因为它的真实名称(其哈希ID)是提交本身的加密校验和。


提交还记住其父提交。也就是说,每个提交都存储了您之前使用提取的提交的丑陋的哈希ID git checkout。它还存储您的姓名(作为提交的作者和提交者)以及您的电子邮件地址和日期/时间戳记;并存储您的日志消息,告诉所有人您为什么认为进行该特定提交是件好事。

假设我们从只有三个提交的存储库开始。让我们给它们一个大写的字母,而不是笨拙的哈希ID。第一个提交是A,第二个提交是,B依此类推。一旦我们有26次提交,我们将不再能够进行新的提交,但这对我们来说已经足够了。

由于commit A是第一个提交,因此没有父级。如果我们做BA,不过,B记得AB的父,如果我们做CBC记得B。由于提交一旦完成就无法更改,A无法记住BB也无法记住C:此“我记得”的东西,此箭头从较晚的提交指向较早的提交,必须向后移动:

A  <-B  <-C
Run Code Online (Sandbox Code Playgroud)

如果我们C使用A而不是使用该B怎么办?然后C将指向,而不是B,而是A

A--B
 \
  C
Run Code Online (Sandbox Code Playgroud)

但目前,让我们坚持直线的提交:

A--B--C
Run Code Online (Sandbox Code Playgroud)

(在这里我们懒得去画箭头,但实际上这部分是因为我们在使用倾斜的箭头时会遇到问题)。现在,记住这些方便的大写字母代表实际的大丑陋哈希ID,请问自己:我们(或Git)如何记住commit 的实际哈希ID CC记住的ID B,也记住的ID A,因此我们不需要记住这两个。但是我们确实需要记住链中的最后一次提交。

这是分支名称输入图片的地方。 像这样的分支名称master只是用来保存一个提交哈希ID:链的末尾或提示提交。从那里,Git将计算出所有较早的提交。因此,我们来画一下:

A--B--C   <-- master
Run Code Online (Sandbox Code Playgroud)

现在,让我们进行一次新提交。我们称它为,D但实际上它有一些独特的丑陋的哈希ID。我们使运行一下:

git checkout master
... edit some files ...
git add ... some files ...
git commit
Run Code Online (Sandbox Code Playgroud)

我们进行时D,我们已经提交了C检出。因此,C将成为new commit的父对象D。Git会收集您的日志消息,并为所有文件制作快照,而不仅仅是git add-ed 文件;我们将使用您的姓名以及作者和提交者数据的当前时间来稍作讨论,并进行新的,永久的,不可更改的提交DC并将其记住为父项:

A--B--C--D
Run Code Online (Sandbox Code Playgroud)

但是现在这个名字master必须改变。它在记忆C; 现在它必须记住D。因此,最后一步git commit是将D的实际哈希ID写入名称master,从而得到以下结果:

A--B--C--D   <-- master
Run Code Online (Sandbox Code Playgroud)

假设我们现在git checkout回过头来进行commit B,并使其(或更容易地已经拥有)一个指向commit的分支名称B,以便我们可以像这样绘制图形:

     C--D   <-- master
    /
A--B   <-- branch
Run Code Online (Sandbox Code Playgroud)

现在我们再次提交E。Git将照常写出新的提交,制作快照并将E的父级设置为— B因为这是我们已签出的。完成后,branch将指向新提交E

     C--D   <-- master
    /
A--B
    \
     E   <-- branch
Run Code Online (Sandbox Code Playgroud)

Git如何知道移动名称branch而不是移动名称master?这是HEAD进来的。当您运行时:

git checkout branch
Run Code Online (Sandbox Code Playgroud)

Git将您/它的HEAD附加到该分支名称,因此我们应该将其真正记录为:

     C--D   <-- master
    /
A--B
    \
     E   <-- branch (HEAD)
Run Code Online (Sandbox Code Playgroud)

您可能现在想知道哪个分支提交AB处于启用状态。我们可以从图中看到,CDmaster-and了Git知道这一点,因为混帐从开始结束master,提交D,并在后台工作来达到C。我们可以看到这E是的技巧branch。但是哪个分支B在?

Git的答案是这两个提交都在两个分支上。包含达到任何给定提交 的分支集是通过从所有分支名称(在右边经过,或图形的任一端是“最新”端)开始并向后工作来确定的。如果我们可以像这样向后移动来到达提交,则该提交在该分支上。

一般来说,分支机构倾向于增加新的提交。正如我们在上面看到的,添加的提交对branch无效master。我们可以做出更多新的提交:

     C--D   <-- master
    /
A--B
    \
     E--F--G--H   <-- branch (HEAD)
Run Code Online (Sandbox Code Playgroud)

并且master仍然指向D。但是,如果我们git checkout master(附HEADmaster),然后使用git merge合并提交H master,我们得到这样的:

     C--D---------I   <-- master (HEAD)
    /            /
A--B            /
    \          /
     E--F--G--H   <-- branch
Run Code Online (Sandbox Code Playgroud)

现在分支masterI同时返回,因此所有提交都再次打开。D Hmaster

我在这里走得很快,所以到现在为止,您可能需要花一些时间来研究网站Think Like(a)Git,该网站提供了有关此图形工作原理的更多信息。

这就是为什么没有分支特定文件的原因

Git内部的已保存文件(快照)存储在提交中。提交是该提交附带的所有文件的快照。没有该文件的任何提交都不会永久地拥有该文件。任何确实包含该文件的提交,都将永久拥有该文​​件。但是包含该提交的分支集取决于您对分支名称的处理方式。

您可以具有特定于提交的文件,但是这些提交可能突然在许多分支上,尤其是在有人使用之后git merge

索引和工作树,或者快照中的文件来自哪里?

使用Git一段时间后,您将了解提交如何存储文件的冻结版本,并使用git checkout <some old commit>可以将其恢复。上面我们还提到过,Git保存每个文件的快照,或者更精确地说,保存所有跟踪的文件的快照。

由于每次提交(快照)都会保存每个文件,因此Git需要一种使它们不占用太多空间的方法。Git通过压缩文件来部分实现此目的。无需太多技术细节,Git可以压缩一些文件中的内容。这些超压缩文件采用特殊的,仅Git的格式,与提交本身一样永久且不可更改。但这意味着没有其他程序可以处理这些文件。您需要以git checkout一种可以查看,编辑和与其他程序一起使用的格式来删除文件。

因此,Git为您提供了一个称为工作树工作树的区域。这里的文件采用普通格式,而不是某些特殊的仅Git格式。这也很简单。

其他版本控制系统在这里停止:它们具有冻结的提交文件和工作树文件。当您运行hg commit或执行svn commit任何操作时,他们会花费数小时来分析您的工作树中的内容,以弄清楚如何提交。好吧,这实际上不是几个小时,只是感觉就像几个小时。无论如何,它很慢。但是,当您跑步时git commit,它就像闪电一样迅速。Git从Git调用(这里称为index暂存区域缓存)获得了巨大的速度,具体取决于Git文档或命令的调用位。

这里的窍门是,当您git checkout提交时,Git不仅会将所有冻结的文件扩展为工作树中的普通解压缩形式。Git首先将冻结的,Git格式的文件复制到该索引中,使它们保持特殊的仅Git格式,但不冻结它们。只有这样,才将它们解压缩到工作树中。因此,该索引跟踪(又是这个词)工作树中的文件集。

在运行时,Git所做的是将工作树副本压缩为特殊的仅Git格式,然后将其写入index中。如果索引中还有文件的其他版本,那么现在它已从工作树更新为新版本。如果文件以前不在索引中,那么现在。无论哪种方式,文件现在都可以使用了。git add filefile

因此,当您运行时git commit,Git要做的只是冻结index中已经存在的预压缩文件。Git这么快就是这样:它只是重新使用所有那些已经压缩的文件。如果尚未运行git add,则会得到先前签出的提交中的文件相同的版本。

请注意,当Git执行此操作时,也都为下一次提交进行了设置。例如,假设您处于这种状态:

...--F--G   <-- somebranch (HEAD)
Run Code Online (Sandbox Code Playgroud)

因为你跑了git checkout somebranch。索引和工作树现在充满了commit的所有文件G。您修改一些工作树文件,运行git add以将修改后的文件复制回索引,然后运行git commit。Git H 索引进行新提交,更改名称somebranch以指向new commit H,您将拥有:

...--F--G--H   <-- somebranch (HEAD)
Run Code Online (Sandbox Code Playgroud)

与提交H匹配索引!

这也解释了为什么,如果你忘了git add一个文件,并提交,提交获取版本的文件。提交将获取index中的任何内容,该索引是上一次提交的旧版本。

这也是Git定义跟踪文件还是未跟踪文件的方式:当且仅当文件当前在索引中时,才会跟踪工作树中的文件。使用git add会将其复制到索引中,然后对其进行跟踪,因为它在索引中并且将在您进行的下一次提交中。使用git rm将从索引和工作树中删除一个文件:现在它已经消失了,因此既没有被跟踪也没有被跟踪。使用git rm --cached会将其从索引中删除,但将其保留在工作树中:现在它已被取消跟踪,因为它在工作树中,但不在索引中。

那么,对索引的一个很好的简短描述是:索引是将进入下一个提交的文件集。 使用git commit只会冻结准备就绪的索引。使用git add,您可以将文件更新或插入索引中,以使其随时可用。您在工作树中进行工作,但是从索引中提交。

关于git status.gitignore

git status命令首先打印出一些有趣的事实,例如“在分支上master”,它通过名称HEAD的附加位置知道您在哪个分支上。然后,它告诉您有关暂存的文件和/或未暂存的文件。

这实际上是在告诉您索引中的内容。您的索引开始与您的提交匹配-例如,由于您是从上一次提交而来的,或者是因为git checkout。但是,如果您运行git add,则会将新文件或现有文件的新版本写入索引。Git可以非常快速地比较HEAD提交(从分支名称记住的哈希ID)索引的提交,以告诉您索引中的文件是否它们的HEAD版本不同。这些是准备提交的文件。

让我们再来看一遍,因为它很重要:

  • 如果索引文件与该文件匹配HEAD,则Git不执行任何操作。
  • 如果索引文件与HEAD文件不同,则Git表示已准备好提交

这样,如果您有一个包含数千个文件的大型项目,那么Git可以引起您的注意,如果您立即进行提交,那将变得与众不同

后半部分是关于您仍然可以进行的操作git add。如果您已经更改(或创建或删除了)工作树中的某些文件,但尚未使索引匹配,则可以运行git add以更新索引以匹配工作树。因此,第二个比较是index-vs-work-tree,并且:

  • 如果索引文件与工作树文件匹配,则Git什么也不说。
  • 如果索引文件与工作树文件不同,则Git表示该文件未暂存为commit

但是,这里发生的一种情况在第一次比较中不会发生,那就是当有一个工作树文件根本不在索引中时。这样的文件未跟踪,Git会抱怨。

好吧,也就是说,除非您告诉Git ,否则 Git会抱怨的:不要抱怨这个特定的未跟踪文件。 这就是很多事情.gitignore:关闭Git。列出其中的文件.gitignore不会使Git 在索引中没有该文件,因为如果您在该提交中检出提交,则该文件将进入索引。

清单文件.gitignore 告诉Git的,一个大规模的“添加”,如git add .git add *不应该添加该文件,如果它不是已经索引中。但是一旦由于某种原因将其放入索引后,便会跟踪该文件,并且.gitignore条目不再适用。它们仅适用于未跟踪的文件(非索引文件)。

结论

你无法得到想要的东西。 最好至少在通用软件的存储库中避免提交主机或站点特定的文件(例如配置)。相反,提交示例或一组默认配置,然后让VCS 忽略实际配置(如果它位于同一目录中)。如果要对配置进行版本控制,请将实际配置保存在单独的存储库中。