前端基础能力 - Git项目管理 - 图1

前言

和大家一起学习探讨下 Git 的内部原理,Git 基本操作,Git的版本管理策略, 最后会和大家通过一些场景问题,学习 Git 的常用黑科技, 例如:cherry-pickpatchreset vs revert

Git项目管理源码地址

源码地址:https://github.com/dkypooh/front-end-develop-demo/tree/master/base/git/git-resp

PS: 查看隐藏文件夹 .git

一句话理解Git(面试专用)

Git 的每个分支的管理类似于链表,每次提交都会产生一个 SHA1 的唯一标识符,此唯一标识符是引用的指针,后续的增删改查操作都可以基于这个指针进行索引操作。

关键字:分布式,四个分区,链表,SHA1指针

通过本章读者可以学习了解到什么?

  1. git 的核心原理,四个分区差异,代码的不同状态在四个分区流转的规则。
  2. 如何修改 git 的基本信息, 用户名,远端仓库地址
  3. 如何删除及忽略 git 已经提交的资源
  4. git reset 和 git revert 的区别
  5. git pull 和 git pull —rebase 的区别
  6. git 版本管理策略模型

认识Git

Git简史

同生活中的许多伟大事物一样,Git 诞生于一个极富纷争大举创新的年代。

Linux 内核开源项目有着为数众多的参与者。 绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002年间)。 到 2002 年,整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper 来管理和维护代码。

版本管理策略

前端基础能力 - Git项目管理 - 图2

master分支: 一般会以此分支为主分支(发布分支)。主分支的意思是说开发者不会在主分支上开发, 主分支只接受外分支的合并。合并完之后,验证通过,打 tag 上线。

develop分支: 作为日常开发分支,同时会有多人在上面提交代码,为了保证提交不冲突,尽量保证模块拆解合理,开发过程中没有多位开发者同时修改同一文件的情况。

feture_a 到 feture_n分支: 这些分支是发布之后的 bug 修复分支,不同开发者产生的 bug,会不同分支上修复 bug,最终合并到 master 分支上上线。

Git核心概念及原理

Git 是分布式版本控制系统,SVN 是集中化版本控制系统。 Git 取代了 SVN 作为当前最好的版本管理工具。

SVN缺点: SVN 集中化版本控制系统虽然能够令多个团队成员一起协作开发,但有时如果中央服务器宕机的话,谁也无法在宕机期间提交更新和协同开发。甚至有时,中央服务器磁盘故障,恰巧又没有做备份或备份没及时,那就可能有丢失数据的风险。

Git 四个工作区

Git 的文件操作原理都是基于 Workspace (工作区),Index / Stage (暂存区), Repository (仓库区) 和 Remote(远程仓库)四个工作区来进行流转。

前端基础能力 - Git项目管理 - 图3

Workspace工作区: 平时存放编辑项目代码的空间

Index / Stage暂存区: 用于临时存放你的改动,事实上它只是一个文件,保存即将提交到文件列表信息

Repository仓库区(或版本库): 就是安全存放数据的位置,这里面有你提交到所有版本的数据。其中 HEAD 指向最新放入仓库的版本

Remote远程仓库: 托管代码的服务器。例如 Github 的代码远端代码托管服务器

操作说明

  1. pull 操作,Git 会从 远端仓库 到 工作区
  2. fetch/clone 操作,Git 会从 远端仓库 到 版本仓库
  3. add 操作,Git 会从 工作区 到 暂存区
  4. commit 操作,Git 会从 暂存区 到 版本仓库

例如我们一次完整的提交 add --> commit --> push 经历的工作区变化就是

工作区 —> 缓存区 —> 本地仓库区 —> 远端仓库

Git内部构造

要理解 Git 内部构造的核心,我们应理解三个东西:实体(objects)、引用(refs)、索引(index)。,这些都会在 Git 的 .git 文件结构目录下找到对应的目录。

前端基础能力 - Git项目管理 - 图4

实体: 提交到一个 Git 代码仓库中的所有文件,包括每个提交的说明信息(the commit info)都在目录 .git/objects/ 中存储为实体。一个实体以一个 40 字符长度的字符串(该实体内容的 SHA1 哈希值)来标识。

引用: Git 中,一个分支(branch)、远程分支(remote branch)或一个标签(tag)(也称为轻量标签)仅是指向一个实体的一个指针,这里的实体通常是一个 commit 实体。这些引用以文本文件的形式存储在目录 .git/refs/ 中。

