git:分支
站内链接:
Introduction
通用格式
1 | # 通用格式 |
一般情况下, 本地分支和远程分支都是保持同样的名字, 所以一般的简写格式是第二种方式, 更进一步, 如果你希望本地任意分支自动关联远程分支, 可以使用--set-upstream
配置, 其命令如下:
1 | # For every branch that is up to date or successfully pushed, add upstream (tracking) reference, used by argument-less git-pull(1) and other commands. |
在上面的配置完成之后, 后续就可以简单的使用git [action]
进行同样的操作了.
Rule
- 确保多分支开发过程: 各个分支的功能尽可能独立
- 确保各项分支在一定阶段同 master 的同步性
- 确保各个分支在合并前能够独立运行
创建分支
格式: git branch [new_branch_name]
本地创建命令:
1 | # 1. 方法1 |
create a local branch with remote:
1 | # 1. 本地创建并推送 |
说明: 在 fetch 所有新分支之后, 还需要在本地创建一个分支(默认映射 remote branch)
Push
格式: git push <remote_name> <remote_branch_name>
命令: git push origin test
或者 git push origin test:test
说明: 将本地分支推送到origin/test
分支下, 其中<remote_branch_name>
的完全路径应该为: refs/heads/branch1: refs/heads/branch1
, 即意为取出本地的 branch1, 推送到远程仓库的 branch1 中.
Pull
new branch
命令: git fetch origin
该命令会同步所有新的 branch 信息, 但是仅仅得到一个无法移动的origin/branch1
指针, 此时需要在该指针的基础上再分化一个新的 local branch. 其执行具体过程如下:
- 查找 origin 服务器, 关于 origin 说明见
git仓库文章
介绍
- 查找 origin 服务器, 关于 origin 说明见
- 拉取服务器上所有信息并更新本地数据库, 同时进行了重建
基底
的操作, 将origin/master
指向最新的HEAD
- 拉取服务器上所有信息并更新本地数据库, 同时进行了重建
exist branch
命令: git pull origin branch1
说明: 抓取数据并合并到 local branch.
Delete
- 删除远程仓库中的某一个分支
格式: git push origin :<remote_branch_name>
1 | # a. 在账户拥有权限的前提下删除某个远程分支, 通过push删除, 注意, 此时本地分支并不会删除 |
- 删除本地分支
1 | # 1. 注意和远程分支删除命令的区别, 前者使用push进行覆盖, 这里实际对branch进行操作 |
若本地分支存在未 merge 到其 upstream 的 commit, -d
会错误error: The branch 'TE' is not fully merged
若本地分支存在未同步到服务器远程分支的 commit, -d
会错误warning: deleting branch 'TE' that has been merged to
, 但最终本地分支还是会被删除.
- 清理远程已经不存在的本地分支的指针信息
命令: git fetch -p
说明: 在远程分支被清理之后, 本地仓库中还存在还分支的指针信息(空), 可以使用该命令清除. 当时, 如果该分支已经被 fetch 到本地, 则需要 5.2 节的命令进行删除操作.
Merge
命令
- 分支合并时, 时刻确保当前分支是最新的
1 | # a. 查看两个分支的差别 |
cherry picking
Cherry-picking
用于将某一个 branch 的某一个 commit 合并到另外一个分支中, 在一些特殊场景如 hotfix 时可能会使用到.
1 | # 1. 在待合并branch中执行 |
Conflict
- 错误
一般情况下, 两个分支会自动合并, 但是两个分支同时对相同的文件中的同一部分进行了改动, 则无法 automerge, 需要开发者手动进行分支的合并, 此时报错信息如下:
1 | Auto-merging te.py |
git log -p
输出内容说明
1 | # 输出内容 |
- conflict 文件输出内容
冲突一般发生在:多人同时更改了某一个文件的同一行代码, A 改动了被 B 删除的代码.
1 | # 命令git checkout TE2 && git merge T1的输出内容 |
如果想要看当前版本控制中历史所有合并信息, 可以输入命令: git log --graph
进行查看. 那么, 怎样尽可能的避免冲突呢?
- 尽可能将一个需求模块化, 确保一个需求能够在真正上线前能够多次 commit 到公共开发分支而不影响其他逻辑, 就算是一个空壳模块或者框架也行, 尽可能保证每天下班前 commit 一次, 在功能不影响其他模块的前提下 merge 到公共开发分支
- 团队内部人员的沟通, 对一些重要代码的更新及时线下通知并同步到所有人, 每天上班前 merge 一次主分支代码
- 至少保持两个自己的分支:
bugfix/name
,develop/name
, 前者永远仅与对外发布分支(以 develop 为例)进行同步更新操作, 后者作为自己的功能开发分支 - 每次开发功能前同步 develop 和当前版本开发分支, 每次 commit 之前 merge 当前版本开发分支, 用于保持本地分支代码最新.
rebase
对当前分支(待变基分支)和目标分支(基分支)进行重新构建操作, git 会从两个分支的共同祖先开始提取当前分支上的修改, 之后将当前分支指向目标分支的 Head, 然后将当前分支的最新改动追加到目标分支后面. 下面我们简单的举例说明下 rebase 的用途. 首先是无冲突情况下, 两个交叉分支的合并是不需要使用到 rebase 的, 比如如下的分支结构:
1 | bifeng: |
此时执行命令: git log --all --pretty=oneline --abbrev-commit --graph
的输出如下:
1 | * 0c7fe76 (HEAD -> master) type: feat four |
在执行合并命令: git merge master
之后的输出如下:
1 | * d13d953 (HEAD -> bifeng) Merge branch 'master' into bifeng |
但是如果执行git merge master
之后, 上面命令的输出如下:
1 | * 65cdbc9 (HEAD -> bifeng) type: feat fourbamboo |
发现两者输出的不同之后了吗, 在执行 rebase 之后进行进行了路线合并操作, 更有意思的地方出现了, 此时切换到 master 分支发现bifeng
分支代码并未合并到 master(这是符合正常逻辑的), 但是此时执行上面git log
命令的输出如下:
1 | * 65cdbc9 (bifeng) type: feat fourbamboo |
说明在执行rebase
之后会同步更新目标分支的提交节点图信息
, 这个过程也会称为变基
, 分支bifeng
是从 master 分支 checkout 出来的, 其基底
是 B(见上面的 graph 分叉输出, 而在这之后 master 和bifeng
都有了新的提交, 此时执行rebase
就是一个基底
重建的过程. 另外, git rebase
和git merge
除了上面的区别之外, 后者会比前者多处一个 commit 提交.
aborting
在一些特殊场景进行 push, pull, merge 时会产生Fatal: Not possible to fast-forward, aborting
, 其可能原因有:
- 多个分支同时改了某个相同的地方, 然后其中某个分支又同时有多人在共同使用导致该分支在 userA 处是
a->b->c
, 在 user2 处是a->b->d -> e -> f
, 此时执行git pull
必然会发生该错误
- 多个分支同时改了某个相同的地方, 然后其中某个分支又同时有多人在共同使用导致该分支在 userA 处是
- 多个分支同时改了某个相同的地方, 此时按照正常情况执行
git merge
可以进行合并操作, 但一些特殊情况下无法进行合并操作
- 多个分支同时改了某个相同的地方, 此时按照正常情况执行
对于此类错误, 需要使用上节聊过的 rebase 命令进行基底
的重建操作, 比如:
git pull origin bamboo --rebase
, 重建 bamboo 分支并同时拉取代码git rebase master
, 合并分支并重建
Rename
- 本地分支重命名:
命令: git branch -m old_branch_name new_branch_name
说明: 该方式仅仅重命名本地的一个分支, 并且提交到服务器时时以创建一个新的分支为目的, 其中-m 表示 move
- 本地分支重命名并且同步到远程
1 | # a. 本地操作 |
注意, 如果重命名之后分支名称没有同步到远程分支, 在已经设置 upstream 的前提下, .git/config
会发生变化, 值变为:
1 | # 将TE3重命名为TE4, 此时本地分支TE4对应远程分支TE3 |
Upstream
set
upstream 用于将本地分支同远程分支做默认关联, 从而在后续 push/pull 中,可以快速的进行, 这条命令非常好用.
首先, 设置本地分支同远程分支关联
1 | # a. 说明: 将当前分支-local_branch同远程分支进行绑定操作 |
每次 upstream 都会在.git/config
中增加新的配置, 确保后续能够自动识别, 例如命令git push --set-upstream origin TE2
1 | [branch "TE2"] |
其次, 如果本地分支名同远程分支名不同的时候, 需要单独指定本地分支名, 不能使用上面的简写方式
1 | # 格式: git branch -u <remote_name>/<remote_branch_name> <local_branch_name> |
最后, 如果希望看到当前本地分支绑定的远程分支, 可以查看.git/config
或者输入命令git branch -vv
进行查看
set + push
命令: git push -u <remote> <remote_branch_name>:<local_branch_name>
说明: 在推送的同时绑定
unset
命令: –unset-upstream, 其他同上
Difference
命令介绍
git diff
命令用于显示提交和工作树等之间的更改, 其会在工作树和缓存索引树, 缓存索引树和版本树之间进行磁盘内容的比较
- 命令格式:
1 | git diff [options] [<commit>] [--] [<path>...] |
- 工作树, 索引树,版本树之间比较
1 | # 前提操作 |
- 缓存区
1 | git diff --cached index/staged <<<--->>> 本地HEAD数据库 |
- 版本区
注意, 版本区可以比较不同 commit 提交的区别, 这对于排查历史提交比较好用, 当然也可以使用git log
来进行查看.
1 | git diff master story 查看HEAD 中某两个版本的区别 |
命令关系图
diff 命令在工作区, 缓存区, 版本区的操作流程示意图如下:
更新细节的流程变化如下, 此时 diff 命令可以指定某个 commit 提交来判断文件变化.
参考: