Git 分支(分布式版本控制系统)

时间:2021-07-29 06:43:47

前言

  • 几乎所有的版本控制系统都以某种形式支持分支。使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。在很多版本控制系统中,这是一个略微低效的过程——常常需要完全创建一个源代码目录的副本。对于大项目来说,这样的过程会耗费很多时间。

  • 有人把 Git 的分支模型称为它的“必杀技特性”,也正因为这一特性,使得 Git 从众多版本控制系统中脱颖而出。为何 Git 的分支模型如此出众呢?Git 处理分支的方式可谓是难以置信的轻量,创建新分支这一操作几乎能在瞬间完成,并且在不同分支之间的切换操作也是一样便捷。与许多其它版本控制系统不同,Git 鼓励在工作流程中频繁地使用分支与合并,哪怕一天之内进行许多次。理解和精通这一特性,你便会意识到 Git 是如此的强大而又独特,并且从此真正改变你的开发方式。

1、分支简介

  • Git 保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照。在进行提交操作时,Git 会保存一个提交对象(commit object)。

  • 知道了 Git 保存数据的方式,我们可以很自然的想到该提交对象会包含一个指向暂存内容快照的指针。但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象,

  • 为了更加形象地说明,我们假设现在有一个工作目录,里面包含了三个将要被暂存和提交的文件。暂存操作会为每一个文件计算校验和(SHA-1 哈希算法),然后会把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 对象来保存它们),最终将校验和加入到暂存区域等待提交。

    $ git add README test.rb LICENSE
    $ git commit -m "The initial commit of my project"
  • 当使用 git commit 进行提交操作时,Git 会先计算每一个子目录的校验和,然后在 Git 仓库中这些校验和保存为树对象。随后,Git 便会创建一个提交对象,它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。如此一来,Git 就可以在需要的时候重现此次保存的快照。

  • 现在,Git 仓库中有五个对象:三个 blob 对象(保存着文件快照)、一个树对象(记录着目录结构和 blob 对象索引)以及一个提交对象(包含着指向前述树对象的指针和所有提交信息)。

    Git 分支(分布式版本控制系统)

  • 做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针。

    Git 分支(分布式版本控制系统)

  • Git 的分支,其实本质上仅仅是指向提交对象的可变指针。Git 的默认分支名字是 master。在多次提交操作之后,你其实已经有一个指向最后那个提交对象的 master 分支。它会在每次的提交操作中自动向前移动。

    Git 分支(分布式版本控制系统)

    • Git 的 master 分支并不是一个特殊分支。它就跟其它分支完全没有区别。之所以几乎每一个仓库都有 master 分支,是因为 git init 命令默认创建它,并且大多数人都懒得去改动它。

2、分支创建

  • Git 是创建新分支很简单,它只是为你创建了一个可以移动的新的指针。比如,创建一个 testing 分支,你需要使用 git branch 命令。

    # git branch [分支名]
    $ git branch testing

    Git 分支(分布式版本控制系统)

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

    Git 分支(分布式版本控制系统)

  • 你可以简单地使用 git log 命令查看各个分支当前所指的对象。提供这一功能的参数是 --decorate

    $ git log --oneline --decorate
    f30ab (HEAD, master, testing) add feature #32 - ability to add new
    34ac2 fixed bug #1328 - stack overflow under certain conditions
    98ca9 initial commit of my project
    • 正如你所见,当前 “master” 和 “testing” 分支均指向校验和以 f30ab 开头的提交对象。

