Git学习笔记之分支管理

廖雪峰Git教程学习笔记

前言

关于教程作者

廖雪峰,十年软件开发经验,业余产品经理,精通Java/Python/Ruby/Visual Basic/ObjectiveC等,对开源框架有深入研究,著有《Spring 2.0核心技术与最佳实践》一书,多个业余开源项目托管在GitHub,个人官网:https://www.liaoxuefeng.com

概述

假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。

创建与合并分支

在版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向mastermaster才是指向提交的,所以,HEAD指向的就是当前分支。
一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:
在这里插入图片描述
每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。
当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:
在这里插入图片描述
你看,Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化!
不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:
在这里插入图片描述
假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:
在这里插入图片描述
所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:
在这里插入图片描述
下面示例演示。
首先,我们创建dev分支,然后切换到dev分支:

$ git checkout -b dev
Switched to a new branch 'dev'

git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:

$ git branch dev
$ git checkout dev
Switched to branch 'dev'

然后,用git branch命令查看当前分支:

$ git branch
* dev
  master

git branch命令会列出所有分支,当前分支前面会标一个*号。
然后,我们就可以在dev分支上正常提交,比如对readme.txt做个修改,加上一行:

Creating a new branch is quick.

然后提交:

$ git add readme.txt
$ git commit -m "branch test"
[dev fa80e64] branch test
 1 file changed, 1 insertion(+)

现在,dev分支的工作完成,我们就可以切换回master分支:

$ git checkout master
Switched to branch 'master'

切换回master分支后,再查看一个readme.txt文件,刚才添加的内容不见了!因为那个提交是在dev分支上,而master分支此刻的提交点并没有变:
在这里插入图片描述
现在,我们把dev分支的工作成果合并到master分支上:

$ git merge dev
Updating a1c3eb9..fa80e64
Fast-forward
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

git merge命令用于合并指定分支到当前分支。合并后,再查看readme.txt的内容,就可以看到,和dev分支的最新提交是完全一样的。
注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。
当然,也不是每次合并都能Fast-forward,我们后面会讲其他方式的合并。
合并完成后,就可以放心地删除dev分支了:

$ git branch -d dev
Deleted branch dev (was fa80e64).

删除后,查看branch,就只剩下master分支了:

$ git branch
* master

因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。

解决冲突

准备新的feature1分支,继续我们的新分支开发:

$ git switch -c feature1
Switched to a new branch 'feature1'

修改readme.txt最后一行,改为:

Creating a new branch is quick AND simple.

feature1分支上提交:

$ git add readme.txt
$ git commit -m "AND simple"
[feature1 fac71c7] AND simple
 1 file changed, 1 insertion(+), 1 deletion(-)

切换到master分支:

$ git checkout  master
Switched to branch 'master'

master分支上把readme.txt文件的最后一行改为:

Creating a new branch is quick & simple.

提交:

$ git add readme.txt 
$ git commit -m "& simple"
[master 8f7809f] & simple
 1 file changed, 1 insertion(+), 1 deletion(-)

现在,master分支和feature1分支各自都分别有新的提交,变成了这样:
在这里插入图片描述
这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,我们试试看:

$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.

果然冲突了!Git告诉我们,readme.txt文件存在冲突,必须手动解决冲突后再提交。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:   readme.txt
no changes added to commit (use "git add" and/or "git commit -a")

我们可以直接查看readme.txt的内容:

$ cat readme.txt
Git is a version control system.
Git is free software.
Git has a index called stage.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1

Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,<<<<<<<,=======修改,=======,>>>>>>>之间的是合并过来的分支的内容,手动删除符号后保存。
再提交:

$ git add readme.txt
$ git commit -m "conflict fixed"
[master 9c2db59] conflict fixed

现在,master分支和feature1分支变成了下图所示:
在这里插入图片描述
使用带参数的git log查看分支的合并情况:

$ git log --graph --pretty=oneline --abbrev-commit

--graph 是图形化, --pretty=oneline 是一行显示, --abbrev-commit 是只显示每次提交id的前几位,执行结果如下:

*   9c2db59 conflict fixed
|\
| * fac71c7 AND simple
* | 8f7809f & simple
|/
* fa80e64 branch test
* a1c3eb9 null
*   462a903 Merge branch 'master' of github.com:uidq5232/gitRepo
|\
| * a759e3f Update test.txt
| * 7a04e30 a test
* 0e437ff remove test.txt
* 789e43f add test.txt
* f086944 understand how stage works
* 92a925f this is a test
uidq5232@hzh6137u:/work/gitRepo$ git branch -d feature1
Deleted branch feature1 (was fac71c7).

最后,删除feature1分支:

$ git branch -d feature1
Deleted branch feature1 (was fac71c7).

工作完成。

分支管理策略

Git一般会用Fast forward模式合并分支,合并之后不会留下分支信息,这样的话删除分支后会丢失掉分支信息。强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。

下面我们实战一下--no-ff方式的git merge

首先,仍然创建并切换dev分支:

$ git switch -c dev
Switched to a new branch 'dev'

修改readme.txt文件,并提交一个新的commit

