我可以创建一个没有来自主分支的任何文件的新分支吗?

oymdgrw7  于 2022-10-23  发布在  Git
关注(0)|答案(2)|浏览(195)

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

1dkrff03

1dkrff031#

TL;博士

不,但问题(“从主人那里创造…”)迫使回答;我们必须以一种特殊的方式来解释“from”,这会迫使“no”。有了正确的问题,答案就会变成“是”。请参阅Creating a new empty branch for a new project或阅读下面的详细答案。

分支不是从分支创建的。它们是由于和/或指向提交而创建的。
更具体地说,branchname只是指向单个提交的指针,Git称之为
tip-commit*。提交本身也可以用作指针:每个提交都指向其父级或父级。
也就是说,我们有一些系列的提交:

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

以哈希ID为H的最近一次提交结束。提交H指向其父G,后者指向F等等。定位最后一次提交的分支名称仅指向m1n 5o1p本身:

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

创建新分支名称时,至少使用a-常用方法:

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

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

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

例如,您只能使用git checkout mastergit switch master“on”这些分支中的一个,因此Git将特殊名称HEAD附加到一个分支名称,以记住我们使用的分支名称:

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

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

  • 我们在哪家分店?(读取HEAD以查看其连接位置。)
  • 我们在做什么?(读取HEAD,然后读取其连接的分支名称。)

各种其他形式的“创建一个新分支”操作都做同样的事情:它们创建一个指向某个“现有提交”的新名称。
现有的提交具有它拥有的任何文件。如果您使用默认的HEAD创建,或者如果您使用可选的起始点参数,则现有的提交已经在当前分支上。这种情况下的最终结果:

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

所有这些提交现在都在两个分支上,而不是只在一个分支上。
当您在某个分支B上进行新提交时,新提交将获得一个新的、唯一的哈希ID

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

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

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

名称master不移动。我们不在master上;我们在br上。名称br*移动:

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

直到H的提交继续在两个分支上进行,但新的提交I仅在br上进行。
我们或Git,至少-findcommits,方法是取一个像brmaster这样的名称,然后按照它的箭头,并得到一个I或者H之类的commit。从这个commit,Git可以遵循
它的向后指向的箭头:如果我们在H并向后移动一步,我们就会到达G。这个commit也有一个父级,所以如果我们遵循这个箭头,我们最终会提交F,以此类推。
如果我们让Git移动名称master,这将更改现有提交所在的
分支集合。它不会更改任何现有的commits*,只会更改可以找到它们的名称。这正是Git的主要目的:我们创建新的提交,它保存快照并具有指向其父母的指针。然后我们让Git通过分支名称或哈希ID检查一些提交,然后我们得到旧快照。或者,我们有Git比较任何两个提交。如果我们比较父母和孩子,差异表明某人改变了。提交仍然是快照!
就像说今天是20摄氏度,昨天是18摄氏度,所以差异是2摄氏度。我们可能会关心温差,或者实际温度。Git可以做任何一个,但它存储实际的东西,而不是差异。

最初的空存储库困境

现在,在一个全新的、完全空的存储库中显然存在一个问题。像master这样的分支名称必须指向某个现有的有效提交。但是这个新的、完全空的存储库没有提交。那么master可以指向哪个提交?
Git通过说您在分支master上,但分支m1n 40o1p不存在来解决这个问题。没有提交,因此没有有效的哈希ID,因此分支不存在。这就可以了:它不存在,所以它不能识别任何现有提交的事实是好的。
换句话说,在一个根本不存在的分支上运行是可以的,并且在任何新的、完全空的存储库中,都会自动获得该状态。然后,当您进行第一次提交时,Git会创建提交并同时创建分支名称:

A   <-- master (HEAD)

现在,存储库中有一个提交,有一个很大的哈希ID,但我们在这里称它为Anamemaster现在存在,并指向现有的提交A

你可以随时重现这种困境

您可以随时将Git放回这种情况:只需使用git checkout --orphan *new-branch*。Git会给你一个不存在的分支名称。git status将告诉您您在新的分支上,而git log则不会显示任何内容(有时会显示错误消息:Git作者最终将Git修复为智能,只需说“还没有提交”)。

Git从Git的索引进行新提交

Git的新手经常认为Git使用他们的工作树文件。这是一个很大的挫折来源,因为这不是真的。Git会在你要求的时候填充你的工作树,但这不是它用来制作新提交的快照的。
当您要求Git进行新提交时:

git commit

Git将Git的索引中的所有文件作为新快照写入。这个名字,索引,不是一个很好的名字,所以这个东西现在有了另一个名字:Git称它为暂存区域。1 Git索引中的文件是进入快照的文件。
通常,索引中充满了文件。只是正常情况下,这些文件也与当前提交中的所有文件相匹配。也就是说,假设您正在提交H

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

你是通过做git checkout master来的。这填充了Git的文件从提交H开始的索引,也填充了您的工作树-从提交m2n50o1p可以看到和使用的文件。2因此Git索引中的文件与HEAD提交中的文件匹配。这让Git说没有什么要做的。
您可以使用git add将文件放入Git的索引中,它将从您的工作树复制到Git的目录。您可以使用git rm将文件从Git的索引中取出,这将删除您的工作树副本Git的目录副本。如果您从Git的索引(和您的工作树)中删除了所有文件,那么索引现在真的是空的。
如果您更改了任何工作树文件,您总是必须再次对其进行git add。原因很简单,很明显:Git没有使用工作树副本。为了让Git在下一次提交中放入更新的文件,您必须首先告诉Git将工作树副本
复制回索引,替换现有的索引副本。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
1.git rm -r .距顶层
命令#1将我们置于一个新的分支new-branch上,该分支不存在。这与完全空的存储库中的状态类似,当我们在master上时,master存在,但这次不存在的分支名称是new-branch
命令#2删除Git索引中的所有文件,并从工作树中删除这些相同的文件。这样可以消除工作树的杂乱,这样您就可以看到现在没有文件了。请注意,未跟踪文件不会被删除;要删除这些文件,请运行git clean -dfx(清除所有内容,包括空目录,并清除.gitignore-ed文件)。
现在您有了一个干净的板岩:一个真正的空索引,没有文件。现在,您可以创建新文件并将其复制到Git的索引中。当您有一组自己喜欢的文件时,使用git commit创建一个无父级提交:

...--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获得新的提交。然而,Commit H将位于新的分支上,Git现在将比较Commit mn75o1p中的快照及其所有文件,以及新Commit m2n76o1p的快照及其独立文件,并告诉您将Commit m1 n77o1p转换为Commit m2 n78o1p需要删除所有这些文件。

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

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

 I--J--K   <-- new-branch (HEAD)

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

结论

你需要知道的是:

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

dw1jzc5e2#

您可以暂存一个空树,这是在提交时有效删除所有文件的快速方法。git read-tree --empty将索引设置为空树。提交此操作会留下历史记录,但会进行删除所有内容的提交。如果需要单独的清除历史记录,请参见为新项目创建新的空分支

相关问题