git - git 分支管理

81 阅读9分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

tag

对于重大的版本我们常常会打上一个标签,以表示它的重要性

Git 可以给仓库历史中的某一个提交打上标签

比较有代表性的是人们会使用这个功能来标记发布结点( v1.0 、 v2.0 等等)

日后我们可以通过这些标签非常快捷方便的找到对应的标签并进行使用或回退到某个具体的tag

tag主要分为如下两种

  • 轻量标签(lightweight)
  • 附注标签(annotated)
# 创建轻量标签
# tag后的标签名可以是任意名称 类型是字符串
# 也就是说 可以是git tag demo 也可以是 git tag v1.0.0
# 一般来说 推荐使用版本号作为tag的描述信息
git tag v1.0.0

# 打上一个附注标签(也就是有描述信息的标签)
git tag -a v1.1.0 -m '这是一个附注标签'

# 查看所有的tag
# 此时对应的标签会以列表的形式进行展示
# 如果存在附注标签 并不会显示对应的描述信息
git tag

# 如果需要查看附注标签的具体描述信息 需要使用git show命令
git show <tagname>

# 默认情况下,git push 命令并不会传送标签到远程仓库服务器上
# 在创建完标签后你必须显式地推送标签到git服务器上

# 提交某一个具体tag
git push <remote> <tagname>

# 提交所有未提交tags
git push <remote> --tags

# git push 命令仅仅只能提交对应的改动内容
# git push <remote> --tags 仅仅只能推送 对应的tags
# 如果我们希望上述两条命令同时执行 可以使用`git push & git push --tags`
# `git push & git push --tags`对应的语法糖写法为 git push <remote> --follow-tags
# git push <remote> --follow-tags 操作会执行如下操作:
# 1. 将改动内容进行推送
# 2. 改动内容推送成功后,在推送与本次改动内容有关的所有tags)、
#    注意: 是和本次内容有关的tags, 而不是所有的tags
git push <remote> --follow-tags

# 删除本地tag
git tag [-d|--delete] <tagname>

# 删除远程tag
git push <remote> –delete <tagname>

# 检出tag --- 回退到tag所在的对应文件状态
# 如果你想查看某个标签所指向的文件版本,可以使用 git checkout 命令
# 通常我们并不会也不能直接修改历史状态,而是会基于历史状态创建一个新的分支,并在新的分支上进行我们所需要的操作
git checkout <tagname>

Git提交对象(Commit Object)

在进行提交操作时,Git 会保存一个提交对象(commit object)

  • 该提交对象会包含一个指向暂存内容快照的指针
  • 该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针
    • 首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象
    • 而由多个分支合并产生的提交对象有多个父对象

image.png

image.png

对应的提交记录会以二进制的方式被存储在.git/object

tree .git/objects 

: '
.
├── 05
│   ├── bab8a46a9ef3711383c7ce3f5273b714af8d50
│   └── dd69d4377f608a596848805d5f34b086067a7b
├── 1d
│   └── 3d466fb53c80cf2da4004fa833a4011db7b762
├── 69
│   └── 492462348ac0a4b02990d7865ddb053a1bfac4
├── df
│   └── c144b007ffd87fc7618f8002aa81e8fc2a69c8
├── e5
│   └── 809bdfcf88b51bd11a56172d14e9ca7f06ac09
├── info
└── pack
'
# 我们可以通过git cat-file 命令来查看.git/object下的文件类型和内容
# 对应的commitId值 为 文件夹名 + 文件名

# -t 查看文件类型 -p 查看文件内容
git cat-file -t e580 # blob
git cat-file -p e580 # console.log('Hello World')

# 当我们使用 git add 命令的时候
# 其本质就是将我们对应转换为二进制文件 存储到 .git/objects文件夹下

# 在我们执行git commit后 会在.git/commit下 生成一个commit 对象
# 所以单纯执行git add 并不会将文件关联到提交历史中
# 而是需要通过执行git commit命令来将对应修改和提交历史进行关联
: '
	# 以下是一个commit对象
	# tree 属性记录着实际当前提交文件所在的对应文件内容
  tree 69492462348ac0a4b02990d7865ddb053a1bfac4
  # author 和 committer 一般都是一样的 记录着当前提交者信息
  author klaus <klauswang.2018@gmail.com> 1658624546 +0800
  committer klaus <klauswang.2018@gmail.com> 1658624546 +0800
  # 除了第一次提交之外,之后的每一个commit对象都存在一个parent属性
  # 记录着上一次的提交记录,如果当前提交是分支合并 那么可能会存在多个parent记录
'

git cat-file -p 6949
: '
	# 在tree文件中 记录这提交文件的名称 和存储修改内容对应的二进制文件
	100644 blob 05dd69d4377f608a596848805d5f34b086067a7b    .gitignore
	100644 blob 05bab8a46a9ef3711383c7ce3f5273b714af8d50    README.md
	100644 blob dfc144b007ffd87fc7618f8002aa81e8fc2a69c8    index.html
	100644 blob e5809bdfcf88b51bd11a56172d14e9ca7f06ac09    index.js
