HOME/git/

git分支

Article Outline
TOC
Collection Outline

title: git分支 date: 2019-04-07 19:25:05 tags: git categories: git

分支简介

在前面已经介绍过了,git中保存的是一系列不同时刻的文件快照,而不是文件的变化或差异。

在进行提交操作的时候,git会保存一个提交对象(commit object)。从这里可以看出,每次提交的提交对象包含一个指向暂存内容快照的指针。在每一次git commit的提交操作时,git会计算项目路径下的子目录的校验和,然后在git仓库中将这个校验和保存为树对象,随后,git会创建一个提交对象,除了上述信息外,还包含指向这个树对象的指针。这样一来,git就会在需要的时候重现此次保存的快照。

git的分支,其实本质上仅仅是指向提交对象的可变指针。默认情况下,git的分支名字是master,所有的提交都是在master分支上进行的操作。

分支创建

使用git branch命令就可以创建一个分支,也就是创建了一个可以移动的新的指针。如:

git branch newbranch

这会在当前所在的提交对象上创建一个指针。git使用一个特殊的指针HEAD跟踪不同的分支,需要注意的是,不同与别的版本控制系统,HEAD在git中只是指向当前所在的本地分支的一个指针。

在创建一个新的分支后,当前的HEAD并不会切换到新的分支上。

分支切换

上面我们创建了一个新的分支newbranch,但是我们的HEAD并没有切换到新的分支上,要切换到新的分支,我们需要使用git checkout命令。如:

git checkout newbranch

这样HEAD就指向了newbranch分支,切换分支时目标分支必须存在,否则会显示找不到目标分支的错误。newbranch

过程如上图所示,这样做的好处在于,如果我们进行下一次提交的时候,newbranch分支会继续向前移动,而master分支则不变,仍然处于C3这个位置,如下:commit

这时候,可以切换回master分支:

git checkout master

结果如下所示:chenkback

可以看出,这条命令做了两件事:1、使HEAD指回master分支;2、将工作目录恢复成master分支所指向的快照内容。这时候,如果我们提交更改,就会使得开发方向向另外的方向发展。

如下所示: twobranch

上述过程阐述了在不同分支之间切换和工作的过程,所用到的命令包括branch(增加分支)checkout(切换分支)commit(提交)。当然,这些分支在适当的时机可以进行合并,后续会有相关说明。

我们可以使用git log命令完整的查看分支历史,运行git log --oneline --decorate --graph --all,将会输出提交历史、各个分支的指向及项目的分支情况。

分支的新建与合并

上一部分的内容中,我们简单的了解了分支的创建与切换等功能,这一部分我们从一个例子入手,整体的回顾一下git分支的工作流。

假设我们在开发一个大型项目,针对不同的客户有不同的定制化需求,目前我们的master分支主要开发一些通用功能。现在,你接到了一个新的任务,为客户A定制化开发一个新功能。

新建分支

为了应对定制化功能的开发,首先我们需要新建一个分支,并且切换到新分支上进行开发工作,可以使用如下命令操作:

git checkout -b newFunction

上述命令可以在仓库中创建一个新的分支newFunction,并且在创建完成后切换到新建的分支上。等于是实现了branchcheckout两条命令的功能。

假设我们在newFunction分支上的开发工作完成了,切换回master分支,这时候,查看git状态就会发现,master分支还是停留在切换到newFunction之前的最后一次提交的状态。

分支的合并

假设,我们新分支上的功能代码可以提供给整个项目复用,那么,我们就不必要再重头开始编写代码,而是将newFunction分支合并到master分支即可。这个操作可以通过merge实现,下来看一下具体的流程:

# 首先,我们目前处于master分支上
# 需要建立并切换到newFunction分支上进行开发工作
git checkout -b newFunction
# 接着,我们完成了开发工作并提交了相关代码
# 此时,需要将newFunction中的代码合并到master中
git checkout master
git merge newFunction

冲突时的代码合并

假设,在新建的newFunction分支上,我们对于代码文件中的某一部分做出了修改。同时,另外的项目组中有同时又创建了一个分支newIssue,也对同样的代码中的相同部分做出了修改。这时候,两个项目组都想要将分支合并到master中的时候,这里就产生了冲突。

这时候,git会做出合并,但是并不会自动的创建一个新的合并提交,而是在等待认为的去解决这部分冲突内容。产生合并冲突后,可以在项目文件中使用git status来查看由于冲突存在而处于未合并(unmerged)状态的文件。

产生冲突的输出信息:

