Skip to content

分支管理

我们往往是团队合作,但每个人的进度、节奏都不一样

  • 所以如果代码都提交到一个同一个主干上,难免会出错

怎么解决这个问题呢?答案就是使用分支!

  • 主干分支是对外的稳定版本
  • 如果需要新功能时,就开一个支干分支来进行开发
  • 支干分支与主干分支是相互独立的
  • 等到开发完毕后,再将的代码支干分支合并到主干分支上
  • 这样,既安全,又互不影响,每个人都能保持自己的节奏!

1.创建分支

1.使用git checkout命令,加上-b参数,即 创建并切换到分支

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

# 或下面这种方式(新版Git使用,语义更清晰)【推荐】
$ git switch -c dev
Switched to a new branch 'dev'
  • 相当于以下两条命令:
git branch dev   # 创建dev分支
git checkout dev # 切换到dev分支

2.然后,我们就可以在dev分支上正常修改、暂存、提交了

2.查看所有分支

  • git branch命令会列出所有分支。当前所在的分支前面会标一个*
$ git branch
* dev
  master

3.切换分支

方式一:使用git checkout命令切换分支

(dev)
$ git checkout master
Switched to branch 'master'
D       test.txt

(master) # 切换到了 master 分支
$

方式二:使用git switch命令来切换分支**【推荐】**

  • 前面讲过的撤销修改git checkout -- <file>,切换分支也是使用git checkout
  • 同一个命令,有两种作用,确实有点令人迷惑。
  • 所以最新版本的 Git,就提供了git switch命令,语义更清晰,比git checkout更容易理解
# 创建并切换到新的dev分支
$ git switch -c dev
Switched to a new branch 'dev'

# 切换到的master分支
$ git switch master

4.合并分支

  • git merge命令,用于合并指定分支到当前分支
(master) # 当前分支是master
$ git merge dev # 把dev分支的修改,合并到当前分支(即master)上
  • 补充:
  • 因为创建、合并和删除分支非常快,所以 Git 鼓励你使用分支完成某个任务,合并后再删掉分支
  • 这和直接在master分支上工作效果是一样的,但过程更安全
  • 示例:
(dev) # 当前是dev分支
$ cat test.txt  # 文件修改前
aaaa
aaaa
aaaaaaaaaa

(dev)
$ vim test.txt # 1.在dev分支修改文件

(dev)
$ cat test.txt # 文件修改后
aaaa

 (dev)
$ git add test.txt # 2.暂存文件

(dev)
$ git commit -m 'delete somethings' # 3.提交修改
[dev 9c7b24e] delete somethings
 1 file changed, 2 deletions(-)

$ git checkout master # 4.切回master分支
Switched to branch 'master'

# 示意图:
                  HEAD
                                                         master
                                        ┌───┐    ┌───┐    ┌───┐    ┌───┐
   │───▶│   │───▶│   │───▶│   └───┘    └───┘    └───┘    └───┘
                                                                                      dev

(master)# 当前是master分支
$ cat test.txt  # 查看文件:此时master分支的文件,没有被dev分支的修改操作影响
aaaa
aaaa
aaaaaaaaaa

(master)
$ git merge dev # 5.把dev分支的工作成果,合并到当前的master分支上
Updating 65458a4..9c7b24e
Fast-forward # Fast-forward:这次合并是“快进模式”,也就是直接把master指向dev的当前提交
    # 这种方式的合并不会创建新的合并提交(merge commit),而是直接将master分支的指针移动到要合并的分支的最新的提交
    # 当然,也不是每次合并都能Fast-forward模式,后面会讲其他种合并方式
 test.txt | 2 --
 1 file changed, 2 deletions(-)

(master)
$ cat test.txt # 再查看文件:发现是dev分支修改后的内容了
aaaa

 (master)
$ git branch -d dev # 6.合并成功后,就可以放心地删除分支了
Deleted branch dev (was 9c7b24e).

(master)
$ git branch  # 7.查看分支,发现只剩下master分支了
* master

5.删除分支

1.使用git branch -d <name>,来删除 已经合并的分支

  • 注意:不可以在分支中,删除分支本身
  • 一般是切换到master分支后,再删除其他分支
