Git分支学习——拉一个新的本地分支做一些试验性demo

1.参考:

https://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%AE%80%E4%BB%8B

2.关键思路

  • (1)Git的分支,本质上仅仅是指向提交对象的可变指针。
  • (2)分支创建

    Git 是怎么创建新分支的呢? 很简单,它只是为你创建了一个可以移动的新的指针。 比如,创建一个 testing 分支, 你需要使用 git branch 命令:
    $ git branch testing
    这会在当前所在的提交对象上创建一个指针。
    两个指向相同提交历史的分支

  • (3)定位当前分支

    Git 又是怎么知道当前在哪一个分支上呢? 也很简单,它有一个名为 HEAD 的特殊指针。 请注意它和许多其它版本控制系统(如 Subversion 或 CVS)里的 HEAD 概念完全不同。 在 Git 中,它是一个指针,指向当前所在的本地分支(译注:将 HEAD 想象为当前分支的别名)。 在本例中,你仍然在 master 分支上。 因为 git branch 命令仅仅 创建 一个新分支,并不会自动切换到新分支中去。
    HEAD 指向当前所在的分支
    (HEAD 指向当前所在的分支)

  • (4)分支切换

    要切换到一个已存在的分支,你需要使用git checkout命令。我们现在切换到新创建的testing分支去
    $ git checkout testing
    这样HEAD就指向testing分支了。
    这里写图片描述
    Note:分支切换会改变你工作目录中的文件,在切换分支时,一定要注意你工作目录里的文件会被改变。如果是切换到一个较旧的分支,你的工作目录会恢复到该分支最后一次提交的样子。如果Git不能干净利索地完成这个任务,它将禁止切换分支。

  • (5)项目分叉

    你创建了一个新分支,并切换过去进行了一些工作,随后又切换回 master 分支进行了另外一些工作。 上述两次改动针对的是不同分支:你可以在不同分支间不断地来回切换和工作,并在时机成熟时将它们合并起来。 而所有这些工作,你需要的命令只有 branch、checkout 和 commit。
    这里写图片描述

  • (6)分支的新建与合并

    参考:
    https://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%9A%84%E6%96%B0%E5%BB%BA%E4%B8%8E%E5%90%88%E5%B9%B6
    重点:
    (a)
    git branch iss53 (新建分支iss53)
    git checkout iss53 (从主分支切换到iss53分支)
    (b)
    git checkout master (切换回到主分支)
    git checkout -b hotfix (新建分支hotfix,并切换到hotfix分支;注此为,新建和切换分支两个命令的简写)
    (c)
    git checkout master (切换回主分支)
    git merge hotfix (将修改bug后的分支hotfix,合并到主分支)
    这里写图片描述
    (d)
    git checkout master (切换回主分支)
    git merge iss53 (将实验性分支iss53合并到主分支)
    这里写图片描述 注:一次典型合并中所用到的三个快照
    这里写图片描述注:Git将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它
    (e)遇到冲突时的分支合并
    如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们。 如果你对 #53 问题的修改和有关 hotfix 的修改都涉及到同一个文件的同一处,在合并它们的时候就会产生合并冲突:

$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
此时git做了合并,但是没有自动创建一个新的合并提交。Git 会暂停下来,等待你去解决合并产生的冲突。你可以在合并冲突的任意时刻 使用:
$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

    both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")
任何因包含合并冲突而有待解决的文件,都会以未合并状态标识出来。 Git 会在有冲突的文件中加入标准的冲突解决标记,这样你可以打开这些包含冲突的文件然后手动解决冲突。 出现冲突的文件会包含一些特殊区段,看起来像下面这个样子:
<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
 please contact us at support@github.com
</div>
>>>>>>> iss53:index.html
这表示 HEAD 所指示的版本(也就是你的 master 分支所在的位置,因为你在运行 merge 命令的时候已经检出到了这个分支)在这个区段的上半部分(======= 的上半部分),而 iss53 分支所指示的版本在 ======= 的下半部分。 为了解决冲突,你必须选择使用由 ======= 分割的两部分中的一个,或者你也可以自行合并这些内容。 例如,你可以通过把这段内容换成下面的样子来解决冲突:

上述的冲突解决方案仅保留了其中一个分支的修改,并且 <<<<<<< , ======= , 和 >>>>>>> 这些行被完全删除了。 在你解决了所有文件里的冲突之后,对每个文件使用 git add 命令来将其标记为冲突已解决。 一旦暂存这些原本有冲突的文件,Git 就会将它们标记为冲突已解决。

  • (7)分支管理

    git branch 命令不加任何参数,会得到当前所有分支的一个列表:
    $ git branch
    iss53

    • master
      testing
      注意 master 分支前的 * 字符:它代表现在检出的那一个分支(也就是说,当前 HEAD 指针所指向的分支)。 这意味着如果在这时候提交,master 分支将会随着新的工作向前移动。
      如果需要查看每一个分支的最后一次提交,可以运行 git branch -v 命令:

$ git branch -v
iss53 93b412c fix javascript issue
* master 7a98805 Merge branch ‘iss53’
testing 782fd34 add scott to the author list in the readmes

–merged 与 –no-merged 这两个有用的选项可以过滤这个列表中已经合并或尚未合并到当前分支的分支。 如果要查看哪些分支已经合并到当前分支,可以运行 git branch –merged:

$ git branch –merged
iss53
* master

因为之前已经合并了 iss53 分支,所以现在看到它在列表中。 在这个列表中分支名字前没有 * 号的分支通常可以使用 git branch -d 删除掉;你已经将它们的工作整合到了另一个分支,所以并不会失去任何东西。

查看所有包含未合并工作的分支,可以运行 git branch –no-merged:

$ git branch –no-merged
testing

  • (8)分支开发工作流
    (a)长期分支
    因为 Git 使用简单的三方合并,所以就算在一段较长的时间内,反复把一个分支合并入另一个分支,也不是什么难事。 也就是说,在整个项目开发周期的不同阶段,你可以同时拥有多个开放的分支;你可以定期地把某些特性分支合并入其他分支中。
    只在 master 分支上保留完全稳定的代码——有可能仅仅是已经发布或即将发布的代码。 他们还有一些名为 develop 或者 next 的平行分支,被用来做后续开发或者测试稳定性——这些分支不必保持绝对稳定,但是一旦达到稳定状态,它们就可以被合并入 master 分支了。
    这样,在确保这些已完成的特性分支(短期分支,比如之前的 iss53 分支)能够通过所有测试,并且不会引入更多 bug 之后,就可以合并入主干分支中,等待下一次的发布。

事实上我们刚才讨论的,是随着你的提交而不断右移的指针。 稳定分支的指针总是在提交历史中落后一大截,而前沿分支的指针往往比较靠前。
这里写图片描述

(b)特性分支
特性分支对任何规模的项目都适用。 特性分支是一种短期分支,它被用来实现单一特性或其相关工作。
考虑这样一个例子,你在 master 分支上工作到 C1,这时为了解决一个问题而新建 iss91 分支,在 iss91 分支上工作到 C4,然而对于那个问题你又有了新的想法,于是你再新建一个 iss91v2 分支试图用另一种方法解决那个问题,接着你回到 master 分支工作了一会儿,你又冒出了一个不太确定的想法,你便在 C10 的时候新建一个 dumbidea 分支,并在上面做些实验。 你的提交历史看起来像下面这个样子:
这里写图片描述

注:拥有多个特性分支的提交历史

现在,我们假设两件事情:你决定使用第二个方案来解决那个问题,即使用在 iss91v2 分支中方案;另外,你将 dumbidea 分支拿给你的同事看过之后,结果发现这是个惊人之举。 这时你可以抛弃 iss91 分支(即丢弃 C5 和 C6 提交),然后把另外两个分支合并入主干分支。 最终你的提交历史看起来像下面这个样子:
这里写图片描述
注:合并了 dumbidea 和 iss91v2 分支之后的提交历史

注意

当你做这么多操作的时候,这些分支全部都存于本地。 当你新建和合并分支的时候,所有这一切都只发生在你本地的 Git 版本库中 —— 没有与服务器发生交互。

  • (9)远程分支

    • (a)远程分支
      Remote references are references (pointers) in your remote repositories, including branches, tags, and so on.
      You can get a full list of remote references explicitly with git ls-remote [remote], or git remote show [remote] for remote branches as well as more information. Nevertheless, a more common way is to take advantage of remote-tracking branches.
      远程跟踪分支是远程分支状态的引用。 它们是你不能移动的本地引用,当你做任何网络通信操作时,它们会自动移动。 远程跟踪分支像是你上次连接到远程仓库时,那些分支所处状态的书签。
      Remote-tracking branches take the form /. For instance, if you wanted to see what the master branch on your origin remote looked like as of the last time you communicated with it, you would check the origin/master branch. If you were working on an issue with a partner and they pushed up an iss53 branch, you might have your own local iss53 branch, but the branch on the server would be represented by the remote-tracking branch origin/iss53.

