清洁的Git分支树

问题描述 投票:0回答:1

我有一个承诺的树,已经成为一种很难做出来,无序。所以,我觉得我需要进行清洁。这将会是真棒,如果你能帮助我解决这个问题。

我工作的这在某些时候与两个分支B和C.每个分支我看到当我登录每个分支以下几个提交后延伸项目一个。我总结了日志,让我知道,如果它无法读取。

master:
commit: B2 (HEAD -> master, origin/master, origin/HEAD)
commit: B1
commit: A3
...

branch_B:
commit: B2 (HEAD -> branch_B)
commit: C3 
commit: C2 (branch_C)
commit: C1
commit: B1
commit: A3
...

branch_C:
commit: C2 (HEAD -> branch_C)
commit: C1
commit: B1
commit: A3
...

编辑:具体地讲,我想从主除去B1和B2,从branch_C除去B1,从branch_B除去C1,C2,和C3以及移动C3回branch_C。

git branch
1个回答
0
投票

Git是建造,因此“希望”在一定意义上,添加新的提交,但从来没有删除任何旧的。它可以去除提交,但是:

  • 要非常确定你真的想这样做!
  • 要知道,提交就像某些疾病:如果你已经在你的仓库提交X,和 体液交换 曾与另一Git仓库这是同一来源的克隆,或者你的一个克隆相互作用或你是他们的一个克隆,他们可能有犯X现在也。你使用Git的连接到他们的Git下一次,你可能会得到再次提交X。为了使一些提交真的走了,你必须 治愈 从所有受影响/感染Git仓库删除的问题。因为在一般情况下,只控制自己的Git仓库,这意味着你必须让其他人来解决他们的仓库了。

有了这样的方式,这里是你如何做到这一点,利用git cherry-pickgit reset。还有就是要做到这一点,但让我们在这里这两个命令的方法不止一种。

Git's thing is the commits; the names are secondary

正如你已经看到的,每次提交都有一个唯一的哈希ID-一些又大又丑的字符串,如b5101f929789889c2e536d915698f58d5c5c6b7a。这些ID在每一个共享此库的Git相同。 (我在这里列出的是在Git仓库为自己的Git提交。)

每个提交保留,只要提交本身的存在,所有文件的完整快照。那么,它拥有所有那些在快照中的文件,但是这等于是说,所有的蓝色蜡笔蓝:它是一种愚蠢的。问题的关键是,它的文件的快照。这并不是说“改变README这样”,这需要回去和寻找README之前的样子。它只是说我们有README,它看起来是这样的。如果快照没有一个文件,Git的或许应该删除该文件(尽管这部分变得有点棘手,因为Git的可以让你有“未跟踪文件”)。在任何情况下,在快照中的文件永远冻结,或至少,只要提交存在。

但是,每个快照也有一些元数据,如您的姓名(如果您所做的承诺),当你成功了,你为什么做它,你的日志信息和关键的是我们的目的,前面的哈希ID提交。元数据,如文件,永远冻结,或者只要提交存在。需要注意的是,当你让Git告诉你一个承诺,Git的节目(部分)的元数据,然后说明你这次提交的文件之间的差异,这次提交的父的文件。它可以这样做,因为,父母的,或以前,提交的哈希ID,保存为这部分承诺。

这意味着对我们来说,我们可以绘制出的向后指向提交的字符串,每次提交命名其父:

A <-B <-C

如果散列的ID是简单的大写字母这样,我们可以只扫描所有这些,找到最后一个,但他们没有:他们似乎随机的(虽然实际上他们严格保存内部提交的所有位,确定哪个所以,我们不能改变任何内提交位的!)。所以Git的需要一种方法来保存最后提交,从它可以向后工作的哈希ID。

在分支哈希ID最后一次提交的是分支名称的功能,如master

A--B--C--D--E   <-- master

我们-并在年底的Git开始,通过使用名称master获得哈希ID(这里E)。然后,我们向后工作,下面那些不变-能的内箭头。

该分支名箭头-的下存储的哈希ID的名称,可以改变的,因为我们拭目以待。

Adding commits to a branch

要添加新的提交到当前分支,我们的Git保存文件的快照,加入我们的姓名和电子邮件,我们的日志信息,并保存当前的哈希ID提交。 Git的写了这一切到新的承诺,其由此获得了新的哈希ID:

A--B--C--D--E   <-- master
             \
              F

现在的Git刚刚更新的名字记录新的最新承诺:

A--B--C--D--E
             \
              F   <-- master

然后我们可以理顺:

A--B--C--D--E--F   <-- master

需要注意的是它的提交,以及它们之间的关系等,内部,向后指向的箭头,这是至关重要的位置。名字做的事,但只是因为这就是我们如何发现的提交。在提交自己形成向非循环图DAG或。名字让我们进入DAG。在DAG本身什么都不能更改,但名称可以移动的,我们可以添加新的提交。