chongtu

打开产生冲突的文件,如下所示:

unmergefile

图中的HEAD所指示的版本在======的上半部分,产生冲突的版本在======下半部分。解决冲突的方式就是保留所需要部分的内容,然后删除<<<<<<======>>>>>>包含的内容。在解决完冲突后,对于每个文件使用git add将其跟踪,然后再进行提交。

那么在完成分支合并后,多余的分支就可以删除了,使用git branch -d <branch_name>可以删除多余的分支。

分支管理

通过上面的内容,现在可以创建、合并及删除分支。除了上述的分支操作外,接下来我们看一看一些其他常用的分支管理工具。
git branch直接使用此命令可以查看当前git仓库下的分支列表。

  • -v:可以查看当前仓库下各个分支的最后一次提交的信息;
  • --merged--no-merged:这两个选项可以过滤这个列表中已经合并或尚未合并到当前分支的分支;
    • -d-D:删除指定的分支,如果指定分支包含未合并的内容,则可以选择-D删除并丢掉那些工作。

前面说到的这些分支管理基本上都是在本地进行操作的方法,下来我们分析以下远程仓库的分支相关内容。
当我们从远程仓库中获取一个git仓库的内容的时候,git的clone命令会自动的为这份代码命名为origin,并且创建一个指向它的master分支的指针,在本地将其命名为origin/master。 需要注意的是,origin这个名称并没有特殊含义,只是一个默认的名称,如果在clone远程代码的时候使用git clone -o myremote,那么默认的远程分支名称将会变成myremote/master。 如果你在本地的master分支做了一些文件的改动,同时,在其他地方有人推送提交了远程的仓库,并更新了远程的master分支,那么你的提交历史将向不同的方向前进。这时候,如果需要同步你的工作,就需要运行git fetch origin命令,这个命令会查找远程服务器,从其中获取本地没有的数据,并且更新本地数据库,移动origin/master指针指向新的、更新后的位置。

**fetch VS. pull**    
我们知道有一个`pull`的命令也可以拉取远程代码到本地,上面介绍了使用`fetch`同步远程和本地的代码。那么,这两者有什么区别呢?当我们使用`fetch`的时候,它只会从服务器上获取本地没有的数据,并不会修改工作目录中的文件。也就是说,下载好的数据还需要你自己手动进行合并。而`pull`命令更像是`fetch`和`merge`命令的组合,它会查找当前分支所跟踪的服务器与分支,从服务器上抓取数据然后尝试合并入那个远程分支。  
为了避免不必要的异常,我们在进行同步操作的时候,最好选择使用`fetch`和`merge`命令分步进行。  

我们可以使用-d命令删除本地不需要的分支,那么对于不需要的远程分支应该怎样操作呢?也很简单,在使用push的时候,加上参数--delete,类似于这样git push origin --delete oldbranch,这样就可以在服务器上删除oldbranch分支。
实质上,这个命令只是从服务器上移除了相应的指针,一般而言,git服务器会保留数据一段时间,直到垃圾回收运行,因此,如果误删分支,通常还是可以恢复的。

变基

在git中,整合来自不同分支的修改方法主要有两种:mergerebase。前面已经讲过merge的用法了,这一部分主要解释一下rebase(变基)的用法。
前面介绍过,合并分支最容易的方法就是使用merge命令,如下: merge.pngC2C3合并后,生成一个新的快照C4。 使用变基的话,可以将C3中做出的修改提取出来,在C2的基础上应用一次。rebase的实质就是将提交到某一个分支上的修改都移动到另外一个分支上。其原理是,先找到当前分支及变基操作的目标分支这两个分支的最近共同祖先,然后对比当前分支相对于该祖先分支的历次提交,提取相应的修改并存为临时文件,然后将当前分支指向变基目标分支,最后以此将之前另存为临时文件的修改依次应用。
对于变基的操作,我们实质上可以通过merge实现,但是变基操作使得提交历史更加整洁。使用变基的目的是确保在向远程分支推送时保持提交历史的整洁。在本地的各个分支进行开发后,需要提交的时候,将代码先变基到提交的分支上,然后再向主项目提交修改,这样项目的维护者就不需要进行整合工作。
需要注意的是,不要对在你的仓库外有副本的分支执行变基。

总结

在这一部分,我们对git的分支有了基本的了解,实际上,以上内容中并没有完全的涵盖主题。其中关于变基的操作,只是做出了简单的介绍,在后续的笔记中会根据实例再详细的介绍这一小结内容。