之前在写一些demo例子的时候, 每个不同的案例都是在同一个工程的不同目录下. 但是由于有些案例其实是不需要相关依赖的, 但是由于添加到了同一个工程中, 会导致依赖不清晰的问题. 解决方法就是, 以master为基本创建不同的分支, 每个分支为不同的案例, 各个分支不需要合并即可!
阅读本文你将学会:
- 什么是Git中的分支? 分支的使用场景?
- 如何创建、查看、切换、删除本地分支?
- 如何合并分支?
- Git版本回退操作
- Git克隆远程仓库的指定分支
- 如何将本地分支推送到远程?
- ……
Git分支相关总结
之前在写一些项目案例的时候, 都是创建一个大工程, 然后创建多个目录分别存放, 各个案例. 但是对于各个案例来说, 由于在同一个工程, 所以依赖也都放在了一起, 时间长就造成了依赖混乱等问题.
之后, 采用Git分支解决了这个问题:
- 首先创建了一个仓库, 处于master分支;
- 然后在master的基础上分别创建不同的分支, feature, feature2, feature3…
- 在不同的分支上创建不同的工程即可!
所以本文主要总结了与Git分支相关的一些知识.
一. Git中分支的使用场景
分支在实际中有什么作用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了,如果等代码全部写完再一次性提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了,你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
分支的主要作用有:
- 版本迭代更加清晰
- 开发效率提升
- 利于代码review的实现,从而使整个团队开发更加规范,减少bug率
二. 分支的常规操作
1. 查看分支
git branch -a
* save
remotes/origin/HEAD -> origin/save
remotes/origin/master
remotes/origin/save
2. 创建、切换分支
创建并切换分支:git checkout -b <分支名称>
这条命令和下面两条命令效果相同:
创建分支:git branch <分支名称>
切换分支:git checkout <分支名称>
zk@jasonkay:~/test$ git branch test
zk@jasonkay:~/test$ git checkout test
切换到分支 'test'
zk@jasonkay:~/test$ git branch -a
master
* test
3. 合并分支
git checkout master
先切换到master分支
git merge test
再将B分支的代码合并到master(在merge合并分支的时候,代码会有冲突,需要自己去解决这些冲突)
语法: (切换到主分支) -> git merge <想要合并的分支名>
3.1 合并冲突分支
有时候合并操作并不会如此顺利, 如果在不同的分支中都修改了同一个文件的同一部分,Git 就无法干净地把两者合到一起(译注:逻辑上说,这种问题只能由人来裁决)
如:
$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
Git 作了合并,但没有提交,它会停下来等你解决冲突。要看看哪些文件在合并时发生冲突,可以用 git status
查阅:
$ 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")
任何包含未解决冲突的文件都会以未合并(unmerged)的状态列出, Git 会在有冲突的文件里加入标准的冲突解决标记,可以通过它们来手工定位并解决这些冲突!
可以看到此文件包含类似下面这样的部分:
<<<<<<< HEAD
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53
可以看到 =======
隔开的上半部分,是 HEAD
(即 master
分支,在运行 merge
命令时所切换到的分支)中的内容,下半部分是在 iss53
分支中的内容.
解决冲突的办法无非是二者选其一或者由你亲自整合到一起
比如你可以通过把这段内容替换为下面这样来解决:
<div id="footer">
please contact us at email.support@github.com
</div>
这个解决方案各采纳了两个分支中的一部分内容,而且我还删除了 <<<<<<<
,=======
和 >>>>>>>
这些行.
在解决了所有文件里的所有冲突后,运行 git add
将把它们标记为已解决状态(译注:实际上就是来一次快照保存到暂存区域)
因为一旦暂存,就表示冲突已经解决!
如果你想用一个有图形界面的工具来解决这些问题,不妨运行 git mergetool
,它会调用一个可视化的合并工具并引导你解决所有冲突:
$ git mergetool
This message is displayed because 'merge.tool' is not configured.
See 'git mergetool --tool-help' or 'git help config' for more details.
'git mergetool' will now attempt to use one of the following tools:
opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge
Merging:
index.html
Normal merge conflict for 'index.html':
{local}: modified file
{remote}: modified file
Hit return to start merge resolution tool (opendiff):
如果不想用默认的合并工具(Git 为我默认选择了 opendiff
,因为我在 Mac 上运行了该命令),你可以在上方”merge tool candidates”里找到可用的合并工具列表,输入你想用的工具名
退出合并工具以后,Git 会询问你合并是否成功。如果回答是,它会为你把相关文件暂存起来,以表明状态为已解决。
再运行一次 git status
来确认所有冲突都已解决:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: index.html
如果觉得满意了,并且确认所有冲突都已解决,也就是进入了暂存区,就可以用 git commit
来完成这次合并提交
提交的记录差不多是这样:
Merge branch 'iss53'
Conflicts:
index.html
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
# .git/MERGE_HEAD
# and try again.
#
4. 删除分支
当分支已经合并到主分支,并且不再需要接着该分支继续开发(后期也可以从主分支分出来),可以删除该分支
删除不是当前分支: git branch -d <分支名称>
强行删除当前打开分支: git branch -D <分支名称>
zk@jasonkay:~/test$ git branch -d test
已删除分支 test(曾为 b8eb76a)
5. 误删分支恢复
Git会自行负责分支的管理,所以当我们删除一个分支时,Git只是删除了指向相关提交的指针,但该提交对象依然会留在版本库中
如果我们知道删除分支时的散列值,就可以将某个删除的分支恢复过来.
在已知提交的散列值的情况下恢复某个分支: git branch <branch_name> <hash_val>
zk@jasonkay:~/test$ git branch test b8eb
zk@jasonkay:~/test$ git branch -a
* master
test
注: 命令创建提交号历史版本的一个分支,分支名称随意
不知道想要恢复的分支的散列值,可以用reflog命令或者log命令将它找出来
如:
zk@jasonkay:~/test$ git reflog
b8eb76a (HEAD -> master, test) HEAD@{0}: merge test: Fast-forward
8fc21ae HEAD@{1}: checkout: moving from test to master
b8eb76a (HEAD -> master, test) HEAD@{2}: commit: commit: test
8fc21ae HEAD@{3}: checkout: moving from master to test
8fc21ae HEAD@{4}: checkout: moving from test to master
8fc21ae HEAD@{5}: checkout: moving from master to test
8fc21ae HEAD@{6}: commit (initial): commit: test_master.txt
reflog命令:显示整个本地仓储的commit,包括所有branch的commit,甚至包括已经撤销的commit. 只要HEAD发生了变化, 就会在reflog里面看得到!
或者使用log命令:
zk@jasonkay:~/test$ git log
commit b8eb76a547ea6e43a5d25bdb3bb603debd861e18 (HEAD -> master)
Author: zk <jasonkayzk@gmail.com>
Date: Tue Oct 22 19:35:03 2019 +0800
commit: test
commit 8fc21ae52d87b63a53b43f4ebcf676550fb5d2ae
Author: zk <jasonkayzk@gmail.com>
Date: Tue Oct 22 17:31:37 2019 +0800
commit: test_master.txt
log命令: 查看历史提交日志
6. 查看分支图
git log --graph
为了使分支图更加简明,可以加上一些参数: git log --graph --pretty=oneline --abbrev-commit
例如:
zk@jasonkay:~/test$ git log --graph
* commit b8eb76a547ea6e43a5d25bdb3bb603debd861e18 (HEAD -> master)
| Author: zk <jasonkayzk@gmail.com>
| Date: Tue Oct 22 19:35:03 2019 +0800
|
| commit: test
|
* commit 8fc21ae52d87b63a53b43f4ebcf676550fb5d2ae
Author: zk <jasonkayzk@gmail.com>
Date: Tue Oct 22 17:31:37 2019 +0800
commit: test_master.txt
7. 重命名分支
git branch –m <当前分支名> <新的分支名>
zk@jasonkay:~/test$ git branch -a
master
* test
zk@jasonkay:~/test$ git branch -m test test2
zk@jasonkay:~/test$ git branch -a
master
* test2
8. 总结
git与分支相关的说明总结如下表:
说明 | 命令 | 备注 |
---|---|---|
查看分支(本地+远程) | git branch -a | |
创建分支 | git branch <分支名称> | |
切换分支 | git checkout <分支名称> | 也支持 git switch <分支名称> |
合并分支 | git merge <被当前分支合并的分支名> | 需要切换其他分支完成合并 |
创建+切换分支 | git checkout -b <分支名称> | 或者使用 git switch -c <分支名称> |
删除分支 | git branch -d <分支名称> git branch -D <分支名称> |
删除不是当前分支使用-d 强行删除当前打开分支-D |
分支恢复 | git branch <分支名称> <删除分支时散列值> | |
显示本地仓储的commit | git reflog | 包括所有branch的commit,甚至包括已经撤销的commit. 只要HEAD发生了变化, 就会在reflog里面看得到 |
显示历史/分支图 | git log [–graph] [–pretty=oneline] [–abbrev-commit] | 可添加git log的参数做显示优化 |
重命名分支 | git branch –m <当前分支名> <新的分支名> |
三. Git分支与远程仓库
1. git克隆远程仓库的指定分支
普通克隆方式:
git clone <远程仓库地址>
这种克隆方式默认是克隆master主分支, 而且通过命令 git branch –list 能看到克隆后在本地也只有这一个分支, 如果再通过新建分支再拉取指定分支,甚至可能还需要解决冲突,太繁琐.
有效的直接克隆远程指定分支, 只需要一条命令:
git clone -b <指定分支名> <远程仓库地址>
会自动在克隆该分支在本地,同样克隆后本地只有这一个分支!
2. 本地分支推送远程仓库
对于本地创建的分支, 该如何推送到远程仓库呢?
下面看一个例子:
首先, 创建一个test分支并切换至:
$ git checkout -b test
然后进行一些添加并提交:
$ vi test.txt
# 添加以下内容
This file is in test branch!
$ git add test.txt
$ git commit -m 'save branch commit'
...
此时, 新增了一个分支, 并作出了内容的修改, 而远程是没有这个分支的! 所以在新的分支下直接提交将会报错!
错误内容类似下面:
$ git push
fatal: The current branch test has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin test
当直接直接git push
的时候,就会报错提示没有设置上游的远程仓库,只要按照提示执行即可:
$ git push --set-upstream origin test
Total 0 (delta 0), reused 0 (delta 0)
* [new branch] test -> test
Branch 'test' set up to track remote branch 'test' from 'origin'.
此时Github上会有对应的PR请求, 接受即可!
附录
文章参考: