【问题标题】:Can I create a new branch, which does not have any files form the master branch?我可以创建一个没有来自 master 分支的任何文件的新分支吗?
【发布时间】:2020-09-06 10:07:14
【问题描述】:

我可以从 master 创建一个分支,而不会将 master 分支上的任何内容复制到新分支吗? 我可以这样做吗,git命令是什么,可以吗?

【问题讨论】:

  • 并非如此。根据定义,分支是对当前代码的修改。但是有一些变通方法取决于您希望分支的含义。您可以从中签出第一个提交和分支。这将意味着您的分支机构在历史上落后。您可以签出 master,从 master 分支,删除所有文件然后提交该分支。这将使分支成为历史上的当前分支
  • @slebetman 这就是我以前做过的,所以有没有更短的方法可以做到这一点?!
  • slebetman 的评论包括两种可能的解决方案,您所说的“那个”指的是哪一种?

标签: git github version-control


【解决方案1】:

您可以暂存一个空树,这是在您提交此操作时有效删除所有文件的快速方法。 git read-tree --empty 将索引设置为空树。提交它会留下历史记录,但会进行删除所有内容的提交。 如果您想要单独清除的历史记录,请参阅Creating a new empty branch for a new project

【讨论】:

    【解决方案2】:

    TL;DR

    不——但所问的问题(“从主人那里创造……”)强制回答;我们必须以特定方式解释“来自”,这迫使“不”。有了正确的问题,答案就变成了“是”。请参阅Creating a new empty branch for a new project 或阅读下面的长答案。

    分支不是从分支创建的。它们是由于和/或指向提交而创建的。

    更具体地说,分支名称只是指向单个提交的指针,Git 将其称为提示提交。提交本身也可以用作指针:每个提交都向后指向其父级或父级。

    也就是说,我们有一些系列的提交:

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

    以哈希 ID 为 H 的最近提交结束。提交H 指向其父G,后者又指向F,依此类推。定位最后一次提交 H分支名称 仅指向 H 本身:

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

    当您创建一个新的分支名称时,通常的方法是使用:或至少 一个

    git branch <new-name> [<start-point>]
    

    其中可选的start-point 参数指定哪个提交哈希ID 将进入这个新的分支名称。如果省略 start-point,则进入新分支名称的哈希 ID 是当前提交的哈希 ID。如果当前提交是H,新名称是br,我们得到:

    ...--F--G--H   <-- br, master
    

    你只能“在”这些分支之一——例如 git checkout mastergit switch master——所以 Git 将特殊名称 HEAD 附加到一个分支名称以记住哪个分支 名称 em> 我们正在使用:

    ...--F--G--H   <-- br, master (HEAD)
    

    因此HEAD 提供了两个独立问题的答案:

    • 我们在哪个分支? (阅读HEAD 了解它的连接位置。)
    • 我们在做什么commit? (阅读HEAD,然后阅读它所连接的分支名称。)

    各种其他形式的“创建新分支”操作都做同样的事情:它们创建一个指向某个现有提交的新名称。

    现有的提交具有它拥有的任何文件。如果您使用从HEAD 创建的默认值,则现有提交已经在当前分支上,或者如果您使用可选的起点参数,则在它现在所在的任何一组分支上。这种情况下的最终结果:

    ...--F--G--H   <-- br, master (HEAD)
    

    所有这些提交现在都在两个分支上,而不是只在一个分支上。

    当您在某个分支上B 并进行新的提交时,新的提交会获得一个新的、唯一的哈希 ID。因此,假设我们按名称检查 br

    ...--F--G--H   <-- br (HEAD), master
    

    然后进行一些更改并提交它们以进行新的提交I。新提交I 拥有所有文件的完整快照——提交不保存更改,它们保存快照——但更重要的是,提交I 是现有提交H:

    ...--F--G--H
                \
                 I
    

    名称master 不动。我们不在master;我们在brbr 的名字确实移动:

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

    通过H 向上提交继续在两个 分支上,但新提交I 仅在br上。

    我们——或者至少是 Git——找到提交,方法是使用 brmaster 之类的名称,按照箭头的方向,并获得 IH 之类的提交。从该提交开始,Git 可以跟随 its 向后的箭头:如果我们在 H 并后退一步,我们将在 G 着陆。该提交也有父提交,所以如果我们按照那个箭头,我们会在提交 F 处结束,依此类推。

    如果我们让 Git 移动名称 master,这将更改可以找到现有提交的 分支 集合。它不会更改任何现有的提交,它只是更改可以找到它们的名称。这就是 Git 的主要内容,真的:我们进行新的提交,它们保存快照并指向它们的父节点。然后我们让 Git 通过分支名称或哈希 ID 检查一些提交,然后我们得到旧的快照。或者,我们有 Git compare 任意两个提交。如果我们比较父母和孩子,差异显示了某人改变了。提交仍然是快照!

    就像说今天是 20˚C,昨天是 18˚C,所以差异是 2˚C。我们可能关心温度的差异,或者关心实际的温度。 Git 可以做任何一个——但它存储实际的东西,而不是差异。

    最初的空仓库困境

    现在,一个新的、完全空的存储库显然存在问题。像master 这样的分支name 必须 指向某个现有的有效提交。但是这个新的、完全空的存储库没有提交。那么master 可以指向哪个提交呢?

    Git 通过说你在分支 master 上解决了这个问题,但分支 master 不存在。没有提交,所以没有有效的哈希 ID,所以分支不存在。这样就可以了:它不存在,因此它无法识别任何现有提交这一事实很好。

    换句话说, 在一个根本不存在的分支上是可以的——你会在任何新的、完全空的存储库中自动获得该状态。然后,当您进行第一次提交时,Git 会创建提交并同时创建分支名称:

    A   <-- master (HEAD)
    

    现在存储库中有一个提交,带有一些丑陋的哈希 ID,但我们在这里称它为 A名称 master 现在存在并指向现有提交 A

    你可以随时重现这个困境

    您可以随时将 Git 重新置于这种情况下:只需使用 git checkout --orphan <em>new-branch</em>。 Git 会将您置于一个不存在的分支名称上。 git status 会告诉你你在新分支上,而git log 不会显示任何东西(有时会出现错误消息:Git 作者最终将 Git 修复为聪明,只是说“还没有提交”)。

    Git 从 Git 的 index 进行新的提交

    Git 新手通常认为 Git 使用他们的工作树文件。这是一个很大的挫败感,因为这不是真的。当您要求时,Git 会填充您的工作树,但这不是它使用的方式,为新提交制作快照。

    当你要求 Git 进行新的提交时:

    git commit
    

    Git 将 Git index 中的所有文件作为新快照写出。这个名字,index,不是一个很好的名字,所以这个东西现在有了另一个名字:Git 称之为 staging area1 Git 索引中的文件是进入快照的那些。

    通常,索引中充满了文件。只是通常情况下,这些文件也匹配当前提交中的所有文件。也就是说,假设你正在提交 H:

    ...--G--H   <-- master (HEAD)
    

    您通过git checkout master 到达这里。这填充了 Git 的 文件——索引——来自提交 H,还填充了你的工作树——你可以看到和使用的文件——来自提交 H .2 所以 Git 索引中的文件与 HEAD 提交中的文件匹配。这让 Git 说没有什么可提交的。

    您可以使用git add 将文件放入Git 的索引中,它会从您的工作树复制到Git 的索引中。您可以使用 git rm 从 Git 索引中取出文件 ,这会同时删除您的工作树副本 Git 的索引副本。如果您从 Git 的索引(和您的工作树)中删除 所有 文件,则该索引现在确实是空的。

    如果您更改任何工作树文件,您总是需要再次git add。其原因现在终于简单明了:Git 没有使用工作树副本。为了让 Git 将更新的文件放入下一次提交,您必须首先告诉 Git 将工作树副本 back 复制到索引中,替换现有的索引副本。3


    1暂存区 大多是一个更好的名字,但它并没有涵盖索引实际所做的所有事情,所以我倾向于坚持使用 index 我自己。

    2从技术上讲,Git 首先从提交复制到其索引,然后从其索引复制到您的工作树。在 Git 2.23 及其新的git restore 命令之前,Git 在内部总是会在这种两步过程中执行此操作。

    3从技术上讲,索引实际上只包含文件的名称、模式和 blob 哈希 ID。实际数据(文件的副本)存储为内部 Git 对象。但这一切对您来说都是不可见的,除非您开始使用 git ls-files --stagegit update-index 命令,这些命令并不真正用于正常工作。


    这终于为您提供所需的答案

    假设我们做两件事:

    1. git checkout --orphan new-branch
    2. git rm -r . 来自顶层

    命令 #1 将我们置于一个不存在的新分支 new-branch。这类似于完全空的存储库中的状态,当我们在master 存在之前在master 上,除了这次不存在的分支名称,我们无论如何都在,是new-branch

    命令 #2 删除 Git 索引中的所有文件,并从您的工作树中删除这些相同的文件。这会整理您的工作树,以便您可以看到现在没有文件。请注意,未跟踪文件不会被删除;要删除这些,请运行git clean -dfx(清理所有内容,包括空目录,并清理.gitignore-ed 文件)。

    您现在有了一张白纸:一个真正空的索引,没有文件。您现在可以创建新文件并git add 将它们复制到 Git 的索引中。当您拥有一组您喜欢的文件后,使用git commit 创建一个 new 提交,该提交没有父级

    ...--G--H   <-- master
    
     I   <-- new-branch (HEAD)
    

    这次新的提交I没有指向H。新提交的父节点是当前提交,git checkout --orphan 安排没有当前提交。我们有一个当前分支,但该分支不存在,因此没有当前提交。

    请注意,如果您在第 1 步中省略了 --orphan,您最终会得到:

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

    也就是说,您可以继续进行初始设置,如下所示:

    ...--G--H   <-- master, new-branch (HEAD)
    

    然后您可以删除所有带有git rm -r . 的文件,创建新文件,将它们添加到现在为空的索引中,然后提交,您将像往常一样获得带有父H 的新提交。但是,提交H 将在新分支上——Git 现在会将提交H 中的快照及其所有文件与新提交I 中的快照及其独立文件进行比较,并告诉您认为将提交 H 转换为提交 I 的方法涉及删除所有这些文件。

    换句话说,这两种设置之间的区别在于,对于--orphan,新分支根本没有连接到旧分支。新分支上的提交以新的根提交开始。这些历史不再在过去的某个时间重新连接(提交H):它们是独立的、不相关的历史。在I 之后添加的新提交仍然与提交H 无关:

    ...--G--H   <-- master
    
     I--J--K   <-- new-branch (HEAD)
    

    您可以随时git checkout master 选择现有提交H 和分支名称master。这将从 Git 的索引中删除您当前提交 K 中的文件,同时从您的工作树中删除这些文件,并将 H 中的快照提取到 Git 的索引中,并将这些文件复制到您的工作树中。

    结论

    你需要知道的是:

    • 历史,在 Git 中,提交。
    • Git finds 从分支名称开始(查找提示提交)并向后工作。
    • 创建一个新的提交包括按名称检查一个分支,以便 Git 知道要更新哪个 name,然后修改 Git 的 index 中的文件,因为 Git 使来自文件索引副本的提交。
    • 当你 git checkout 提交时,你的工作树就出现了,因为 Git 索引中的文件是 Git 的内部 Git-only 格式,它只对 Git 有用,对你或其他人没有用你的电脑软件。您将处理工作树文件,然后使用 git add 将它们复制回 Git 的索引,为下一次提交做好准备。
    • 新分支名称通常在您创建它们的那一刻就已经指向现有提交。这只是一个git checkout --orphan 案例,加上最初的完全空的存储库,这是特别的。当您进行提交时,这些会生成新的 name。在那之前,你处于一个特殊的未出生分支状态。

    【讨论】:

      猜你喜欢
      • 2018-07-16
      • 2018-09-08
      • 2021-04-16
      • 1970-01-01
      • 2020-01-21
      • 2020-10-11
      • 1970-01-01
      • 1970-01-01
      • 2019-03-26
      相关资源
      最近更新 更多