Git 如何在分支顶端创建分支并为其创建单独的拉请求?

Git 如何在分支顶端创建分支并为其创建单独的拉请求?,git,github,Git,Github,我正在同一个文件中进行小的功能更改。但是这些更改是不相关的,这就是为什么它们必须在单独的拉取请求中 因此,我从我的项目staging分支创建了一个分支: git签出-b分支 对它做了一些工作并创建了pull请求 现在,由于我将在单独的PRs中做更多的小更改,我不想等到这些PRs得到批准,然后合并并重新设置基础,直到我可以开始开发下一个功能。此外,我希望避免基于暂存创建单独的分支,然后在批准每个PR后必须处理合并冲突(因为所有更改都在同一个文件中) 有没有办法做类似的事情 git checkou

我正在同一个文件中进行小的功能更改。但是这些更改是不相关的,这就是为什么它们必须在单独的拉取请求中

因此,我从我的项目
staging
分支创建了一个分支:

git签出-b分支
对它做了一些工作并创建了pull请求

现在,由于我将在单独的PRs中做更多的小更改,我不想等到这些PRs得到批准,然后合并并重新设置基础,直到我可以开始开发下一个功能。此外,我希望避免基于暂存创建单独的分支,然后在批准每个PR后必须处理合并冲突(因为所有更改都在同一个文件中)

有没有办法做类似的事情

git checkout -b branch2 branch1

然后从这个分支创建一个PR,它只包含在branch2中完成的提交?我尝试过这样做,但它从
branch1

一开始就推送了所有提交。您可以完全按照您提到的方式创建新的分支名称,但这并没有达到您想要的效果。原因是。。。原因是Git就是这样。Git很复杂!GitHub试图简化这些复杂的问题,这样当您发出GitHub请求时,Git本身就没有这些东西;它们是特定于GitHub1的GitHub尝试向您展示一些非常简单的东西

不幸的是,正如您所看到的,这种简单性是假的。我认为GitHub实际上是在做一个dis服务,以简化类似这样的事情。不过,它在许多常见情况下都非常有效,很多人显然喜欢它的简化。但是因为你工作的方式,你不能让GitHub假装。它就是不起作用。相反,您需要进入真正的Git,以及它的所有复杂性。这些复杂性的存在是因为它们是必要的,对于这种分布式开发,至少在最复杂的情况下是必要的(有些情况你可能永远不会遇到)


1 GitHub的其他竞争对手提供自己的拉取请求,因为PRs非常有用。如果没有PRs,这些竞争对手将永远不会得到太多的利用


Git是关于提交的 首先要认识到的是Git本身与文件无关。这是关于承诺的。提交包含文件,这对我们人类很重要,但Git关心提交。Git也不是关于分支的。提交是分支的好形式,分支是分支的几种含义之一,但分支名称是分支的另一种含义,它只是帮助您和Git找到提交。这是最重要的

这意味着您需要的第一件事是牢牢掌握提交。现在,您一直在进行提交,但是人们可以在不完全了解什么是提交的情况下进行此操作,所以让我们在这里详细介绍一下。您可能已经知道了其中的一些甚至所有内容,但让我们涵盖所有内容,尽管速度很快:

  • 每次提交都进行编号。这些数字是散列ID:它们看起来是随机的,但实际上是Git用来保存提交数据的内部对象内容的加密校验和。校验和算法在每个Git二进制文件中都是相同的,因此每个Git为相同的提交计算相同的ID。这就是提交如何从一个Git存储库传播到另一个Git存储库:通过hash ID

    commit对象是四种内部对象中的一种。其他三个是树、blob(文件数据)和带注释的标记。所有四个都由散列ID寻址。树和blob可以在任何有意义的时候重用,但提交不能。最后,提交对象始终只包含一个树对象哈希ID

    您通常不需要知道提交对象的内部结构。请记住,因为散列ID是内部数据的散列,所以任何人或任何事,包括Git本身,都不可能更改任何提交的任何部分。如果您确实从内部对象数据库中取出一个提交,更改某些部分,然后将其放回,那么您得到的是一个新的、不同的提交,具有新的、不同的散列ID。旧的提交仍然存在:您只添加了另一个提交。旧提交的哈希ID仍然引用旧提交

  • 这意味着每个提交都有两个部分:它的数据——所有文件的快照,由存储的树对象哈希ID表示;它的元数据,由提交对象中的内容表示。元数据包含有关提交人(姓名和电子邮件地址)、时间(某些日期和时间戳)以及原因(日志消息)的信息。对于Git本身来说至关重要的是,元数据包括一个早期提交哈希ID的列表

