Git 学习笔记(一)

1 Git 的安装

对于 Ubuntu: sudo apt-get install git

对于 MacOS(前提是安装好了 homebrew 工具): brew install git

2 Git 的配置

  • 全局配置文件为: /etc/gitconfig 通过 --system 添加。
  • 当前用户的配置文件位置为: $HOME/.gitconfig 通过 --global 添加。
  • 当前项目的配置文件为: .git/config 通过 --local 添加。

每一个级别的配置都会覆盖上层的相同配置。

一般情况下在当前用户的配置文件中添加个人信息: git config --global user.name "用户名" git config --global user.email "邮箱" git config --global core.editor "vim"

配置完成后,通过以下命令查看用户配置信息: git config --list

3 Git 图解

屏幕快照 2015-07-02 下午4.21.46.png-187.2kB
屏幕快照 2015-07-02 下午4.21.46.png-187.2kB
  • 工作区 (Working Tree 或者 Working Directory)

    在哪个目录下初始化了 git,那这个目录就是工作区。工作区所对应的目录下,除了有自己的文件外,还有初始化 git 时,自动生成的 .git目录。当然工作区是不包含 .git 目录的。
  • 仓库 (Repository)

    .git 目录除了一些配置信息外,就是 仓库 或者 版本库,它包含 暂存区,以及各个 commit 节点 构成的时间线。

    暂存区 (Stage 或者 Index) 是仓库中数据的一个暂时存放地点。由于可能会有多次 git add 操作,因此,需将每次操作后的结果暂时存放到暂存区中。

    另外还有一个存放 commit 节点的地方,它们按提交时间的先后顺序组织在一起。其中对于相邻的两个 commit 节点,提交较晚的 commit 节点指向提交较早的 commit 节点
  • 分支 (Branch)

    分支实质上是一个 指向 commit 节点的指针。对于一个项目可以有多个分支,用以进行协同开发。

    有两种分支,一种是本地分支,另一种是远程分支。

    .git/refs/heads 目录下的所有文件保存了本地仓库中的所有本地分支指针信息。 远程分支见后面。

  • HEAD 指针

    HEAD 指针指向当前正在使用的本地分支指针,也可以指向任何一个 commit 节点。

    每次生成的 commit 节点的插入操作都是依赖于 HEAD 指针进行的。如下图所示:

    屏幕快照 2015-07-02 下午1.32.52.png-40.1kB
    屏幕快照 2015-07-02 下午1.32.52.png-40.1kB

    上图中有两个本地分支,一个 master 分支,一个 dev 分支,由于 HEAD 指针指向的是 dev 分支,所以当前所使用的分支是 dev。

    .git/HEAD 文件中保存了 HEAD 指针的信息。

4 工作区以及暂存区的状态

工作区大致分为两种状态:

  • 所有内容都被处理完成的状态;
  • 还有内容未被处理的状态,比如 Untracked files 以及 Changes not staged

当 add 所有内容到都被 add 到暂存区后,工作区就会转变成所有内容均被处理完成的状态。

暂存区也是分为这两种状态。其中还有内容未被处理的状态一般为 Changes to be committed

当 commit 到仓库后,暂存区就转变成所有内容均被处理完成的状态。

5 Git 初始化(创建工作区以及暂存区和仓库)

git init

执行完后,当前目录就成为一个工作区(不包括 .git 目录),其中的 .git 目录 是一个空的仓库,没有任何的内容与分支指针。

通过 init 操作生成的 .git 目录 在工作区所在目录内。事实上,.git 目录与工作区也可以在其他任何目录下。这种情况下进行 Git 操作时,只需要通过 --git-dir 与 --work-tree 指定即可,形式如下: git --git-dir=xxx --work-tree=xxx command

6 从工作区中添加文件到暂存区

添加当前操作目录下的所有文件,还包括跟踪那些未曾跟踪过的文件: git add .

添加工作区中的某个文件,如果这个文件为被跟踪过,则跟踪此文件: git add filePath

只将工作区中所有已被追踪的文件添加到暂存区中: git add -u

7 查看暂存区中的文件

git ls-files 该命令会查看暂存区中的所有文件。

还有一个常用命令,用于查看处于暂存区中但并不存在于工作区中的所有文件: git ls-files --deleted

8 将暂存区中的所有文件提交到仓库

git commit -m "描述性说明" 其中,-m 参数指定一个字符串,该字符串用以表示对此次提交的说明,方便日后查看。