(我们仍可以随意绘制DAG我们想要的,弯曲的连接箭头,只要他们还在联系。我用线条,而不是在这里,因为它很难找到好的文本字符做斜箭头的箭文本。)

Adding more branches to the graph

假设我们现在有我们六个提交:

A--B--C--D--E--F   <-- master

并希望使一个新的分支。我们用两种git branchgit checkout使分支,所以现在我们有:

A--B--C--D--E--F   <-- BranchA, master

这两个名字都指向同一个commit,F。所有六个提交现在对两个分支。

如果我们添加了一个新的提交,显然我们会得到:

A--B--C--D--E--F
                \
                 G

我们F早些时候得到了同样的方式。但名字要改变?要回答这个问题,Git的高度名字HEAD的分支之一:

A--B--C--D--E--F   <-- BranchA (HEAD), master

这告诉Git的这名以更改:

A--B--C--D--E--F   <-- master
                \
                 G   <-- BranchA (HEAD)

当名移动HEAD附件仍然存在。我们需要了解的附件时,我们想知道:哪个分支,我们呢?哪个分支会,如果它影响到当前分支我们的命令影响?如果我们只是在寻找什么是在存储库中,我们可以把它关闭。

So, with that out of the way, let's draw your existing graph more completely

您有一系列在你上面调用A3,之后事情变得有点多毛的一个结束的提交。我喜欢一个字母的名字,但我会在这里用你的:

...--A3

现在,你说你master达到B2这是由通过B1之前A3之前,所以必须有经过两次提交:

...--A3--B1--B2   <-- master

同时您Branch_BB2,这是由C3之前开始了,但是这是根本不可能:

...--A3--B1--B2   <-- master
           \
            C3--B2   <-- Branch_B

所以你必须已在抄写你提交的哈希一些错误(并不奇怪,因为他们是大和丑陋,基本需要小心剪切和粘贴,以避免错误)。我会认为在掌握B2真的是一些其他的ID,并与B2a这里替换它:

...--A3--B1--B2a   <-- master
           \
            C3--B2   <-- Branch_B

Branch_C启动井,结束 - 附C2,这是由C1前面,然后B1,然后A3

            C1--C2   <-- Branch_C
           /
...--A3--B1--B2a   <-- master
           \
            C3--B2   <-- Branch_B

您可以通过使用git log --decorate --oneline --graph --decorate master Branch_B Branch_C(或git log --all --decorate --oneline --graphGet Help From A Dog)证实了这一点。绘制垂直方向的图表,这是不一样漂亮或明显对我,但还是非常有用的。

How to get what you want: it requires changing what you want, slightly

现在,这里是你说的,你想:

        C1--C2--C3   <-- Branch_C
       /
...--A3   <-- master
       \
        B1--B2   <-- Branch_B

你不能这样。我们已经说过,没有电源在任何地方任何现有的承诺,并期待在我们现在有,提交B2的父提交C3,例如改变任何东西。

但是你可以得到的东西,可能是一样好,那就是:你可以B2的副本。事实上,你可能已经-B2aB2是彼此的可能拷贝。

无需担心的确切复制机制呢,让我们来看看,如果我们做一个B2b这是B2的副本,但是有B1作为其父发生了什么:

            C1--C2   <-- Branch_C
           /
...--A3--B1--B2a   <-- master
         | \
         |  C3--B2   <-- Branch_B
          \
           B2b   <-- new-branch-b

接下来,让我们C1复制到新C1aA3弹簧:

          C1a   <-- new-branch-C
         /
        /   C1--C2   <-- Branch_C
       /   /
...--A3--B1--B2a   <-- master
         | \
         |  C3--B2   <-- Branch_B
          \
           B2b   <-- new-branch-b

然后,我们只需要通过一个复制C2C3,之一:

          C1a--C2a--C3a   <-- new-branch-C
         /
        /   C1--C2   <-- Branch_C
       /   /
...--A3--B1--B2a   <-- master
         | \
         |  C3--B2   <-- Branch_B
          \
           B2b   <-- new-branch-b

几乎最后,我们需要移动的旧名称,Branch_BBranch_C,使点分别提交B2bC3a

          C1a--C2a--C3a   <-- new-branch-C, Branch_C
         /
        /   C1--C2   [abandoned]
       /   /
...--A3--B1--B2a   <-- master
         | \
         |  C3--B2   [abandoned]
          \
           B2b   <-- new-branch-b, Branch_B

然后,我们需要移动的名称master后退了两步,使其指向A3而不是B2a,放弃B2a完全。这是很难得出直到我们停止绘制废弃的提交。他们仍然会在你的仓库了一段时间(至少30天默认情况下),但隐藏起来,使你不能看到他们的任何更多,这给了我们:

          C1a--C2a--C3a   <-- new-branch-C, Branch_C
         /
        /__________
       /           \
...--A3--B1         -- master
         |
         |
          \
           B2b   <-- new-branch-b, Branch_B