这可能有点难以理解,让我们来看一个例子。假设你的网络里面有一个在git.ourcompany.com的git服务器。如果你从这里克隆,Git的clone命令会为你自动将其命名为origin,拉取它的所有数据,创建一个指向它的master分支的指针,并且在本地将其命名为origin/master。Git也会给你一个与origin的master分支在指向同一个地方的本地master分支,这样你就有工作的基础。
注:“origin”并无特殊含义,远程仓库名字“orgin”与分支名字“master”一样,在Git中没有任何特别的含义。同时master是当你运行git init时默认的起始分支名字,原因仅仅是它的广泛使用,“origin”是当你运行git clone 时默认的远程仓库的名字。如果你运行git clone -o booyah,那么你默认的远程分支名字将会是booyah/master.
这里写图片描述
注:克隆之后的服务器与本地仓库


如果你在本地的master分支做了一些工作,然而在同一时间,其他人推送提交到git.ourcompany.com并更新了它的master分支,那么你的提交历史将向不同的方向前进。也许,只要你不要origin服务器连接,你的origin/master指针就不会移动。
这里写图片描述
注:本地与远程的工作可以分叉


如果你要同步你的工作,运行git fetch origin 命令。这个命令查找“origin”是哪一个服务器(在本例中,它是git.ourcompany.com),从中抓取本地没有的数据,并且更新本地数据库,移动origin/master指针指向新的、更新后的位置。
这里写图片描述
注:git fetch 更新你的远程仓库引用


为了演示有多个远程仓库与远程分支的情况,我们假定你有另一个内部Git服务器,仅用于你的sprint小组的开发工作。这个服务器位于git.team1.ourcompany.com.你可以运行git remote add 命令添加一个新的远程仓库引用到当前的项目,将这个远程仓库命名为teamone,将其作为这个url的缩写。
这里写图片描述
注:添加另一个远程仓库


现在,可以运行git fetch teamone 来抓取远程仓库teamone 有而本地没有的数据。因为那台服务器上现有的数据是origin服务器上的一个子集,所以Git并不会抓取数据而是会设置远程跟踪分支teamone/master 指向teamone 的master分支。
这里写图片描述
注:远程跟踪分支teamone/master


  • (b)推送
    当你想要公开分享一个分支时,需要将其推送到有写入权限的远程仓库上。本地的分支并不会自动与远程仓库同步,——你必须显示地推送想要分享的分支。这样,你就可以把不愿意分享的内容放到私人分支上,而将需要和别人协作的内容推送到公开分支。

如果希望和别人一起在名为serverfix的分支上工作,你可以像推送第一个分支那样推送它。运行
git push (remote) (branch):

$ git push origin serverfix
Counting objects: 24, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done.
Total 24 (delta 2), reused 0 (delta 0)
To https://github.com/schacon/simplegit
 * [new branch]      serverfix -> serverfix

这里有些工作被简化了。Git自动将serverfix分支名字展开为refs/heads/serverfix:refs/heads/serverfix,那意味着,“推送本地的serverfix分支来更新远程仓库上的serverfix分支” 我们将会详细学习Git 内部原理的refs/heads/部分,但是现在可以先把它放在这儿。你也可以运行git push origin serverfix:serverfix,他会做同样的事情,相当于他说,“推送本地的serverfix分支,将其作为远程仓库的serverfix分支”可以通过这种格式来推送本地分支到一个命名不相同的远程分支。如果不想被远程仓库上的分支叫做serverfix,可以运行git push origin serverfix:awesomebranch 来将本地的serverfix分支推送到远程仓库的awesomebranch分支。


下一次其他协作者从服务器上抓取数据时,他们会在本地生成一个远程分支origin/serverfix,指向服务器的serverfix分支的引用:

$ git fetch origin
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/schacon/simplegit
 * [new branch]      serverfix    -> origin/serverfix

要特别注意的一点是当抓取到的新的远程跟踪分支时,本地不会自动生成一份可编辑的副本(拷贝)。换句话说,这种情况下,不会有一个新的serverfix分支——只有一个不可以修改的origin/serverfix指针