索引: 索引是一个暂存区,以二进制文件的形式存储为文件 .git/index。当 git add 一个文件,Git 将该文件的信息添加到索引中。当 git commit,Git 仅提交索引文件中列出的文件。

Git初始化

Git授权SSH

大多数 Git 服务器都会选择使用 SSH 公钥来进行授权。在 Github 或者 Gitlab 上提交代码,我们需要把 SSH 公钥复制托管到Github的

personal setting -> ssh keys

生成 SSH-Key 方法

  1. # 进入ssh目录
  2. cd ~/.ssh
  3. # 生成ssh公私钥
  4. ssh-keygen
  5. # 复制ssh公钥
  6. cat ~/.ssh/id_rsa.pub

仓库基本操作

  1. # 在当前目录新建一个Git代码库
  2. git init
  3. # 新建一个目录,将其初始化为Git代码库
  4. git init <project-name>
  5. # clone git仓库
  6. git clone <git-hub-url>
  7. # [高阶用法] clone git仓库并且制定分支
  8. git clone <url> -b <branch>

Git忽略不应该跟踪的文件

.gitignore 文件显式地指定了哪些文件不应被 Git 追踪,即被 Git 忽略掉。例如开发过程中 node_module,.vscode 等文件不需要被跟踪和提交,可以在初始化的忽略它们。

  1. # .gitignore 文件
  2. node_module
  3. .vscode

Git配置

修改用户信息

  1. # 配置信息列表
  2. git config --list
  3. # 设置用户名
  4. git config --global user.name "John Doe"
  5. # 设置邮箱
  6. git config --global user.email johndoe@example.com

设置不同的仓库源

  1. # 查看帮助
  2. git remote --help
  3. # 查看不同源
  4. git remote
  5. # 添加不同地址的源,并取一个别名
  6. git remote add [name] [url]
  7. # 删除一个源
  8. git remote remove [name]

Git基本操作

操作一次完整提交流程

当文件修改时,需要把本地仓库提交到远端仓库上面,一次完整提交路径: 工作区 —> 缓存区 —> 本地仓库区 —> 远端仓库

  1. # 修改readme文件,文件在工作区
  2. vi readme.md
  3. # 文件进入缓存区,缓存区的文件可以被checkout移除到工作区
  4. git add readme.md
  5. # 文件进入提交分支,但还是在本地
  6. git commit 'add readme'
  7. # 提交分支 push 到远端分支
  8. git push origin master

操作一次完整更新流程

多人协同开发过程中,开发者随时需要更新本地仓库代码,始终保持本地代码处于最新状态。 本小节会介绍更新本地代码的具体操作, git pullgit fetchgit pull --rebase具体操作,以及他们之间的差异。

  • git pull : git fetch + git merge
  • git pull —rebase: git fetch + git rebase

前端基础能力 - Git项目管理 - 图5

分支管理

本小节介绍如何创建一个分支,如何删除一个本地分支和远端分支。

注意:本地分支删除了,并不代表远端分支删除。如何定期清理远端分支。

  1. # 已当前分支为基础,创建daily/0.0.1分支
  2. git checkout -b daily/0.0.1
  3. # 查看本地分支及远端分支
  4. git branch -la
  5. # 强制删除本地分支
  6. git branch -D [branchName]
  7. # 删除已经Merge过的分支
  8. git branch -d [branchName]
  9. # 创建一个分支
  10. git checkout -b daily/0.0.1
  11. # 如何删除远端多余分支
  12. # 大多数情况remote_name为origin
  13. git push -delete <remote_name> <branchName>

Git提交信息检查

  1. # 查看当前工作区改动点
  2. git diff
  3. # 提交hash1和hash2的差异
  4. git diff commit_hash1 commit_hash2
  5. # 分支a和b的差异
  6. git diff branch_a branch_b
  7. # 当前改动文件
  8. git status
  9. # 查看提交历史
  10. git log
  11. # 提交历史缩减一行查看,主要是提交Hash值
  12. git log --pretty=oneline

Git高阶操作(黑科技)

merge, cherry-pick和patch使用及差别

多人协同开发中我们需要合并别人的代码(或者pick别人的部分代码),使本地分支代码达到理想最新状态。Git提供了三种合并的方式,后两者操作属于高阶操作,初学者很少知道如何使用以及他们的差别。