快照保存每个文件,但采用只有Git可以读取的特殊格式,并且实际上没有任何内容可以更改(同样是因为这些散列ID)。各种文件内容会自动消除重复(通过相同的哈希ID技巧)。这意味着,当您在仅更改一个或两个文件的情况下进行新提交时,所有其他文件的副本都不占用空格2,因为提交只是重复使用其他提交的副本。最后,任何文件的任何版本都只有一个副本。这很好,因为它是只读的:任何内容都不会覆盖保存的内容

当然,如果我们不能编写自己的文件,这些保存的快照将只作为归档文件使用。因此,当我们在Git中处理文件时,我们不会处理提交的文件。我们一会儿再谈这个问题

每次提交中保存的哈希ID都是为了让Git从提交向后工作,从提交到更早的提交。这就是Git向我们展示提交的方式,提交是快照,就好像它们是一组更改一样。让我们画一个简单的、向后看的提交链,其中每个较新的提交都指向其(单个)父提交。我们将把较新的提交放在右边,把较旧的提交放在左边,因为散列ID又大又难看,不适合人类使用,所以我们将
... <-F <-G <-H
...--G--H   <-- main
...--G--H   <-- feature1, main
...--G--H   <-- feature1, main (HEAD)
...--G--H   <-- feature1 (HEAD), main
          I   <-- feature1 (HEAD)
         /
...--G--H   <-- main
git checkout -b feature2 main
          I   <-- feature1
         /
...--G--H   <-- feature2 (HEAD), main
          I   <-- feature1
         /
...--G--H   <-- main
         \
          J   <-- feature2 (HEAD)
            J   <-- feature2 (HEAD)
           /
          I   <-- feature1
         /
...--G--H   <-- main
git clone <url> [<directory-name>]
git fetch origin
...--G--H   <-- master (HEAD), origin/master
         \
          I   <-- origin/feature1
...--G--H   <-- master (HEAD), origin/master
         \
          I--J   <-- origin/feature1
          K   <-- master (HEAD)
         /
...--G--H--L   <-- origin/master
         \
          I--J   <-- origin/feature1
          K   <-- master (HEAD)
         /
...--G--H--L   <-- origin/master
         \
          I--J   <-- origin/feature1
            K   ???
           /
          /  K'  <-- master (HEAD)
         /  /
...--G--H--L   <-- origin/master
         \
          I--J   <-- origin/feature1
          K--M   <-- master (HEAD)
         /  /
...--G--H--L   <-- origin/master
         \
          I--J   <-- origin/feature1
          K--M
         /  /
...--G--H--L   [they already have these]
         \
          I--J   [they already have these]
          K--M   <-- master (HEAD), origin/master
         /  /
...--G--H--L
         \
          I--J   <-- origin/feature1
...--P--Q   <-- staging
...--P--Q   <-- staging, origin/staging
          R   <-- branch1
         /
...--P--Q   <-- staging, origin/staging
          R   <-- branch1
         /
...--P--Q   <-- staging
          R   <-- branch1
         /
...--P--Q   <-- staging
         \
          S   <-- branch2
            S   <-- branch2
           /
          R   <-- branch1
         /
...--P--Q   <-- staging