可以运行git merge origin/serverfix
将这些工作合并到当前所在的分支。如果想要在自己的serverfix分支上工作,可以将其建立在远程跟踪分支之上:

$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

这回给你一个用于工作的本地分支,并且起点位于origin/serverfix.


  • (c)跟踪分支
    从一个远程跟踪分支检出一个本地分支会自动创建一个叫做“跟踪分支”(有时也叫做“上游分支”)。跟踪分支时与远程分支有直接关系的本地分支。如果在一个跟踪分支上输入 git pull ,Git能自动地识别去哪个服务器上抓取、合并到哪个分支。

当克隆一个仓库时,它通常会自动地创建一个跟踪origin/master的master分支。然而,如果你愿意的话可以设置其他的跟踪分支-其他远程仓库上的跟踪分支,或者不跟踪master分支。最简单的就是之前看到的例子,运行git checkout -b [branch] [remotename] /[branch]。这是一个十分常用的操作所以Git提供了–track快捷方式:

$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

如果想要将本地分支与远程分支设置为不同名字,你可以轻松地增加一个不同名字的本地分支的上一个命令:

$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch serverfix from origin.
Switched to a new branch 'sf'

现在,本地分支 sf 会自动从 origin/serverfix 拉取。


设置已有的本地分支跟踪一个刚刚拉取下来的远程分支,或者想要修改正在跟踪的上游分支,你可以在任意时间使用 -u 或 –set-upstream-to 选项运行 git branch 来显式地设置。

$ git branch -u origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.

如果想要查看设置的所有跟踪分支,可以使用 git branch 的 -vv 选项。 这会将所有的本地分支列出来并且包含更多的信息,如每一个分支正在跟踪哪个远程分支与本地分支是否是领先、落后或是都有。

$ git branch -vv
  iss53     7e424c3 [origin/iss53: ahead 2] forgot the brackets
  master    1ae2a45 [origin/master] deploying index fix
* serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] this should do it
  testing   5ea463a trying something new

这里可以看到iss53分支正在跟踪origin/iss53并且“ahead”是2,意味着本地有两个提交还没有推送到服务器上。也能看到master分支正在跟踪origin/master分支并且是最新的。接下来可以看到serverfix分支正在跟踪teamone服务器上 server-fix-good分支并且领先3落后1,意味着服务器上有一次提交还没有合并入;同时本地有三次提交还没有推送。最后testing分支并没有跟踪任何远程分支。


注意:这些数字的值来自于你从每个服务器上最后一次抓取的数据。这个命令并没有连接服务器,它只会告诉你关于本地缓存的服务器数据。如果想要统计最新的领先于落后数字,需要在运行此命令前抓取所有的远程仓库。可以像这样做:
$ git fetch –all; git branch - vv


  • (d)拉取
    当git fetch 命令从服务器上抓取本地没有的数据时,它并不会修改工作目录中的内容。它只会获取数据然后让你自己合并。然而,有一个命令叫做git pull 在大多数情况下,它的含义是一个git fetch紧接着一个git merge 命令。

如果有一个像之前章节中演示的设置好的跟踪分支,不管它是显示地设置还是通过clone 或 checkout 命令为你创建的,git pull 都会查找当前分支所跟踪的服务器与分支,从服务器上抓取数据后然后尝试合并入哪个远程分支。


由于 git pull 的魔法经常令人困惑所以通常单独显示地使用fetch与merge命令会更好一些。


  • (e)删除远程分支
    假设你已经通过远程分支做完所有的工作了,-也就是说你和你的协作者已经完成了一个特性并且将其合并到了远程仓库的master分支(或任何其他稳定代码分支)。可以运行带有 –delete选项的git push 命令来删除一个远程分支。如果想要从服务器上删除serferfix分支,运行下面的命令:
$git push origin -- delete serverfix
To https://github.com/schacon/simplegit
 - [deleted]         serverfix

基本上这个命令做的只是从服务器上移除这个指针。Git服务器通常会保留数据一段时间知道垃圾回收运行,所以如果不小心删除掉了,通常是很容易恢复的。


  • (10)变基

  • (a)变基
    在Git中整合来自不同分支的修改主要有两种方法:merge以及rebase。在本节中中我们将学习什么是“变基”,怎么使用“变基”,并将展示该操作的惊艳之处,以及指出在何种情况下你应该避免使用它。


  • (b)变基的基本操作
    请回顾之前在分支的合并中的一个例子,你会看到开发任务分叉到两个不同分支,又各自提交了更新。
    这里写图片描述
    注:分叉的提交历史

之前介绍过,整合分支最容易的方法是merge命令,它会把两个分支的最新快照(C3和C4)以及二者最近的共同祖先(C2)进行三方合并,合并的结果是生成一个新的快照(并提交)。
这里写图片描述
注:通过合并操作来整合分叉了的历史


其实,还有一种方法:你可以提取在C4中引入的补丁和修改,然后在C3的基础上应用一次。在Git中,这种操作就叫做:变基。


你可以使用rebase命令将提交到某一分支上的所有修改都移动至另一个分支上,就好像“重新播放”一样。
在上面这个例子中,运行:

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it ...
Applying: added staged command 

它的原理是首先找到这两个分支(即当前分支experiment、变基操作的目标基底分支master)的最近共同祖先C2,然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件,然后将当前分支指向目标基底C3,最后以此将之前另存为临时文件的修改依序应用(译注:写明了commit id,以便理解,下同)
这里写图片描述
注:将C4中的修改变基到C3上


现在回到master分支,进行一次快进合并。

$git checkout master
$ git merge experiment

这里写图片描述
注:分支的快进合并


此时,C4’ 指向的快照就和上面使用merge命令的例子中C5指向的快照一模一样了。这两种整合方法的最终结果没有任何区别,但是变基使得提交历史更加整洁。你在查看一个经过变基的分支的记录时会发现,尽管实际的开发工作是并行的,但它们看上去就像是串行的一样,提交历史是一条直线没有分叉。

  • (c)更有趣的变基例子
    在对两个分支进行变基时,所生成的“重放”并不一定要在目标分支上应用,你也可以指定另外的一个分支进行应用。如:你创建了一个特性分支server,为服务端添加了一些功能,提交了C3和C4.然后从C3上创建了特性分支client,为客户端添加了一些功能,提交了C8和C9.最后,你回到server分支,又提交了C10.
    这里写图片描述
    注:从一个特性分支里再分出一个特性分支的提交历史

假设你希望将client中的修改合并到主分支并发布,但暂时并不想合并server中的修改,因为他们还需要经过更全面的测试。这时,你就可以使用git rebase命令的–onto选项,选中client分支里但不在server分支里的修改(即C8和C9),将它们在master分支上重放:

$ git rebase --onto master server client 

以上命令的意思是:“取出client分支,找出处于client 分支和server 分支的共同祖先之后的修改,然后把它们在master分支上重放一遍”。这理解起来有一点复杂,不过效果非常酷。
这里写图片描述
注:截取特性分支上的另外一个特性分支,然后变基到其他分支


现在可以快进合并master分支了

$ git checkout master
$ git merge client

这里写图片描述
注:快进合并master分支,使之包含来自client分支的修改


接下来你决定将server分支中的修改也整合进来。使用git rebase [basebranch] [topicbranch] 命令可以直接将特性分支(即本例中的server)变基到目标分支(即master分支)这样做能省去你先切换到server分支,再对其执行变基命令的多个步骤。

$ git rebase master server

如图将server中的修改变基到master上所示,server中的代码被“续”到了master后面。
这里写图片描述
注:将server中的修改变基到master上


然后就可以快进合并主分支master了:

$git checkout master
$git merge server

至此,client和server分支中的修改都已经整合到主分支里了,你就可以删除这两个分支,最终提交历史会变成这样子:
这里写图片描述
注:最终提交历史

$ git branch -d client
$ git branch -d server

  • (d)变基的风险
    变基准则:不要对在你的仓库外有副本的分支执行变基

变基的操作的实质是丢弃一些现有的提交,然后相应的新建一些内容一样但实际上不同的提交

如果你已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用git rebase 命令重新整理了提交并再次推送,你的同伴因此将不得不再次将他们手头的工作与你的提交进行整合,如果接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。


让我们来看一个在公开的仓库上执行变基操作所带来的问题。假设你从一个中央服务器克隆然后在它的基础上进行了一些开发。你的提交历史如图所示:
这里写图片描述
注:克隆一个仓库,然后在它的基础上进行一些开发