$ git add readme.txt 
$ git commit -m "add merge"
[master 5170837] add merge
 1 file changed, 1 deletion(-)

现在,我们切换回master

$ git switch master
Switched to branch 'master'

准备合并dev分支,请注意--no-ff参数,表示禁用Fast forward

$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
 readme.txt | 1 +
 1 file changed, 1 insertion(+)

因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。

合并后,我们用git log看看分支历史:

$ git log --graph --pretty=oneline --abbrev-commit
*   eb4ef53 merge with no-ff
|\
| * 1d94bb3 add merge
|/

可以看到,不使用Fast forward模式,merge后就像这样:
在这里插入图片描述

分支策略

在实际开发中,我们应该按照几个基本原则进行分支管理:
master分支应该是非常稳定的,仅用来发布新版本,平时不能在上面干活;
dev分支用来干活,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本。

Bug分支

在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。但是当前正在dev上还有未提交的正在进行的工作,此时先用git stash将工作现场保存起来,等修复完bug再恢复现场继续工作。

$ git stash
Saved working directory and index state WIP on dev: 1d94bb3 add merge
HEAD is now at 1d94bb3 add merge

此时用git status查看工作区,显示是干净的

$ git status
On branch dev
nothing to commit, working directory clean

确定要在哪个分支上修复bug,假定需要在master分支上修复代号01的bug,就从master创建临时分支:

$ git checkout master
Switched to branch 'master'
$ git checkout -b issue-01
Switched to a new branch 'issue-01'

现在修复bug,需要把“Git is free software …”改为“Git is a free software …”,然后提交:

$ git add readme.txt
$ git commit -m "fix bug 01"
[issue-01 79dec3f] fix bug 01
 1 file changed, 1 insertion(+), 1 deletion(-)

修复完成后,切换到master分支,并完成合并,最后删除issue-01分支:

$ git checkout master
Switched to branch 'master'
$ git merge --no-ff -m "merged bug fix 01" issue-01
Merge made by the 'recursive' strategy.
 readme.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

bug修复完毕,回到dev分支继续干活,使用stash list查看可知工作现场被存放在了某个地方:

$ git checkout dev
Switched to branch 'dev'
$ git stash list
stash@{0}: WIP on dev: 1d94bb3 add merge

恢复工作现场有两种方式:
一种是用git stash apply恢复,但是恢复后,stash内容并没有删除,需要用git stash drop来删除;
另一种方式是用git stash pop,恢复的同时把stash内容也删了:

$ git stash pop
On branch dev
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
        modified:   readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (839c903d2a8ba253605275f85d828dc679493beb)
$ git stash list

恢复之后再次使用stash list就看不到任何内容了。
可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:

$ git stash apply stash@{0}

dev分支是早期从master分支分出来的,master分支上修复了bug后,在当前dev分支上也存在这个bug。
同样的bug,要在dev上修复,只需要把4c805e2 fix bug 101这个提交所做的修改“复制”到dev分支。注意:我们只想复制4c805e2 fix bug 101这个提交所做的修改,并不是把整个master分支merge过来。
为了方便操作,Git专门提供了一个cherry-pick命令,让我们能复制一个特定的提交到当前分支:

$ git branch
* dev
  master
$ git cherry-pick 79dec3f
[master 1d4b803] fix bug 101
 1 file changed, 1 insertion(+), 1 deletion(-)

如果提示报错,可以切换回dev后先使用git cherry-pick命令,再使用git stash pop命令就可以了。

强行删除分支

master分支上删除一个已经提交但没有合并的其它分支,则会报错:

$ git branch -d issue-101 
error: The branch 'issue-101' is not fully merged. 
If you are sure you want to delete it, run 'git branch -D issue-101'.

这时可以用参数 -D 强制删除:

$ git branch -D issue-101

需要注意的是,由于分支未合并,删除之后就没有任何记录了,分支上所有的修改也会丢失。

多人协作

从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin
要查看远程库的信息,用git remote

$ git remote
origin

或者,用git remote -v显示更详细的信息:

$ git remote -v
origin  git@github.com:[用户名]/[仓库名].git (fetch)
origin  git@github.com:[用户名]/[仓库名].git (push)

上面显示了可以抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。

推送分支

推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:

$ git push origin master

如果要推送其他分支,比如dev,就改成:

$ git push origin dev

但是,并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?

master分支是主分支,因此要时刻与远程同步; dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;

bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug

feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。

总之,就是在Git中,分支完全可以在本地自己藏着玩,是否推送,视你的心情而定!

抓取分支

多人协作时,大家都会往master和dev分支上推送各自的修改。
现在,模拟一个你的小伙伴,可以在另一台电脑(注意要把SSH Key添加到GitHub)或者同一台电脑的另一个目录下克隆:

$ git clone git@github.com:[用户名]/[仓库名].git
Cloning into 'gitRepo'...
remote: Enumerating objects: 61, done.
remote: Counting objects: 100% (61/61), done.
remote: Compressing objects: 100% (34/34), done.
remote: Total 61 (delta 23), reused 59 (delta 21), pack-reused 0
Receiving objects: 100% (61/61), 5.96 KiB | 0 bytes/s, done.
Resolving deltas: 100% (23/23), done.
Checking connectivity... done.