(master) # 在master分支
$ git branch -d dev  # 删除dev分支
Deleted branch dev (was 9c7b24e).

2.使用git branch -D <name>,来**强行删除** 还没有合并的分支

  • 使用git branch -d <name>,来删除**还没有合并的分支**时,会报错
  • 然而,有时候必须强行删除,防止信息泄露
  • 有时候就是这样的,开发到一半功能不要啦!而为了防止代码泄露,所以就得强行删除分支!
(master) # 在master分支
$ git branch -D dev  # 强行删除还没有合并的dev分支
Deleted branch dev (was 287773e).

6.解决分支合并冲突

(1)问题描述

如果两个分支都对同一个文件进行修改提交,就不能使用“快速合并”(Fast-forward)模式了,否则合并时会产生冲突

  • 因为合并时,Git 不确定到底应该保留哪个分支的修改、以哪个分支的修改为主
  • 所以就产生了冲突

demo 复现:

(feature1)
$ cat test.txt # 文件修改前的内容
aaaa

(feature1) # 1.在feature1分支进行修改
$ vim test.txt

(feature1)
$ cat test.txt # 修改后的内容
aaaa
a

# 2.然后在feature1分支进行暂存、提交
(feature1)
$ git add test.txt

(feature1)
$ git commit -m 'feature1 add somethings'
[feature1 87a87d7] feature1 add somethings
 1 file changed, 1 insertion(+)


(master)
$ cat test.txt # master分支查看文件,未被修改
aaaa

(master) # 3.master分支修改文件
$ vim test.txt

(master)
$ cat test.txt # 修改后的内容
aaaa
a
a

# 4.然后在master分支进行暂存、提交
(master)
$ git add test.txt

(master)
$ git commit -m 'master add somethings'
[master 4f4afd9] master add somethings
 1 file changed, 2 insertions(+)

# 5.在master分支,合并feature1分支
(master)
$ git merge feature1
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt # 合并冲突了
Automatic merge failed; fix conflicts and then commit the result.

# 6.也可以通过 查看git库状态,发现合并冲突
(master|MERGING) # 这里也发生变化了
$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   test.txt

no changes added to commit (use "git add" and/or "git commit -a")

# 7.此时再查看文件
(master|MERGING)
$ cat test.txt
aaaa
a
# 标记之上的部分,都是共有的
<<<<<<< HEAD  # 表示冲突的开始,且接下来的内容是属于 HEAD 分支
a     # <<<<<<< HEAD 到 ======= 之间的 a ,是 HEAD 分支相对于共同祖先的修改
=======
>>>>>>> feature1
# ======= 到 >>>>>>> feature1 之间没有内容,表示在这个冲突区域,feature1 分支没有相对于共同祖先的额外添加,也即 它的修改止步于 aaaa a。
  • 此时,分支树如下
                            HEAD
                                                                                       master
                                                                                        ┌───┐
                         ┌─▶│   ┌───┐    ┌───┐    ┌───┐    └───┘
   │───▶│   │───▶│   │──┤
└───┘    └───┘    └───┘    ┌───┐
                         └─▶│                               └───┘
                                                                                      feature1

(2)问题解决

如何解决?很简单:手动解决冲突(所以学车还是得学手动挡[dog])

  • 也即 :

  • 1.手动编辑冲突文件,保留你想要的版本

  • 2.删除文件中的所有冲突标记
  • 3.再重新提交即可

  • 例如:

(master|MERGING)
$ cat test.txt
aaaa
a
<<<<<<< HEAD
a
=======
>>>>>>> feature1

# 1.解决冲突
(master|MERGING)
$ vim test.txt
## 1.1保留feature1版本,删除HEAD版本的内容;
## 1.2.删除所有冲突标记

(master|MERGING)
$ cat test.txt
aaaa
a

# 2.再进行提交
(master|MERGING)
$ git add test.txt

(master|MERGING)
$ git commit -m 'keep feature1'
[master a9de091] keep feature1  # 提交成功

(master)  # 冲突解决了
$