如果是通过 git init 操作建立仓库,则初次提交时会创建一个分支,名字默认为 master

有时需要需要重新进行提交,此时可以先删除 HEAD 所指向的分支指针所指 commit 节点,然后提交暂存区中的所有文件。执行如下命令: git commit --amend -m "描述性说明"

如果暂存区中没有作任何变动,直接运行此命令的话相当于有机会重新编辑提交说明。

9 查看各个区的状态

git status 通过这个命令,可以查看到工作区中是否有文件还未被 add,暂存区中是否有文件还未被 commit 等等情况。

10 将工作区的文件添加到暂存区并提交到仓库

git commit -a -m "描述性说明" 该命令会对 在暂存区中存在(就是被跟踪)但在工作区中被修改过或被删除了的文件 添加到暂存区,并同时提交到仓库。

注意:该命令对那些工作区中未被跟踪的文件无效。

当然,还可以对同时使用 --amend 参数: git commit -a --amend -m "描述性说明"

11 查看文件的差异

查看工作区和暂存区中文件的差异: git diff

查看某一个文件在工作区和暂存区之间的差异: git diff filePath

查看暂存区与当前操作的分支指针所指向的 commit 节点的差异: git diff --cached HEAD

查看工作区和当前操作的分支(即 HEAD 指向的分支)所指向的 commit 节点之间的差异: git diff HEAD

注意:上面的所有命令只适用于查看那些工作区中已被跟踪的文件与其在其他区域之间的差异。

查看两个 commit 节点间的差异: git diff commitID1 commitID2

12 将暂存区中的指定文件强制替换到工作区中

用暂存区中 filePath 文件强制替换工作区中的 filePath 文件: git checkout -- filePath

该命令常用来取消对工作区中某文件的修改,将其还原至上次执行 git add 操作前的情形。

强制替换 的意思就是不管工作区或者暂存区的状态如何,都会执行替换。

-- 表示后面跟的名字是一个文件名,而非一个分支名。

13 将某个版本中的指定文件强制替换到暂存区中

用最近一次 commit 前(当前版本)的 filePath 对应文件强制替换掉暂存区中的 filePath: git reset HEAD -- filePath

用上一个版本的 filePath 对应的文件强制替换掉暂存区中的 filePath: git reset HEAD^ -- filePath 或者 git reset HEAD~1 -- filePath

用上上个版本的 filePath 对应的文件强制替换掉暂存区中的 filePath: git reset HEAD^^ -- filePath 或者 git reset HEAD~2 -- filePath

其他情况,依此类推。

用指定版本的 filePath 对应的文件强制替换掉暂存区中的 filePath: git reset commitID -- filePath 这里的 commitID 字段也可以写成 commitID 的前几位,git 会自动进行查找与其匹配的 commitID。

该操作并不会移动分支指针,也即当前操作的分支指针以及其中的所有节点的信息不会改变。

14 将某个版本中的指定文件同时强制替换到暂存区和工作区中

用最近一次 commit 前(当前版本)的 filePath 对应的文件同时强制替换掉暂存区和工作区中的 filePath: git checkout HEAD -- filePath

用上一个版本的 filePath 对应的文件同时强制替换掉暂存区和工作区中的 filePath: git checkout HEAD^ -- filePath 或者 git checkout HEAD~1 -- filePath

其他情况,依此类推。

用指定版本的 filePath 对应的文件同时强制替换掉暂存区和工作区中的 filePath: git checkout commitID -- filePath 这里的 commitID 字段也可以写成 commitID 的前几位,git 会自动进行查找与其匹配的 commitID。

该操作并不会移动分支指针,也即当前操作的分支指针以及其中的所有节点的信息不会改变。

15 删除暂存区或工作区的文件

删除暂存区和工作区中的 filePath 文件: git rm filePath

只删除暂存区中的 filePath 文件(相当于是取消跟踪这个文件): git rm --cached filePath

16 查看 Commit 日志

可以通过如下命令来查看 HEAD 指针所指向分支指针所指向的 commit 节点及其之前的所有 commit 节点的提交信息,或者 HEAD 所指向 commit 节点及其之前的所有 commit 节点的提交信息(对于 HEAD 分离状态,见后面): git log

该命令由上到下地打印出上述 commit 节点(由后到前)的提交信息,包括:

  • 每次 commit 的 id 号;
  • 作者;
  • 提交日期;
  • 以及描述性说明(commit 时 -m 参数后面的东西)。

