git rebase/submodule/flow介绍
整理并学习几个git中的工具:
- rebase - 用于合并提交,重写历史,较
merge
更复杂 - submodule - 子模块功能,用于管理项目中包含的独立子模块
- git-flow - 一个工具,提供flow命令来简化符合
git branching model
中规范的操作
git rebase
rebase
(变基)命令用于合并提交,重写历史。
在git有两个命令可以合并不同分支的变动: merge
和rebase
,merge
是比较常用的合并命令,而且在pull
命令中也包含merge
。rebase
相对于merge
的不同之处:
- 破坏性的操作,合并操作后会破坏提交历史,重写操作后无法追溯历史
- 具有交互式编辑方式,可以对多个提交进行不同操作,使用[-i|–interactive]选项开启
使用铁律: 永远不要在公共的分支上执行rebase,因为这会影响其他使用它的人。
合并同一分支的多次提交
将一连串的提交合并为一次提交,简化提交历史。
合并前:
C1 --- C2 --- C3 --- C4
合并后:
C1 --- C2'
两种指定要合并提交的方式:
-
指定起始和终止提交
git rebase -i [START_COMMIT_HASH] [END_COMMIT_HASH]
START_COMMIT_HASH
为起始提交hash,END_COMMIT_HASH
为终止提交hash。。 -
根据HEAD指针向前推断
git rebase -i HEAD~[SUM]
,SUM
为最后提交的个数。
若执行git rebase -i HEAD~3
命令,git会在文本编辑器上提供一个列表:
pick 6d8b3f7 fix lerna
pick 4e556da add nuxt
pick 41cf252 add git files
在这个列表中需要关注的两个地方:
-
该列表中最旧的提交在最上面,最新的在最下面,与使用
git log
查看的顺序是相反的,该顺序是交互式rebase
的操作顺序。 -
hash前的单词表示对该提交的操作,该操作有如下几种:
# 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
,如下:
pick 6d8b3f7 fix lerna
squash 4e556da add nuxt
squash 41cf252 add git files
保存后即得到一个包含前三次提交所有变更及提交信息的提交。
合并不同分支的多次提交
比如,我们想将A分支的提交合并到B分支的提交上
合并前:
C1 ---- C3 --- C5 Branch-A
\
--- C2 --- C4 Branch-B
合并后:
// C3'与C3有可能内容完全一样,但实际上是不一样的提交,有着不同的hash。
C1 --- C2 --- C4(Branch-B) --- C3' --- C5'(Branch-A)
在这个过程中有三步:
- 来到A与B分支的共同提交处(C1)
- 整合B分支的提交(C2, C4)
- 整合A分支的提交(C3’, C5’),这些提交被重新应用到了B分支的提交后,位置发生了改变,被
re-base
了
要实现上述过程只需要执行:
git checkout Branch-A
git rebase Branch-B
若第三步整合的提交与第二步中的存在冲突,则需要先解决冲突,git add
提交修改后,再执行如下命令处理剩余的合并操作:
git rebase --continue
注意事项
- 不要在已提交的公共分支上使用
rebase
操作 - 在需要的场合再使用
rebase
git submodule
用于管理项目中的submodule(子模块)。
场景
适合用submodule
来管理博客
项目(从hugo中了解)。在博客中,一般有博客项目和生成的静态站点。可以将博客项目作为主项目,所生成的静态站点作为submodule,这样在修改完文章生成新的静态站点后,可以提交主项目中submodule的改动,而不用切换到另一个项目中。
一个 “子模块” 其实就是一个标准的 Git 仓库。不同的是,它被包含在另一个主项目的仓库中。一般情况下,它包含一些库文件和其它资源文件,你可以简单地把这些库文件作为一个子模块添加到你的主项目中。
添加submodule
git submodule add http://<@repo>/<module>.git <explicitDirectory>
默认会将module clone到以module命名的根目录文件夹下,也可以指定clone的路径。虽然将该项目clone了下来,但是它并不作为主项目版本控制的一部分。
当添加一个submodule时,会生成一个.gitmodules
文件,该文件会跟踪并保存所使用的submodule信息。除了 “.gitmodules” 配置文件,Git 也会在你本地的 “.git/config” 文件中保存对子模块的记录。最终它也会在它的 “.git/modules” 目录中保存每一个子模块的 “.git” 仓库。
一般情况下不要手动修改这个文件,可能会产生意想不到的结果。
更新submodule及初始化clone项目中的submodule
当submodule中的代码变更时,需要更新它的签出版本才能正常提交主项目的代码。
git submodule update <someModule>
当该子模块项目中还包含子模块时,需要使用--recrusive
来安装它内部的sumodule
git submodule update --recrusive
默认情况下,一个项目并不包含它submodule的文件。若想在clone项目时同时保留submodule的文件,需要在使用命令时加上参数。
-
--recurse-submodules
在
clone
命令上使用--recurse-submodules
参数,会在clone完成时初始化所有submodule。 -
git submodule update --init --recursive
如果项目已经clone了下来,则需要使用
submodule
命令来初始化所有submodule。
删除submodule
安全起见不要手动直接删除,使用如下两步删除submodule
- 删除配置文件中submodule
git submodule deinit <module>
- 删除submodule的对应文件
git rm <module> or <explicitDirectory>
修改submodule
假设submodule在根目录的lib
文件夹下:
cd lib/some-module
git log
当进入子模块的目录后,git命令会仅对子模块有效,而不会影响父仓库。
当修改完submodule文件未提交时,git会在父仓库中提示:
git status
...
modified: lib/<module> (modified content)
改变submodule的签出版本
对于一个git仓库,总应有一个版本会签出到工作副本中,对于submodule也不例外。普通的git仓库可能是签出一个分支,而submodule可以签出一个特定的提交。
当在submodule中切换一个tag: v1.0.1
对应的提交时:
cd lib/<module>
git checkout v1.0.1
切换回主仓库查看submodule状态
cd ..
$ git submodule status
+3557a0e0f7280fb3aba18fb9035d204c7de6344f lib/<module> (v1.0.1)
在hash前的+
号表示签出的版本与父仓库中记录的submodule版本不一致。
使用git status
会发现git移动了指向该submodule的指针:
$ git status
On branch master
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: lib/<module> (new commits)
因此需要提交这个改动:
git commit -am "move module pointer to v1.0.1"
git-flow
使用git-flow
工具可以通过一些简单命令来进行符合git branching model
中规范的相关操作。通常使用AVH版本的git-flow。
init
在初始化git项目时会有一些交互选项来设置一些预设分支与命名规则。
$ git flow init
Initialized empty Git repository in /Users/abc/desktop/git-test/.git/
Branch name for production releases: [master]
Branch name for "next release" development: [develop]
How to name your supporting branch prefixes?
Feature branches? [feature/]
Release branches? [release/]
Hotfix branches? [hotfix/]
branch
git-flow
中存在两种预设的分支:
- master
- develop
feature
使用feature
命令来创建和完成新功能。假设我们要开发一个计数器功能:
开始feature开发
git flow feature start counter
它所做的就是签出一个新的分支,使用feature的命名规则(feature/counter)
完成feature开发
git flow feature finish counter
所做的事包含:
- 将当前工作整合到
develop
中 - 删除
feature/counter
分支,切换到develop
分支
release
当认为develop分支已经是个成熟release版本时,使用release
命令来发布新的版本。
创建release
git flow release start 1.1.0
# Switched to a new branch 'release/1.1.0'
会创建一个新的release分支,用于后续可能的文件版本标记与最后的编辑。
完成release
git flow release finish 1.1.0
在这里会执行以下操作:
- 拉取remote仓库,保持最新版本
- 将
release
内容合并到master
和develop
分支上 - 使用release名称(1.1.0)标记本次
release
提交 - 删除
release/1.1.0
分支,回到develop
hotfix
hotfix流程用于修复在release版本的后续测试中所发现的小错误。
创建hotfix
git flow hotfix start missing-link
会基于master
分支创建一个名为hotfix/missing-link
的分支。
完成hotfix
git flow release finish 1.1.5
该命令所做的事情:
- 将
hotfix
内容合并到master
和develop
分支上 - 对本地
hotfix
提交添加标记 - 删除
hotfix
分支,回到develop
参考
- 原文作者:yrq110
- 原文链接:http://yrq110.me/post/tool/git-rebase-submodule-and-flow/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。