'

分支

本地分支操作

Git 的分支,其实本质上仅仅是指向最新提交对象的可移动指针

Git 的默认分支名字是 master,master 分支会在每次提交时自动移动

在多次提交操作之后,master指针一定指向最新的那个提交对象

image.png

# 创建分支
git branch <newbranchname>

# 切换分支
git checkout <newbranchname>

# 新建分支 并 同时切换到新建分支上
git checkout -b <newbranchname> 

git中有一个特殊的指针HEAD,他永远指向的是类似于master,testing这类的branch指针

当某一个分支被HEAD指针所指向的时候,表示对应的分支即为当前活跃分支

image.png

git branch # 查看当前所有的分支

git branch –d hotfix # 删除当前分支
git branch –D hotfix # 强制删除某一个分支

git branch –v # 查看当前所有的分支, 同时查看最后一次提交

git branch --merged # 查看所有合并到当前分支的分支
git branch --no-merged # 查看所有没有合并到当前分支的分支

远程分支操作

image.png

image.png

# 新建一个名为main的本地分支
# 同时设置main的上游分支为 origin/main
git checkout --track origin/main

# 设置上游分支
# 创建一个本地分支<branch>
# 并设置其上游分支为<remote branch>

# <branch>是可以省略的
# 如果在checkout命令中省略 表示创建一个和远程分支同名的本地分支
git checkout -b <branch> --track <remote branch>

# 下面这个命令是git checkout -b <branch> --track <remote branch>的简写形式
# 1. 新建一个分支 并切换到新建的那个分支
# 2. 设置新建分支的上游分支为 远程仓库中与新建分支同名的那个远程分支
# 3. 将上游分支的代码 拉取到本地 并进行合并
git checkout <branch>

# 也可以在推送的时候 设置 上游分支
git push --set-upstream-to=<remote>/<branch>

# 或者为一个已经存在的现有分支设置上游分支
git branch --set-upstream-to <remote branch> <branch>

# 当你想要公开分享一个分支时,需要将其推送到有写入权限的远程仓库上
git push <remote> <branch>

# 删除远程分支
git push <remote> --delete <branch>

git rebase vs git merge

在 Git 中整合来自不同分支的修改主要有两种方法: merge 以及 rebase

现在有如下分支结构

image.png

当我执行merge操作后,对应的commit历史记录会变成如下结构

merge.png

对于merge操作而言,会记录全部的历史操作记录

这也就意味着merge操作后,git的历史记录将不会是线性

而当我们对原有结构进行rebase操作之后,git的历史记录结构将变为

rebase.png

rebase操作和merge操作所做的功能是类似的,但是rebase会简化我们对应的历史操作

所以在rebase(变基)后,我们对应的历史记录是线性的,相比较merge后的历史记录更为的简洁

rebase基本原理

在进行合并之前, master分支和feature分支拥有共同的base 即为bbb

而当我们在feature上执行git rebase master

git会将feature分支的base 设置成 master分支上的最新commit对象

jj0p59.png

此时我们可以看到 feature分支的修改以线性的形式合并到了master分支中

此时我们只需要将master指针也指向最新的commit对象(在这里 即为 feature_01)即可

# 在master分支上执行
# 此时master指针 仅仅只要移动对应的位置即可
# 这种merge方式 也被称之为 fast-forward
git merge feature

简而言之

  1. merge用于记录git的所有历史,那么分支的历史错综复杂,也全部记录下来
  2. rebase用于简化历史记录,将两个分支的历史简化,整个历史更加简洁

假设存在如下历史记录结构

image.png

如果我们在master上执行git rebase dev

image.png

如果我们执行

# 在dev分支上执行
git rebase master

# 在master分支上执行
git merge dev

image.png

因此 在master上面使用rebase,会造成大量的提交历史在master分支中不同

而master分支又是我们的主分支 是我们的用于上线的分支

所以 rebase有一条黄金法则: 永远不要在主分支上使用rebase

git flow

Git Flow是一套基于git的工作流程,这个工作流程围绕着项目的整个生命周期定义了一个严格的如何建立分支的模型

由于Git上分支的使用的便捷性,产生了很多Git的工作流,也就是说,在整个项目开发周期的不同阶段,你可以同时拥有多个开放的分支

image.png

分支名功能
master主分支
只记录那些上线的功能
development
或者叫sit(System Integration Testing)
开发分支或者叫测试分支
日常开发和测试的分支
feature新功能开发
fixbug修复

git cheat sheet

image.png

Gitlab 安装和使用


npm version

git 工作流 fix style feature

VM

Git


--soft

homebrew 基本命名使用

curl

git switch git restore