3、分支切换

  • 要切换到一个已存在的分支,你需要使用 git checkout 命令。 我们现在切换到新创建的 testing 分支去。

    # git checkout [分支名]
    $ git checkout testing
    • 这样 HEAD 就指向 testing 分支了。

      Git 分支(分布式版本控制系统)

  • 那么,这样的实现方式会给我们带来什么好处呢?现在不妨再提交一次。

    $ vim test.rb
    $ git commit -a -m 'made a change'

    Git 分支(分布式版本控制系统)

    • 如图所示,你的 testing 分支向前移动了,但是 master 分支却没有,它仍然指向运行 git checkout 时所指的对象。这就有意思了,现在我们切换回 master 分支看看。

      $ git checkout master
      ![](https://images2018.cnblogs.com/blog/993906/201803/993906-20180310010128358-1739885695.png)
      • 这条命令做了两件事。一是使 HEAD 指回 master 分支,二是将工作目录恢复成 master 分支所指向的快照内容。也就是说,你现在做修改的话,项目将始于一个较旧的版本。本质上来讲,这就是忽略 testing 分支所做的修改,以便于向另一个方向进行开发。
    • 我们不妨再稍微做些修改并提交。

      $ vim test.rb
      $ git commit -a -m 'made other changes'
      ![](https://images2018.cnblogs.com/blog/993906/201803/993906-20180310010133774-1495305256.png)
      • 现在,这个项目的提交历史已经产生了分叉。因为刚才你创建了一个新分支,并切换过去进行了一些工作,随后又切换回 master 分支进行了另外一些工作。上述两次改动针对的是不同分支:你可以在不同分支间不断地来回切换和工作,并在时机成熟时将它们合并起来。而所有这些工作,你需要的命令只有 branch、checkout 和 commit。

      • 你可以简单地使用 git log 命令查看分叉历史。运行 git log --oneline --decorate --graph --all,它会输出你的提交历史、各个分支的指向以及项目的分支分叉情况。

        $ git log --oneline --decorate --graph --all
        * c2b9e (HEAD, master) made other changes
        | * 87ab2 (testing) made a change
        |/
        * f30ab add feature #32 - ability to add new formats to the
        * 34ac2 fixed bug #1328 - stack overflow under certain conditions
        * 98ca9 initial commit of my project
  • 由于 Git 的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,所以它的创建和销毁都异常高效。创建一个新分支就相当于往一个文件中写入 41 个字节(40 个字符和 1 个换行符),如此的简单能不快吗?这与过去大多数版本控制系统形成了鲜明的对比,它们在创建分支时,将所有的项目文件都复制一遍,并保存到一个特定的目录。完成这样繁琐的过程通常需要好几秒钟,有时甚至需要好几分钟。所需时间的长短,完全取决于项目的规模。而在 Git 中,任何规模的项目都能在瞬间创建新分支。同时,由于每次提交都会记录父对象,所以寻找恰当的合并基础(译注:即共同祖先)也是同样的简单和高效。这些高效的特性使得 Git 鼓励开发人员频繁地创建和使用分支。

4、分支合并

  • 假设你已经修正了 #53 问题,并且打算将你的工作合并入 master 分支。为此,你需要合并 iss53 分支到 master 分支。你只需要检出到你想合并入的分支,然后运行 git merge 命令。

    $ git checkout master
    Switched to branch 'master'
    # git merge [要合并到当前分支的分支名]
    $ git merge iss53
    Merge made by the 'recursive' strategy.
    index.html | 1 +
    1 file changed, 1 insertion(+)
    • 在这种情况下,你的开发历史从一个更早的地方开始分叉开来(diverged) 因为,master 分支所在提交并不是 iss53 分支所在提交的直接祖先,Git 不得不做一些额外的工作。出现这种情况的时候,Git 会使用两个分支的末端所指的快照(C4 和 C5)以及这两个分支的工作祖先(C2),做一个简单的三方合并。

      Git 分支(分布式版本控制系统)

    • Git 将三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。

      Git 分支(分布式版本控制系统)

    • 需要指出的是,Git 会自行决定选取哪一个提交作为最优的共同祖先,并以此作为合并的基础;这和更加古老的 CVS 系统或者 Subversion(1.5 版本之前)不同,在这些古老的版本管理系统中,用户需要自己选择最佳的合并基础。Git 的这个优势使其在合并操作上比其他系统要简单很多。

  • 有时候合并操作不会如此顺利。如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,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 命令来查看那些因包含合并冲突而处于未合并(unmerged)状态的文件。

      $ 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 分支所指示的版本在 ======= 的下半部分。为了解决冲突,你必须选择使用由 ======= 分割的两部分中的一个,或者你也可以自行合并这些内容。例如,你可以通过把这段内容换成下面的样子来解决冲突。

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

    • 如果你对结果感到满意,并且确定之前有冲突的的文件都已经暂存了,这时你可以输入 git commit 来完成合并提交。默认情况下提交信息看起来像下面这个样子。

      Merge branch 'iss53'
      
      Conflicts:
      index.html
      #
      # It looks like you may be committing a merge.
      # If this is not correct, please remove the file
      # .git/MERGE_HEAD
      # and try again. # Please enter the commit message for your changes. Lines starting
      # with '#' will be ignored, and an empty message aborts the commit.
      # On branch master
      # All conflicts fixed but you are still merging.
      #
      # Changes to be committed:
      # modified: index.html
      #

5、分支管理

  • 1)删除分支

    # git branch -d [分支名]
    $ git branch -d iss53
  • 2)查看分支列表

    • 1> 如果不加任何参数运行它,会得到当前所有分支的一个列表。

      $ git branch
        iss53
      * master
      testing
      • 注意 master 分支前的 * 字符:它代表现在检出的那一个分支(也就是说,当前 HEAD 指针所指向的分支)。这意味着如果在这时候提交,master 分支将会随着新的工作向前移动。
    • 2> 如果需要查看每一个分支的最后一次提交,可以运行 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
    • 3> --merged--no-merged 这两个有用的选项可以过滤这个列表中已经合并或尚未合并到当前分支的分支。

      • 如果要查看哪些分支已经合并到当前分支,可以运行 git branch --merged

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

        $ git branch --no-merged
          testing
        • 这里显示了其他分支。因为它包含了还未合并的工作,尝试使用 git branch -d 命令删除它时会失败。

          $ git branch -d testing
          error: The branch 'testing' is not fully merged.
          If you are sure you want to delete it, run 'git branch -D testing'.
        • 如果真的想要删除分支并丢掉那些工作,可以使用 -D 选项强制删除它。