如果查看 log 的简单信息(如 commitId 和描述性说明),并以一行显示,则可以用如下命令: git log --pretty=oneline

查看更简洁的 log(commit 的 id 只显示前面几个字符,以及描述性说明)的命令: git log --pretty=oneline --abbrev-commit 相当于是: git log --oneline

以图形方式查看 log,并对 diff 添加颜色: git log --color --graph

以图形方式查看简略的 log 信息,并对 diff 添加颜色: git log --color --graph --oneline

自定义常用格式,并以图形方式查看简略的 log 信息(对 diff 添加颜色): git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit

17 为 Git 命令加上别名

对于一些较长的命令可以自定义一些较短的别名: git config --global alias.shortname "xxx"

比如,对上面较长的 git log 命令添加别名: git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

使用如下: git lg

要删除别名,直接在对应的配置文件中找到相应行删除即可。

18 版本回退

有两种版本回退方式。一种是 git reset;另一种是 git revert。前者主要用于个人分支上,后者主要用于公共分支上。

18.1 git reset

实质上就是更改分支指针,使其指向前面某个 commit 节点并更改 HEAD 指针,同时根据参数来判断是否用这个 commit 节点中的所有内容 强制性替换 暂存区或工作区中的原有内容。

注意,这里的强制替换针对的是工作区或暂存区中的所有内容。

另外,原来的 commit 节点不再存在于历史记录中,因而 git log 命令的输出中是找不到这个 commit 节点的。 事实上,这种 commit 节点会在之后的某个时刻(过期)被垃圾回收掉。如果并未被回收掉,可以用 git reflog 命令进行查看,见后面。

18.1.1 单纯的版本回退

--soft 参数,表示只是更改分支指针及 HEAD 指针。

比如将 HEAD 指针及其所指分支指针指向前一个 commit 节点: git reset --soft HEAD^ 或者 git reset --soft HEAD~1

其余类似。

将 HEAD 指针及其所指分支指针指向指定版本的 commit 节点: git reset --soft commitID 这里的 commitID 字段也可以写成 commitID 的前几位,git 会自动进行查找与其匹配的 commitID。

18.1.2 版本回退且将所有文件强制替换至暂存区中

--mixed 参数,表示不仅更改分支指针,而且还会用更改后的分支指针所指向的 commit 节点中的所有内容强制替换掉暂存区中的原有内容。

--mixed 是默认参数,因此不指定也可以。

比如将 HEAD 指针及其所指分支指针指向前一个 commit 节点,并用更改后的分支指针所指向的 commit 节点中的所有内容强制替换掉暂存区中的原有内容: git reset HEAD^ 或者 git reset HEAD~1

其余类似。

将 HEAD 指针及其所指分支指针指向指定版本的 commit 节点,并用该 commit 节点中的所有内容强制替换掉暂存区中的原有内容: git reset commitID 这里的 commitID 字段也可以写成 commitID 的前几位,git 会自动进行查找与其匹配的 commitID。

18.1.3 版本回退且同时将所有文件强制替换至暂存区和工作区中

--hard 参数,表示不仅更改分支指针,而且还会用更改后的分支指针所指向的 commit 节点中的所有内容同时强制替换掉暂存区和工作区中的原有内容。

比如将 HEAD 所指分支指针指向前一个 commit 节点,并用更改后的分支指针所指向的 commit 节点中的所有内容同时强制替换掉暂存区和工作区中的原有内容: git reset --hard HEAD^ 或者 git reset --hard HEAD~1

其余类似。

将 HEAD 所指分支指针指向指定版本的 commit 节点,并用该 commit 节点中的所有内容同时强制替换掉暂存区和工作区中的原有内容: git reset --hard commitID 这里的 commitID 字段也可以写成 commitID 的前几位,git 会自动进行查找与其匹配的 commitID。

18.2 git revert

实质就是将前面某个 commit 节点对应的项目文件重新提交一次,生成一个新的 commit 节点。

这样做的好处在于不会丢失 commit 节点信息,给之后可能的回退操作留有余地。其次,在 push 到远程仓库时,也不会像 reset 那样会产生冲突。

使用如下: git revert commitID

19 查看分支指针与 HEAD 指针的日志

查看 HEAD 指针的更改日志: git reflog HEAD

默认情况下就是查看 HEAD 指针的更改日志,因此也可以不用指定 HEAD。

在运行这个命令后,会出来很多关于 HEAD@{...} 的条目。

若想查看 reflog 中 n 次前的所有 HEAD 指针改动信息,则输入以下命令: git reflog HEAD@{n}

例如,HEAD@{1} 就是查看对于上上次及其之前的 HEAD 指针改动信息。

类似于 git log 打印格式打印 reflog 日志信息: git log -g

可以通过查看 reflog 中的 HEAD 指针或分支指针改动信息,进而来恢复某些 commit 节点意外的删除操作(比如 git commit --amend 操作,或者 git reset 操作)。比如: git reset --hard HEAD@{2}

需要注意的是,当回退到 HEAD@{2} 后,被替换的 commit 节点及其之后所有的 commit 节点都会被移除(用 git log 命令不会打印出来)。

20 HEAD 分离与查看某个版本中的所有文件

HEAD 正常情况下是指向一个本地分支指针的,不过 HEAD 也可以分离出来,使其指向一个 commit 节点。具体操作如下: git checkout commitID

执行此操作后,除了将 HEAD 分离出来指向某个具体的 commit 节点,还会用这个 commit 节点的所有文件替换掉暂存区和工作区中的原有内容。从而达到了查看这个 commit 节点对应版本的所有文件。

注意,这里并不是强制替换。因此当工作区或者暂存区为还有内容未被处理的状态,则会终止该操作的执行。

由于根据 HEAD 找不到分支指针,而提交生成的新的 commit 节点的插入必须要依赖于分支指针。因而当分离 HEAD 后,所有的提交操作都是无效的

21 分支的建立, 删除与切换

前面已经讲过,分支在实质上是一个指向 commit 节点的指针。建立分支也就是建立一个指针,使其指向某个分支指针所指的 commit 节点。如图所示:

屏幕快照 2015-07-03 上午10.55.34.png-35.3kB
屏幕快照 2015-07-03 上午10.55.34.png-35.3kB

21.1 建立分支

命令如下(假设建立的分支名为 dev): git branch dev

该命令中并没有在新建的分支名后指定另外的分支名。这种情况下,默认建立的分支所指向的 commit 节点与 HEAD 所指向的分支指针所指向的 commit 节点相同

如果指明了分支指针: git branch dev anotherBranch 则表示建立的 dev 分支指针所指向的 commit 节点与 anotherBranch 分支针所指向的 commit 节点相同。

21.2 切换分支

切换至 dev 分支的命令为: git checkout dev

切换分支的实质,是将 HEAD 指针指向切换后的分支指针,同时用切换后的分支指针所指向的 commit 节点中的所有内容替换掉暂存区与工作区中的原有内容。

注意,这里并不是强制替换。因此当工作区或者暂存区为还有内容未被处理的状态(若未被处理的是新建的文件则除外),则会终止该操作的执行。

即使对于未被处理的是新建的文件,该命令仍然可以执行,但是强烈建议不要这么做。 当工作区或暂存区还有内容为被处理时,建议先进行 commit,或者执行 stash(见后)。

若想在建立分支的同时,切换至这个分支进行操作: git checkout -b dev anotherBranch

21.3 查看分支

若想查看本地所有的分支名: git branch

其中,* 号所标识的分支为当前操作的分支。

想查看远端仓库的分支名: git branch -r

想查看本地以及远端所有的分支名: git branch -a

21.4 重命名分支

git branch -m 原分支名 更改后的分支名

21.5 删除分支

git branch -d oneBranch

22 分支合并 - merge

假设是将 dev 分支合并到 master 分支。

22.1 fast-forward 方式

先切换至 master 分支,然后执行: git merge dev

git merge 的默认参数为 --ff,也就是说默认采用 fast-forward 方式。

这种方式的实质,在于将 master 分支指针移动到 dev 分支指针所指向的 commit 节点上。

操作前后如图所示:

22.2 非 fast-forward 方式

fast-forward 方式的有一个很大的问题。由于是直接将 master 分支指针移动到 dev 分支指针所指向的 commit 节点。表现为曾经没有建立分支,以及后来的在该分支上所作的操作和归并操作。这在开发过程中有时候会带来诸多的困扰。所以,尽量不要使用 fast-forward 方式进行 merge。

先切换至 master 分支,然后执行: git merge --no-ff dev -m "描述性信息"

--no-ff 参数表示禁止使用 fast-forward 方式进行 merge。

如果要说这两种方式的区别,就在于非 fast-forward 方式的 merge 要进行一次 commit。

操作前后如图所示:

