git基础(7)-分支(branch)

Git 分支本质是指向提交对象(commit object)的可变指针,默认是指向最后那个提交对象,每次提交后,都会自动向前移动。HEAD是特殊指针,指向当前所在的本地分支(可理解为当前分支的别称)。

Git 是如何保存数据的?

  • Git 保存的不是文件的一系列或变更集或者差异,而是一系列不同时刻的文件快照。

  • 每次提交时,Git 会保存一个提交对象。该对象包含:

    • 一个指向暂存内容快照的指针(树对象)
    • 作者姓名、邮箱、提交解决信息
    • 指向它的父对象(上一次提交对象)的指针
      • 首次提交产生的提交对象没有父对象
      • 其他普通提交操作产生的提交对象有一个父对象
      • 由多个分支合并产生的提交对象有多个父对象
  • 例子
    假设有一个工作目录,包含三个将要被暂存、提交的文件。
    暂存操作

    • 为每一个文件计算校验和
    • 把当前版本的文件快照保存到 Git 仓库中(使用 blob 对象来保存文件快照)
    • 将校验和加入到暂存区域 等待提交

    提交操作:

    • Git 先计算每一个子目录的校验和
    • 在 Git 仓库中把这些校验和保存为树对象
    • Git 创建一个提交对象

    此时,Git 仓库

    • 3 个 blob 对象(保存文件快照)

    • 1 个树对象(记录着目录结构和 blob 对象索引)

    • 1 个提交对象 (包含 树对象的指针,和所有提交信息)

      首次提交对象及其树结构

      修改后再次提交,此次产生的提交对象会包含一个指向上次提交对象(父对象)的指针

      提交对象及其父对象

Git 分支

Git 分支,其本质仅仅时指向其中一个提交对象的可变指针。默认分支名字是 master。

A branch in Git is simply a lightweight movable pointer to one of these commits.

分支及其提交历史

创建分支

创建分支只是创建了一个可以移动的新的指针。名为HEAD的特殊指针,指向当前所在的本地分支

2个指向相同提交历史的分支

  • git branch <branchname>: 创建一个分支
  • git branch -b <branchname>: 创建分支,并切换,创建:git branch <branchname>, 切换:git checkout <branchname>

切换分支

git checkout命令,切换分支,实际上是改变 HEAD 的指向,同时将工作目录恢复到该分支所指向的快照内容(最后一次提交时的样子)
git checkout testing,切换到 testing 分支,此时 HEAD 指向 testing 分支

切换分支,HEAD指向testing

testing分支,修改再提交一次,testing分支向前移动,HEAD也随着提交自动向前移动,但master分支保持不动。

HEAD随分支提交自动向前移动

合并分支

git merge命令,合并指定分支到当前分支。

  • Fast-forward, “快进模式”,由于当前分支所指向的提交是并入分支的直接上游,所有 Git 只是简单的将指针向前移动。
    通俗来讲:当试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么在合并时,只会简单的将指针向前推进。

  • 'recursive' strategy,在合并时,需要递归的查找两个分支的分叉点。

    三方合并

    Git 会使用两个分支的末端的快照(C4 、C5)和这两个分支的共同祖先(C2),做一个简单的三方合并。再将结果做一个新的快照并自动创建一个新的提交(C6)指向它。

    合并提交

分支合并冲突

如果在两个不同分支上,对同一个文件的同一个部分进行了不同的修改,分支合并时就会产生合并冲突。 此时,Git 做了合并,但没有自动创建一个新的合并提交。,需要解决冲突,在使用git add命令来将其标记为冲突已解决,然后git commit完成提交。具体步骤:

  • git status 查看因 包含合并冲突 而处于未合并(unmerged)状态的文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ 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")

    未合并状态的文件中都会加入标准的冲突解决标记,需要打开文件,手动修改文件,解决冲突。

    1
    2
    3
    4
    5
    6
    7
    <<<<<<< 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
  • 解决了所有文件冲突后, git add将每个文件标记为 冲突已解决。注意: 一旦暂存这些冲突文件,Git 就会将它们标记为冲突已解决。所以最后是修改一个文件,暂存一个文件,以免漏掉。

  • git commit,提交合并。

分支管理

  • 查看分支

    git branch 不加任何参数,列出所有分支,* 代表当前分支(当前 HEAD 指针所指向的分支)

    1
    2
    3
    4
    $ git branch
    * develop
    master
    release

    git branch -v,每个分支的最后一次提交

    1
    2
    3
    4
    $ git branch -v
    * develop b1e18237 【预警查询】 分页滑动,触发页面样式更新函数,屏幕自适应
    master 3ea94fa9 Merge branch 'release' into 'master'
    release a98f6667 Merge branch 'develop' into 'release'

    git branch --merged/--no-merged,过滤分支列表中已经合并或尚未合并到当前分支的分支。

    1
    2
    3
    $ git branch --merged
    * develop
    release

    在列表中没有*号的分支,通常是已经将它们的分支合并到另一个分支,可已使用git branch -d 删除的。