# 现在,合并的示意图如下:
                                     HEAD
                                                                                                                  master
                                                                                                          ┌───┐    ┌───┐
                         ┌─▶│   │───▶│   ┌───┐    ┌───┐    ┌───┐    └───┘    └───┘
   │───▶│   │───▶│   │──┤             └───┘    └───┘    └───┘    ┌───┐                               └─▶│   │──────┘
                            └───┘
                                                                                      feature1

补充:查看冲突合并图

# 1.查看完整信息
$ git log --graph
*   commit a9de0916b9167fe608a791acd4d3cb8c3d4b2ba3 (HEAD -> master)
|\  Merge: 4f4afd9 87a87d7
| | Author: wangshangjian <wsj15685374939@163.com>
| | Date:   Sun Sep 15 12:22:08 2024 +0800
| |
| |     keep feature1
| |
| * commit 87a87d74090e687abf73862df5acdd9c1bd3ac20 (feature1)
| | Author: wangshangjian <wsj15685374939@163.com>
| | Date:   Sun Sep 15 11:52:02 2024 +0800
| |
| |     feature1 add somethings
| |
* | commit 4f4afd9403a0298eaf715f688bf4af7a05fdccee
|/  Author: wangshangjian <wsj15685374939@163.com>
|   Date:   Sun Sep 15 11:55:17 2024 +0800
|
|       master add somethings


# 2.查看简略信息
$ git log --graph --pretty=oneline --abbrev-commit
*   a9de091 (HEAD -> master) keep feature1
|\
| * 87a87d7 (feature1) feature1 add somethings
* | 4f4afd9 master add somethings
|/

7.分支管理的整体策略

在实际开发中,我们应该按照几个基本原则进行分支管理

  • 首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活
  • 然后,干活都在dev分支上
  • 你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往master分支上合并就可以
  • 也就是说,dev分支是不稳定的

所以,团队合作的分支看起来就像这样:

image-20240915135143408

8.保存工作现场

当你临时接受到新任务时,自然要创建新分支;但当前分支上进行的工作还没有做完、没法提交,肿么办?

1.Git 提供了一个stash功能,可以把当前工作现场“储藏”起来,等待以后恢复现场、继续工作

  • 冻结现场
git stash
# 作用:
#   1.1保存当前工作目录的所有更改
#   1.2重置工作目录,使其与最近一次提交的状态一致

$ git status
On branch master
nothing to commit, working tree clean  # 很干净

2.可以查看储藏列表

$ git stash list
stash@{0}: WIP on master: 9cfd34c Merge branch 'feature1'

3.恢复工作现场

  • 也即应用储藏
git stash apply [stash-name]

# 注意:
#   3.1 如果不指定 `[stash-name]`,Git 默认使用最近的储藏(这对后面的几个命令也适用)
#   3.2 应用储藏不会将工作现场从储藏列表中删除,这意味着你可以多次应用同一个储藏。

# 示例:
$ git stash apply stash@{0}
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   test.txt

no changes added to commit (use "git add" and/or "git commit -a")

4.删除储藏

  • 这会从储藏列表中,删除指定的储藏。
git stash drop [stash-name]

# 示例
$ git stash drop stash@{0}
Dropped stash@{0} (6803dc11d0fffa6c1bf962e5358ecdb0de71c38f)

$ git stash list

5.恢复工作现场的同时,把储藏也删了

  • 3、4 的简写
git stash pop [stash-name]  # 弹出

9.多人协作开发

(1)查看远程库的信息

  • 当你克隆远程仓库时,实际上 Git 自动把本地的master分支,和远程的master分支关联起来了,并且,远程仓库的默认名称是origin

1.要查看远程库的信息,用git remote

$ git remote
origin

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

$ git remote -v
origin  https://gitee.com/xiaofuce233/test.git (fetch)
origin  https://gitee.com/xiaofuce233/test.git (push)

上面显示了,可以**抓取(fetch)**和**推送(push)**的origin的地址。

  • 如果没有推送权限,就看不到 push 的地址

(2)推送分支

  • 推送分支,就是**把本地 git 库中目标分支上的提交,推送到远程库**。

1.推送时,要指定 远程库本地分支

  • 这样,Git 就会把该分支,推送到对应的远程库的相应分支上
# origin是远程库
# master是本地分支
$ git push origin master