屏幕快照 2015-07-03 上午11.27.52.png-61.2kB
屏幕快照 2015-07-03 上午11.27.52.png-61.2kB

这样,就保存了清晰的分支信息,易于开发过程中对代码的查看。

22.3 squash 方式

该方式会在工作区中合并 dev 分支当前所指向的 commit 节点向前一直到 dev 建立时中间所有的 commit 节点。

如果在 dev 分支自从建立以来进行了很多次提交,且这些 commit 节点没有太大的意义,那么在合并到 master 分支上时就可以用这种 merge 方式。

先切换至 master 分支,然后执行: git merge --squash dev

执行过程如下:

  • 首先从 dev 分支指针所指向的 commit 节点与 master 分支指针所指向的 commit 节点向前查找,直至找到第一个公共 commit 节点。
  • 然后将 dev 分支指针所指向的 commit 节点及其后面的 commit 节点合并到工作区中,直至遇到公共 commit 节点为止(公共 commit 节点不合并)。

注意,这种合并方式只是在工作区中对 dev 分支所指向的 commit 节点到第一个公共 commit 节点间的所有 commit 节点进行合并,并不会产生新的 commit 节点。而且合并完成后,dev 分支上所有被处理的 commit 节点仍然存在。

接下来,就可以将暂存区中的内容提交到仓库中了: git commit -m "描述性信息" 表示将新建的 commit 节点插入到 master 分支所指向 commit 节点的后面,并更新 master 分支为刚刚插入的 commit 节点。

22.4 解决 merge 操作时产生的冲突

上面所说的 merge 操作实质上 都是先在工作区和暂存区中进行的,并非直接移动分支指针或者添加一个 commit 节点,因此很有可能会有冲突发生。

如果要放弃本次 merge,则可执行: git merge --abort

否则,需要手动解决冲突。

在执行 merge 命令时,若有 merge 冲突的文件,会提示出来。打开每个冲突的文件手动解决冲突。例如:

1
2
3
4
5
<<<<<<< HEAD
test in master
=======
test in dev
>>>>>>> dev
  • <<<<<<< 标记冲突开始,后面跟的是当前分支中的内容。
  • ======= 之前,<<<<<<< 之后的是本分支的内容。
  • ======= 之后,>>>>>>> 之前是要 merge 过来的另一条分支上的内容。
  • >>>>>>> 之后的 dev 是该分支的名字。

也可以通过 merge tool。但前提需要进行配置: git config --global merge.tool xxx 然后在解决冲突时执行: git mergetool

手动编辑解决冲突完之后(还包括去掉这些标记),先执行 add 操作,标记冲突已解决。最后执行: git merge --continue

23 分支衍合 - rebase

事实上就是改变某个分支的 base commit 节点为另一个分支上的节点,同时根据该分支上的每个 commit 节点重新生成新的节点。

假设是将 dev 分支衍合到 master 分支。

23.1 rebase 执行过程

首先切换至 dev 分支,然后执行: git rebase master

该命令的执行过程如下:

  • 首先从 dev 分支指针所指向的 commit 节点与 master 分支指针所指向的 commit 节点向前查找,直至找到第一个公共 commit 节点。
  • 然后将 dev 分支指针所指向的 commit 节点及其后面的 commit 节点按顺序进行处理,直至遇到公共 commit 节点为止(公共 commit 节点不处理)。

    处理过程是,新建一个 commit 节点,其内容与所要处理的 commit 节点的内容一样(但由于是新建节点,因而两个节点的 commitID 并不相同)。然后将其插入到 master 分支所指向 commit 节点的后面。

    注意,并不会更新 master 分支指针。

  • 插入完成后,dev 分支上所有被处理的 commit 节点将被移除。同时将 dev 分支指向最后一个被插入的节点。

23.2 解决 rebase 操作时产生的冲突

rebase 操作是 先在工作区和暂存区中进行的,并非直接添加一个 commit 节点,因此很有可能会有冲突发生。

在执行 reabse 过程中,如果产生了冲突,则会暂停执行该操作。这个时候就需要手动解决冲突(同 merge 中一样)。

解决完了之后,先执行 add 操作,标记冲突已解决。然后执行: git rebase --continue 表示继续执行 rebase 操作。至此,rebase 操作执行完成。

23.3 rebase 示例

操作前后如图所示:

Screen Shot 2017-03-05 at 5.22.41 PM.png-147.5kB
Screen Shot 2017-03-05 at 5.22.41 PM.png-147.5kB