现在,我们可以删除new-branch-[bc]名称和清理绘图的安排:

        C1a--C2a--C3a   <-- Branch_C
       /
...--A3   <-- master
       \
        B1--B2b   <-- Branch_B

除了这里的后缀,这意味着它们是不同的散列的ID,这就是你想要的!

Getting from here to there: adding new names

首先,你只需要添加新的名字,指着所需提交:

git branch new-branch-b <hash of B1>
git branch new-branch-c <hash of A3>

我们在这里选择的哈希ID是将继续对新建分支的提交。对于Branch_B,这是B1,我们可以留在原地,但Branch_C,这是犯A3,因为我们必须复制到C1 C1a

Getting from here to there: copying commits

现在,它的时间来复制的提交。让我们复制B2B2a。你可以使用任何一个你喜欢的,只要他们做出同样的变化并且具有相同的提交信息,因为复制命令是git cherry-pick和它的工作方式非常类似于我们前面关于显示承诺说:

[混帐]将会显示该之间的差别提交的文件,这次提交的父的文件

而不是显示的差异,git cherry-pick发现差异,那么应用于的任何承诺,我们已经签出,使得同样的变化,并提交结果,使用相同的日志信息与原始提交过。

所以,我们只需要:

git checkout new-branch-b
git cherry-pick <hash-of-B2a or whatever>

这会让我们这么远,当我们绘制图形,并留下了很多:

...--A3
       \
        B1--B2b   <-- new-branch-b

然后,我们需要建立C进行同样方式的新的分支:

git checkout new-branch-b
git cherry-pick <hash-of-C1>
git cherry-pick <hash-of-C2>
git cherry-pick <hash-of-C3>

结果,再留出大量图形的绘制,是期望:

        C1a--C2a--C3a   <-- Branch_C
       /
...--A3

最后一步是让master确定提交A3,为此,我们只需要git checkout master然后git reset --hard

git checkout master
git reset --hard <hash-of-A3>

(注:如果您使用的哈希的ID这样做,这是一个好主意,剪切和粘贴他们,和/或将它们保存在文件中,因为它太容易在这里得到错别字有技巧,使用相对的名字,但我。 “M不会将其包含在这个答案。)

git reset命令会影响哪个分支名HEAD连接到,和git cherry-pick命令使上取其分支名HEAD连接到新的提交。这就是为什么我们必须git checkout每个那些名字。

在这一点上,我们有新的分支名称,并masterA3,但我们还没有更新了其他两个分支的名字。和以前一样,我们可以在这里使用git checkoutgit reset --hard

git checkout BranchB
git reset --hard new-branch-b
git checkout BranchC
git reset --hard new-branch-c

我们不需要哈希ID的这段时间,因为像git cherry-pickgit reset命令,一个分支的名字的意思是提交其ID存储在该分支的名称。

一旦我们完成了这一切,我们可以删除new-branch-bnew-branch-c的名字:

git branch -D new-branch-b
git branch -D new-branch-c

-D是强制删除,这使得Git来完成它,即使Git的认为它是不安全的。 (这个时候Git的想法是安全的,当这不是嘛,和一个很好的尝试,但不是很大。)

Cherry-pick can have merge conflicts

这是不是特别容易为你的情况,但要知道对于未来是很重要的。每git cherry-pick实际上是一种合并。 Git是要通过比较父提交到提交,就像git show“合并”的承诺本身算所做的更改比较两到当前提交,找到您最近提交的变化,通过比较父提交樱桃-picked提交到电流(HEAD)提交。

如果你是一个有点糊涂这里,不用担心:在前款肯定是难以阅读。它是由插图真的最佳所示:

       o--o--...--P--C--o--...--o   <-- other-branch
      /
...--o
      \
       o--o--H   <-- your-branch (HEAD)

您运行git cherry-pick <hash of C>。 Git的方法:

  • 比较速度P VS C:这是他们改变了什么。
  • 比较速度P VS H:这是你改变了什么,有点儿
  • 结合这两套变化,将组合的变化,从P文件(即重复“你改变了什么”刚要回什么是在H,但后来加入“他们改变了什么”从H去的结果) 。
  • 如果结合的作品,使一个新的提交C'。否则,停止并留下一个烂摊子。

当这种不费力的工作在你的一部分,其效果是,无论从改变到P C,这些相同的变化现在在新的提交C'git cherry-pick作出的提交C的副本:

       o--o--...--P--C--o--...--o   <-- other-branch
      /
...--o
      \
       o--o--H--C'  <-- your-branch (HEAD)

当它出现问题,Git会停止与合并冲突,它将停止在git merge出问题的时候一样。在这一点上它是你的工作,完成了“合并”诚摘樱桃,在这种情况下,然后运行git commitgit cherry-pick --continue来完成这项工作。您可以使用所有相同的工具,你会git merge期间,完成作业,所以不管你喜欢git merge,用同样的方法。

© www.soinside.com 2019 - 2024. All rights reserved.