Enumerating objects: 24, done.
Counting objects: 100% (23/23), done.
Delta compression using up to 16 threads
Compressing objects: 100% (11/11), done.
Writing objects: 100% (20/20), 1.71 KiB | 1.71 MiB/s, done.
Total 20 (delta 4), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Powered by GITEE.COM [1.1.5]
remote: Set trace flag d3438082
To https://gitee.com/xiaofuce233/test.git
   b4def30..0f3985a  master -> master
  • 如果要推送其他分支,比如dev,就改成:
$ git push origin dev

2.但并不是本地的所有分支都要往远程推,那么,哪些分支需要推送,哪些不需要呢?

  • master分支是主分支,因此要**时刻与远程同步**;

  • dev分支是开发分支,团队所有成员都需要在上面工作,所以**也需要与远程同步**;

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

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

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

3.多人协作时,大家都会往masterdev分支上推送各自的修改。

(3)拉取分支

拉取分支有多种方式

**1.**当通过git clone克隆远程仓库时,默认只会拉取到master分支

$ git clone git@github.com:michaelliao/learngit.git
$ git branch
* master

**2.**当想要在dev分支上开发时,就必须本地创建dev分支,然后和远程origindev分支关联

  • 主要目的:用于基于远程分支,创建新的本地分支
  • 通常不涉及合并,因为它是基于一个远程分支,创建新的本地分支
git checkout -b [branch] [remote]/[branch]
# 命令格式允许你创建一个新的本地分支,并立即切换到这个分支,
# 同时这个新分支会设置为跟踪指定的远程分支

$ git checkout -b dev origin/dev

# 它执行了两个操作:
#   首先,它基于远程分支 origin/dev 创建并检出了一个新的本地分支 dev,并立即切换到这个分支
#   其次,它将这个新创建的本地分支, 设置为 跟踪远程分支 origin/dev。

# 或
$ git switch -c dev origin/dev

**3.**使用git pull命令,从远程仓库拉取数据,并尝试与当前检出的本地分支合并

  • 主要目的:用于更新和合并(可能涉及合并操作)
git pull [remote-name] [branch-name]

[remote-name] 是远程仓库的名称,默认是 origin。
[branch-name] 是要拉取的远程分支的名称。如果不设置,会拉取和当前本地分支相同的远程库分支

(4)推送冲突

1.当 A**已经**向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送

  • 会推送失败!因为 A 的最新提交,和你试图推送的提交有冲突
# 提交
$ git add test.txt
$ git commit -m "add new env"

$ git push origin dev # 推送
To github.com:michaelliao/learngit.git
 ! [rejected]        dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

2.解决办法也很简单,Git 已经提示了:

(1)先用git pull,把最新的提交从origin/dev拉取下来,在本地合并、解决冲突

1)即先更新你当前的分支信息

  • pull会拉取分支信息,并进行合并(前面说了)
$ 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 <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

    git branch --set-upstream-to=origin/<branch> dev

2)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'.

3)关联好两个仓库后,再 pull

  • 这回git pull成功,但是合并有冲突,需要手动解决
$ git pull
Auto-merging env.txt
CONFLICT (add/add): Merge conflict in env.txt # 有合并冲突
Automatic merge failed; fix conflicts and then commit the result.
  • 解决的方法,和分支管理中的解决分支合并冲突完全一样。

(2)然后,再提交和 push 推送

# 提交修改
$ git commit -m "fix env conflict"
[dev 57c53ab] fix env conflict

# push到远程仓库
$ git push origin dev
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 621 bytes | 621.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
   7a5e5dd..57c53ab  dev -> dev

(5)多人协作的工作模式

所以,多人协作的工作模式通常是这样:

  1. 首先,可以尝试用git push origin <branch-name>推送自己的修改;
  2. 如果推送失败,则因为远程分支比你的本地更加新,需要先用git pull试图合并;
  3. 如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建
  4. 则用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
  5. 如果合并有冲突,则解决冲突,并在本地提交;
  6. 没有冲突,或者解决掉冲突后,再用git push origin <branch-name>推送,就能成功!
本文阅读量  次
本站总访问量  次
Authors: wangshangjian