下图列举了三种操作的使用场景和差异,并且举例了具体操作实例。
前端基础能力 - Git项目管理 - 图6

删除 Git 缓存文件

场景: 有些情况开发者把原有不需要提交的代码提交到了远端仓库,再使用.gitignore忽略文件不生效。哪怕我们删除后再提交也没有办法忽略。这种情况下我们应该怎么解决?

方法: 我们可以使用git rm —cache 删除原来git跟踪的文件缓存,再在.gitignore里面添加忽略文件

  1. ## 当我们需要删除暂存区或分支上的文件, 同时工作区也不需要这个文件了, 可以使用
  2. git rm file_path
  3. ## 当我们需要删除暂存区或分支上的文件, 但本地又需要使用, 只是不希望这个文件被版本控制, 可以使用
  4. # PS: file_path 为文件路径
  5. git rm --cached file_path

如何强制提交

场景: 对于多人协作开发,有些时候我们会遇到版本管理混乱的情况,例如:远端版本错误了,但本地版本是正确的。 如何才能让强制更新远端版本,保持和本地工作区环境一样?

方法: 强制push本地正确的版本,但是慎用。因为它是不可逆转的。

  1. # 强制更新,慎用
  2. git push origin master --force

revert 和 reset区别

场景: 有些时候开发者需要退回到某次正确的提交记录,有些时候开发者的commit错误了,这时候可以使用 git revert 和 git reset。

  • git revert: 撤销某次操作,此次操作之前的commit都会被保留。
  • git reset : 撤销某次提交,但是此次之后的修改都会被退回到暂存区。
  1. # 强制回退到某次提交,且需要强制提交
  2. git reset ——hard commit_hash
  3. git push origin master --force
  4. # 回退到某提提交,保存提交commit记录, 重新commit
  5. git revert commit_hash
  6. git add .
  7. git commit -m "revert"
  8. git push origin master

创建Tag,如何以某个Tag创建分支

有时我们代码已经发布很长时间才发现了一个Bug,同时当前开发分支又有需求在开发,如何以之前发布版本的节点开分支修复问题,这样就可以使用 Tag 创建分支。

  1. # 创建tag
  2. # 创建标注标签
  3. git tag -a daily/0.0.1 -m "add develop file"
  4. # 简单创建tag
  5. git tag daily/0.0.1
  6. # 分享tag到远端
  7. git push origin [tagname]
  8. git push origin --tags
  9. # 如何已某个tag创建分支
  10. git checkout -b <newbranch> <tagname>

已某个远端分支为基础分支,创建本地分支

默认情况创建一个分支是以远端的 Master 分支为基础,本地创建一个 Master 分支。如果我们想直接远端某个分支为基础创建相应本地分支,可以如下操作, 或者想本地某个分支创建一个新分支,可以如下操作:

  1. # 本地从当前所在分支上创建一个新分支:
  2. git checkout -b 新分支名
  3. # 拉取远程某个分支到本地:
  4. git checkout -b 本地分支名 origin/远程分支名

子模块(submodule)的使用场景

在复杂工程项目中,可能会遇到在一个 Git 仓库中添加其他 Git 仓库的场景,Submodule是仓库的一份引用。 下文会涉及到基于 lerna 多仓库管理的情况下,lerna 会去链接各个仓库的依赖关系,但是各个仓库又是独立的,那么就需要 submodule 进行管理,具体操作如下:

  1. # 添加子模块
  2. git submodule add 仓库地址
  3. # 更新子模块
  4. git submodule update --remote 模块名称
  5. ## 删除子模块
  6. 1. git rm --cached 模块名称
  7. 2. 删除 .gitmodules 下相应子模块信息
  8. 3. 删除 .git/config 下相应子模块信息

结语

本章介绍的 Git 版本管理策略是一个最简单模型,原理上需要区分开发(日常),预发,线上环境三套环境。对于 Git 的版本管理需要根据具体场景具体分析。

欢迎在此 ISSUE 补充问题,完善场景解决方案。 Git 版本管理场景及解决方案合集

学习资料下载地址:学习资料

下面一章将要学习 Lerna包管理, 大家需要提前了解下 Lerna 基本知识。

查看 Lerna 官网 Lerna 官网

思考题

Q: Learning Git Branching网站,完成一次完整的发布更新流程。

参考文档