当你的小伙伴从远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支。使用git branch命令可以看到:

$ git branch
* master

现在,你的小伙伴要在dev分支上开发,就必须创建远程origindev分支到本地,于是他用这个命令创建本地dev分支:

$ git checkout -b dev origin/dev

现在,他就可以在dev上继续修改,然后,时不时地把dev分支push到远程:

$ git add readme.txt
$ git commit -m "add readme"
[dev 6a50e8b] add readme
 1 file changed, 1 insertion(+)
$ git push origin dev
Counting objects: 3, done.
Delta compression using up to 12 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 345 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To git@github.com:uidq5232/gitRepo.git
   90025cd..6a50e8b  dev -> dev

你的小伙伴已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:

$ git add readme.txt
$ git commit -m "add new readme"
[dev fc40080] add new readme
 1 file changed, 2 insertions(+)
$ git push origin dev

推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送:

$ git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置devorigin/dev的链接:

$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

再pull:

$ git pull
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.

这回git pull成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的解决冲突完全一样。解决后,提交,再push就成功了:

$ git add readme.txt
$ git commit -m "fix read conflict"
[dev 762794f] fix read conflict
$ git push origin dev
Counting objects: 6, done.
Delta compression using up to 12 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 684 bytes | 0 bytes/s, done.
Total 6 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 1 local object.
To git@github.com:uidq5232/gitRepo.git
   6a50e8b..762794f  dev -> dev

因此,多人协作的工作模式通常是这样:
首先,可以试图用git push origin <branch-name>推送自己的修改;
如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
如果合并有冲突,则解决冲突,并在本地提交;
没有冲突或者解决掉冲突后,再用git push origin <branch-name>推送就能成功!
如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>建立连接。
这就是多人协作的工作模式。

<think>好的,用户想学习Git分支管理的相关知识,包括教程和最佳实践。首先,我需要整理Git分支管理的基础概念,然后根据提供的引用内容,总结常见的分支策略和最佳实践。用户可能对不同的分支模型(如Git Flow、GitHub Flow等)感兴趣,需要逐一解释它们的特点和适用场景。同时,结合引用中的信息,比如引用1提到的五种分支策略,引用3和4中的具体操作步骤,以及引用5的最佳实践,我需要将这些内容结构化,以清晰易懂的方式呈现。还要注意用户可能的需求,比如如何选择合适的分支策略,常用命令有哪些,以及如何解决实际开发中的问题(如修复bug时的分支管理)。最后,生成相关问题帮助用户进一步学习,确保回答覆盖了用户可能关心的各个方面。</think>### Git分支管理学习教程与最佳实践 #### 一、Git分支基础概念 1. **分支的本质** Git分支本质是一个指向提交对象的**可变指针**,与SVN等工具不同,Git分支创建和切换成本极低[^4]。 2. **默认分支** `master`(或`main`)分支是默认创建的主分支,但无特殊性质,与其他分支完全平等[^2]。 #### 二、常见分支管理策略 1. **Git Flow** - 特点:严格定义开发、发布、热修复流程 - 分支结构: ``` master(生产环境) ├── develop(集成分支) │ ├── feature/*(功能开发分支) │ └── release/*(预发布分支) └── hotfix/*(紧急修复分支) ``` - 适用:需要长期维护的复杂项目[^1] 2. **GitHub Flow** - 特点:简化流程,强调持续部署 - 规则: - 所有开发基于`master`分支 - 通过Pull Request进行代码审查 - 合并后立即部署 - 适用:SaaS类快速迭代项目[^1] 3. **Trunk-Based Development** - 特点:所有开发者直接向`master`提交代码 - 关键实践: - 短生命周期分支(1-2天) - 特性开关控制功能发布 - 适用:高成熟度团队与CI/CD深度集成项目 #### 三、最佳实践指南 1. **分支命名规范** - 功能分支:`feature/login-page` - 修复分支:`hotfix/payment-bug` - 发布分支:`release/v2.1.0` 2. **工作区管理技巧** ```bash # 临时保存未完成的工作(引用3示例) git stash git checkout master git checkout -b hotfix/xxx # 修复完成后恢复工作区 git stash pop ``` 3. **二进制文件处理** - 使用`git diff --numstat`检测二进制文件变更[^5] 4. **第三方库管理** - 建议通过子模块(submodule)或包管理器独立管理[^5] #### 四、常用命令速查 | 操作场景 | 命令示例 | |-------------------------|-----------------------------------| | 创建分支 | `git branch feature/new-api` | | 切换分支 | `git checkout develop` | | 合并分支(无冲突) | `git merge feature/login` | | 删除已合并分支 | `git branch -d feature/old` | | 查看分支图 | `git log --graph --oneline --all`| #### 五、策略选择建议 1. **初创团队** → GitHub Flow 2. **传统软件** → Git Flow 3. **微服务架构** → Trunk-Based Development 4. **紧急修复** → 单独创建`hotfix`分支[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值