6、分支开发工作流

  • 1)长期分支

    • 因为 Git 使用简单的三方合并,所以就算在一段较长的时间内,反复把一个分支合并入另一个分支,也不是什么难事。也就是说,在整个项目开发周期的不同阶段,你可以同时拥有多个开放的分支;你可以定期地把某些特性分支合并入其他分支中。

    • 许多使用 Git 的开发者都喜欢使用这种方式来工作,比如只在 master 分支上保留完全稳定的代码——有可能仅仅是已经发布或即将发布的代码。他们还有一些名为 develop 或者 next 的平行分支,被用来做后续开发或者测试稳定性——这些分支不必保持绝对稳定,但是一旦达到稳定状态,它们就可以被合并入 master 分支了。这样,在确保这些已完成的特性分支(短期分支,比如之前的 iss53 分支)能够通过所有测试,并且不会引入更多 bug 之后,就可以合并入主干分支中,等待下一次的发布。

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

      Git 分支(分布式版本控制系统)

    • 通常把他们想象成流水线(work silos)可能更好理解一点,那些经过测试考验的提交会被遴选到更加稳定的流水线上去。

      Git 分支(分布式版本控制系统)

    • 你可以用这种方法维护不同层次的稳定性。一些大型项目还有一个 proposed(建议)或 pu: proposed updates(建议更新)分支,它可能因包含一些不成熟的内容而不能进入 next 或者 master 分支。这么做的目的是使你的分支具有不同级别的稳定性;当它们具有一定程度的稳定性后,再把它们合并入具有更高级别稳定性的分支中。 再次强调一下,使用多个长期分支的方法并非必要,但是这么做通常很有帮助,尤其是当你在一个非常庞大或者复杂的项目中工作时。

  • 2)特性分支

    • 特性分支对任何规模的项目都适用。特性分支是一种短期分支,它被用来实现单一特性或其相关工作。也许你从来没有在其他的版本控制系统(VCS)上这么做过,因为在那些版本控制系统中创建和合并分支通常很费劲。然而,在 Git 中一天之内多次创建、使用、合并、删除分支都很常见。

    • 你已经在上一节中你创建的 iss53 和 hotfix 特性分支中看到过这种用法。 你在上一节用到的特性分支(iss53 和 hotfix 分支)中提交了一些更新,并且在它们合并入主干分支之后,你又删除了它们。这项技术能使你快速并且完整地进行上下文切换(context-switch),因为你的工作被分散到不同的流水线中,在不同的流水线中每个分支都仅与其目标特性相关,因此,在做代码审查之类的工作的时候就能更加容易地看出你做了哪些改动。你可以把做出的改动在特性分支中保留几分钟、几天甚至几个月,等它们成熟之后再合并,而不用在乎它们建立的顺序或工作进度。

    • 考虑这样一个例子,你在 master 分支上工作到 C1,这时为了解决一个问题而新建 iss91 分支,在 iss91 分支上工作到 C4,然而对于那个问题你又有了新的想法,于是你再新建一个 iss91v2 分支试图用另一种方法解决那个问题,接着你回到 master 分支工作了一会儿,你又冒出了一个不太确定的想法,你便在 C10 的时候新建一个 dumbidea 分支,并在上面做些实验。你的提交历史看起来像下面这个样子。

      Git 分支(分布式版本控制系统)

      • 现在,假定两件事情:我们最终决定使用第二个解决方案,即 iss91v2 中的办法;另外,我们把 dumbidea 分支拿给同事们看了以后,发现它竟然是个天才之作。所以接下来,我们准备抛弃原来的 iss91 分支(实际上会丢弃 C5 和 C6),直接在主干中并入另外两个分支。最终的提交历史将变成图 3-21 这样。

        Git 分支(分布式版本控制系统)

    • 请务必牢记这些分支全部都是本地分支,这一点很重要。当你在使用分支及合并的时候,一切都是在你自己的 Git 仓库中进行的,完全不涉及与服务器的交互。

7、远程分支

  • 1)远程分支

    • 远程引用是对远程仓库的引用(指针),包括分支、标签等等。你可以通过 git ls-remote (remote) 来显式地获得远程引用的完整列表,或者通过 git remote show (remote) 获得远程分支的更多信息。然而,一个更常见的做法是利用远程跟踪分支。

    • 远程跟踪分支是远程分支状态的引用。它们是你不能移动的本地引用,当你做任何网络通信操作时,它们会自动移动。

    • 它们以 (remote)/(branch) 形式命名。例如,如果你想要看你最后一次与远程仓库 origin 通信时 master 分支的状态,你可以查看 origin/master 分支。你与同事合作解决一个问题并且他们推送了一个 iss53 分支,你可能有自己的本地 iss53 分支;但是在服务器上的分支会指向 origin/iss53 的提交。

    • 这可能有一点儿难以理解,让我们来看一个例子。假设你的网络里有一个在 git.ourcompany.com 的 Git 服务器。如果你从这里克隆,Git 的 clone 命令会为你自动将其命名为 origin,拉取它的所有数据,创建一个指向它的 master 分支的指针,并且在本地将其命名为 origin/master。Git 也会给你一个与 origin 的 master 分支在指向同一个地方的本地 master 分支,这样你就有工作的基础。

    • 远程仓库名字 “origin” 与分支名字 “master” 一样,在 Git 中并没有任何特别的含义一样。同时 “master” 是当你运行 git init 时默认的起始分支名字,原因仅仅是它的广泛使用,“origin” 是当你运行 git clone 时默认的远程仓库名字。如果你运行 git clone -o booyah,那么你默认的远程分支名字将会是 booyah/master。

      Git 分支(分布式版本控制系统)

      • 一次 Git 克隆会建立你自己的本地分支 master 和远程分支 origin/master,并且将它们都指向 origin 上的 master 分支。
    • 如果你在本地的 master 分支做了一些工作,然而在同一时间,其他人推送提交到 git.ourcompany.com 并更新了它的 master 分支,那么你的提交历史将向不同的方向前进。也许,只要你不与 origin 服务器连接,你的 origin/master 指针就不会移动。

      Git 分支(分布式版本控制系统)

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

      Git 分支(分布式版本控制系统)

    • 为了演示有多个远程仓库与远程分支的情况,我们假定你有另一个内部 Git 服务器,仅用于你的 sprint 小组的开发工作。这个服务器位于 git.team1.ourcompany.com。你可以运行 git remote add 命令添加一个新的远程仓库引用到当前的项目,这个命令我们会在 Git 基础中详细说明。将这个远程仓库命名为 teamone,将其作为整个 URL 的缩写。

      Git 分支(分布式版本控制系统)

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

      Git 分支(分布式版本控制系统)

  • 2)推送分支

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

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

      # 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 分支。”
    • 你也可以运行以下命令,它会做同样的事,相当于它说,“推送本地的 serverfix 分支,将其作为远程仓库的 serverfix 分支” 可以通过这种格式来推送本地分支到一个命名不相同的远程分支。

      # git push [remote] [branch]:[remote-branch]
      $ git push origin 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 [branch] [remotename]/[branch]
      $ 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。
  • 3)跟踪分支

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

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

      # git checkout --track [remotename]/[branch]
      $ 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 [remotename]/[branch]
      $ 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

  • 4)拉取分支

    • git fetch 命令从服务器上抓取本地没有的数据时,它并不会修改工作目录中的内容。它只会获取数据然后让你自己合并。然而,有一个命令叫作 git pull 在大多数情况下它的含义是一个 git fetch 紧接着一个 git merge 命令。如果有一个像之前章节中演示的设置好的跟踪分支,不管它是显式地设置还是通过 clonecheckout 命令为你创建的,git pull 都会查找当前分支所跟踪的服务器与分支,从服务器上抓取数据然后尝试合并入那个远程分支。

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

  • 5)删除远程分支

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

      # git push [remotename] --delete [branch]
      $ git push origin --delete serverfix
      To https://github.com/schacon/simplegit
      - [deleted] serverfix
      • 基本上这个命令做的只是从服务器上移除这个指针。Git 服务器通常会保留数据一段时间直到垃圾回收运行,所以如果不小心删除掉了,通常是很容易恢复的。