23.4 rebase 操作后进行 merge

rebase 操作执行完成后,HEAD 指针指向的是 dev 分支指针,而非 master 指针,因此还需进行简单的 merge。

先切换到 master 分支: git checkout master 然后通过 fast-forward 方式进行分支合并: git merge master dev 将 master 分支指针移动到 dev 分支指针处。

23.5 更复杂的情况

对于如下的 commit 节点图:

Screen Shot 2017-03-05 at 9.27.31 PM.png-18.7kB
Screen Shot 2017-03-05 at 9.27.31 PM.png-18.7kB

若要将其通过 rebase 操作变换为下面的图:

Screen Shot 2017-03-05 at 9.27.43 PM.png-18.6kB
Screen Shot 2017-03-05 at 9.27.43 PM.png-18.6kB

则可以使用 --onto 参数: git rebase --onto master dev feature

若当前正在操作的分支为 feature,则可以忽略 featuer 参数: git rebase --onto master dev

操作完成后,原 feature 分支指针所指 commit 节点向前直至第一个公共 commit 节点之间的所有 commit 节点都将被移除。

其余操作同上。

23.6 rebase -i

可以根据该命令来交互式地来批量完成 rebase 操作。实际情况中,常用该命令来合并某个分支上多个节点为一个。

假设某个分支的 log 如下:

1
2
3
4
* 0fc6a2c - (HEAD -> dev) dev-4th
* b42ab32 - dev-3rd
* 2035053 - dev-2nd
* abf6d3a - dev-1st

假设需要将 0fc6a2c, b42ab32 合并到 2035053 中。

首先以 abf6d3a 为固定节点,执行如下命令: git rebase -i abf6d3a

然后进入交互式页面,内容大概如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pick 2035053 dev-2nd
pick b42ab32 dev-3rd
pick 0fc6a2c dev-4th

# Rebase abf6d3a..0fc6a2c onto abf6d3a (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit

前面三行是内置命令。其中, squash commit节点 commit信息 的意思是将该 commit 节点合并到其父节点中。为了达到上述目的,我们修改如下:

1
2
3
pick 2035053 dev-2nd
squash b42ab32 dev-3rd
squash 0fc6a2c dev-4th

保存退出后,又会进入另一个交互界面,需要我们输入合并后的节点的 commit 信息。然后操作结束。

详见:如何优雅地合并多个 Commit

23.7 rebase 使用注意事项

在实际开发中,绝对不要在公共分支上(比如 master 等)使用 rebase 操作。

如果在公共分支上使有 rebase 操作,那么该分支指针所指向的 commit 节点到第一个公共 commit 节点间的所有 commit 节点都将会被移除,而这些操作对于其他开发者是看不见的(也就是说其他开发者仍然认为公共分支上的这些 commit 节点仍然是存在的)。

24 重新提交 commit 节点到对应的项目文件到另一个分支上

假设想将 master 上的某个对应的 commit 节点重新提交到 dev 分支上(不同的 hash 值)。需要先切换至 dev 分支,然后执行: git cherry-pick commitID

如果是将 master 某个区间的 commit 节点对应的项目文件重新提交到 dev 上,则执行: git cherry-pick startCommitID..endCommitID 该命令会将 (startCommitID, endCommitID] 中的所有节点按次序提交到 dev 分支上。

git cherry-pick startCommitID^..endCommitID 则是针对 [startCommitID, endCommitID] 这个闭区间中的 commit 节点。

cherry-pick 操作中可能会出现冲突,此时同 merge 操作类似,需要先解决冲突,然后 add 到暂存区,标记冲突已解决。最后继续执行该操作: git cherry-pick --continue

如果想要终止操作,回退到操作前的状态,则执行: git cherry-pick --abort

25 多分支策略 - Git Flow

  • Master 主干分支 代码库应该有一个、且仅有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布。
  • Develop 分支 主分支只用来分布重大版本,日常开发应该在另一条分支上完成。我们把开发用的分支,叫做 Develop。
  • Feature 特性开发分支 它是为了开发某种特定功能,从 Develop 分支上面分出来的。开发完成后,要再并入 Develop。
  • Release 预发布分支 它是指发布正式版本之前(即合并到 Master 分支之前),我们可能需要有一个预发布的版本进行测试。
  • Bug 修复分支 软件正式发布以后,难免会出现 bug。这时就需要创建一个分支,进行 bug 修补。

Reference