然后,某人又向中央服务器提交了一些修改,其中还包括一次合并。你抓取了这些在远程分支上的修改,并将其合并到你本地开发分支,然后你的提交历史就会变成这样。
这里写图片描述
注:抓取别人的提交,合并到自己的开发分支


接下来,这个人又决定把合并操作回滚,改用变基;继而有用git push –force命令覆盖了服务器上的提交历史。之后你从服务器抓取更新,会发现多出来一些新的提交。
这里写图片描述
注:有人推送了经过变基的提交,并丢弃了你的本地开发所基于的一些提交


结果就是你们两个人的处境都十分尴尬。如果你执行git pull 命令,你将合并来自两条提交历史的内容,生成一个新的合并提交,最终仓库会如图所示:
这里写图片描述
注:你将相同内容又合并了一次,生成了一个新的提交


此时如果你执行git log 命令,你会发现有两个提交的作者、日期、日志居然是一样的,这会令人感到混乱。此为,如果你将这一堆又推送到服务器上,你实际是将那些已经被变基抛弃的提交有找了回来,这回令人感到更加混乱。很明显对方并不想在提交历史中看到C4和C6,因为之前就是他把这两个提交通过变基丢弃的。

  • (e)用变基解决变基
    如果你 真的 遭遇了类似的处境,Git还有一些高级的方法可以帮到你。如果团队中的某人强制推送并覆盖了一些你所基于的提交,你需要做的就是检查你做了哪些修改,以及他们覆盖了哪些修改。

实际上,Git除了对整个提及计算SHA-1校验和以外,也对本次提交所引入的修改计算了校验和——即“patch-id”。
如果你拉取被覆盖过的更新并将你手头的工作基于此进行变基的话,一般情况下Git都能分辨出哪些是你的修改,并把它们应用到新的分支上。


举个例子,如果遇到前面提到的 有人推送了经过变基的提交,并丢弃了本地开发所基于的一些提交哪种处境,如果我们不是执行合并,而是执行git rebase teamone/master
Git将会:
(I)检查哪些提交时我们的分支上独有的(C2,C3,C4,C6,C7)
(II)检查其中哪些提交不是合并操作的结果(C2,C3,C4)
(III)检查哪些提交在对方覆盖更新时并没有被纳入目标分支(只有C2和C3,因为C4其实就是C4’)
(IV)把查到的这些提交应用在teamone/master上面
从而我们将得到与 你将相同的内容又合并了一次,生成一个新的提交中不同的结果,如图 在一个被变基然后强制推送的分支上再次执行变基所示。
这里写图片描述
注:在一个被变基然后强制推送的分支上再次执行变基


要想上述方案有效,还要对方在变基时确保C4‘和C4是几乎一样的。否则变基操作将无法识别,并新建另一个类似C4的补丁(而这个补丁很可能无法整洁的整合入历史,因为补丁中的修改已经存在于某个地方了。)

只要你把变基命令当作是在推送前清理提交使之整洁的工具,并且只在从未推送至共用仓库的提交上执行变基命令,就不会有事。


假如在那些已经被推送至共用仓库的提交上执行变基命令,并因此丢弃了一些别人的开发所基于的提交,那你就有大麻烦了,你的同事也会因此鄙视你。

  • (f)变基 vs. 合并
    至此,你已在实战中学习了变基和合并的用法,你一定会想问,到底哪种方式更好。在回答这个问题之前,让我们退后一步,想讨论一下提交历史到底意味着什么。

有一种观点认为,仓库的提交历史即是 记录实际发生过什么。他是针对历史的文档,本身就有价值,不能乱改。从这个角度看来,改变提交历史是一种亵渎,你使用谎言掩盖了实际发生过的事情。如果由合并产生的提交历史是一团糟怎么办?既然事实就是如此,那么这些痕迹就应该被保留下来,让后人能够查阅。


另外一种观点则正好相反,他们认为提交历史是项目过程中发生的事。没人会出版一本书的第一版草稿,软件维护手册也是需要反复修订才能方便使用。持这一观点的人会使用rebase及filter-branch等工具来编写故事,怎么方便后来的读者就怎么写。


现在,让我们回到之前的问题上来,到底合并还是变基好?希望你能明白,这并没有一个简单的答案。Git是一个非常强大的工具,它允许你对提交历史做许多事情,但每个团队、每个项目对此的需求并不相同。
既然你已经分别学习了两者的用法,相信你能够根据实际情况作出明智的选择。


总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已经推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。

3.实践应用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值