git相关知识整理分享

部门内要做技术分享, 我选择了分享git相关的知识, 现在贴出来, 供大家学习参考.


目录:


为什么要使用版本控制系统?

在这里插入图片描述

什么是git?

Git is a
free and open source
distributed version control system
designed to
handle everything from small to very large projects
with
speed and efficiency.
– https://git-scm.com/

“自由,开源,分布式的版本控制系统?”
– Pony

“快速, 高效?”
– Jack

“一千个读者心里有一千个哈姆雷特”, 1024个攻城狮就有1024个git.
关于什么是git, 当你用熟悉后心里自然会有一个答案, 别人再多的解释都不如自己的理解.

为什么要使用git?

强大的分支模型, 独特的暂存区设计, 分布式特性…

一些术语

在这里插入图片描述

  • Workspace(工作区): 我们编辑代码的地方就是工作区
  • Stage area/Staging/Index(暂存区): 工作区用来提交更改前可以暂存工作区的变化的地方。
  • Local Repository(本地版本库)): 受git控制的所有文件修订历史的本地共享数据库.
  • Remote Repository(远程版本库): 受git控制的所有文件修订历史的远程(服务端)共享数据库.
  • Branch(分支): 从主线上分离开的副本,默认分支一般叫master
  • Commit(提交): 通过git commit或者其他操作(如git merge)会生成一个commit id, 此为一个提交. 实质为将暂存区的修改同步到版本库, 根据语境可分为动词或者名词.
  • Tag(标记, 标签): 特定提交的别名(如: git tag v1.0 <commit-id>).
  • more…

下述描述中, 如无特别说明, “版本库"特指"本地版本库”.

安装

略.
个人根据自己系统环境(Linux, MacOS, Win)自行安装.

我的环境:
gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/tmp

git version
git version 2.21.0.windows.1

git子命令 (ref: https://git-scm.com/docs)

常用命令(应该熟练掌握)

配置相关

  • config
    # git账户配置
    git config --global user.email [email protected]
    git config --global user.name gl
    
    # 子命令别名配置
    git config --global alias.st status
    git config --global alias.co checkout
    git config --global alias.br branch
    git config --global alias.cm commit -m
    git config --global alias.df diff
    git config --global alias.sh stash
    git config --global alias.ci commit
    git config --global alias.lg log --graph
    git config --global alias.lgo log --graph --oneline
    git config --global alias.logf log --pretty format:"%h%x09%an%x09%ad%x09%s"
    git config --global alias.cpk cherry-pick
    

初始化相关

  • init (初始化一个版本库)
    git init myrepo # 在当前目录下创建版本库myrepo(自动创建myrepo目录)
    
  • clone (克隆版本库)
    git clone [email protected]:my-test-group/git-share.git
    

基本操作

  • add (工作区变动添加到暂存区)
  • commit (暂存区修改提交到版本库)
  • status (查看状态)
    git status
    
    # 精简输出
    git status -s
    
  • diff
    git diff # 查看工作区与暂存区的差异
    git diff --cached # 查看暂存区和版本库的差异
    git diff HEAD # 查看工作区和版本库的差异
    
  • reset(撤销)

分支与合并

  • branch(分支)
    git branch -a
    git branch
    git branch -m new-branch # 创建并切换到新分支
    
  • checkout
    git checkout some-branch # 切换到 some-branch分支
    git checkout -b new-branch # 基于当前分支 创建并切换到 new-branch分支
    
    # 用暂存区的main.c覆盖工作区的main.c
    # 注意: 工作区中的main.c会被覆盖, 比较危险, 慎用!
    git checkout -- main.c
    
    # 用暂存区覆盖整个工作区, 非常危险, 慎用!!!
    git checkout -- .
    
  • merge(合并分支)
  • stash(本地修改和暂存区修改 临时 存储起来)
    git stash list # 查看有哪些stash
    git stash save "为本次临时存储写一些说明" # 推荐此种用法
    git stash pop # 取出最近一次stash
    git stash apply stash@{1} # 将倒数第二次stash应用到工作区.(stash从0开始编写)
    git stash drop stash@{1} # 删除倒数第二次stash, 慎用
    
    git stash clear # 删除所有stash, 慎用
    
  • tag(打标签)

共享与更新项目

  • fetch(下载但不合并)

    # 下载所有分支
    gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/project/git-share (feature-share)
    $ git fetch  -v
    From my.gitlab.com:my-test-group/git-share
     = [up to date]      feature-share -> origin/feature-share
     = [up to date]      master        -> origin/master
    
     # 下载指定分支
     gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/project/git-share (feature-share)
    $ git fetch -v origin master
    From my.gitlab.com:my-test-group/git-share
     * branch            master     -> FETCH_HEAD
     = [up to date]      master     -> origin/master
    
  • remote (远程仓库管理)

    # 为远程版本库[email protected]:my-test-group/git-share.git起个别名(绑定)为origin
    git remote add origin [email protected]:my-test-group/git-share.git
    
    # 查看有哪些远程版本库别名
    $ git remote
    origin
    
    # 上述一条的详细输出
    git remote -v
    origin  [email protected]:my-test-group/git-share.git (fetch)
    origin  [email protected]:my-test-group/git-share.git (push)
    
  • pull(下载并合并)

    # 下载并合并master分支
    git pull -v origin master
    
    # 下载远程分支dev到本地, 并重新命名为dev-local
    # dev-local仅为说明参数顺序, 实际应保持远程和本地分支名称一致.
    git pull -v origin dev:dev-local
    
  • push(推送)

    # 推送本地feature-xxx分支到远程, 并在远程创建同名分支, 并作关联(-u)
    git push -u origin feature-xxx:feature-xxx
    
    # 强制推送, 慎用
    git push -f
    

检查与对比

  • log
    git log
    
    git log --oneline # 简短输出
    git log --oneline --graph # 带有"分支树"格式的输出
    
    git log -2 # 查看最近两次提交日志
    
  • diff
  • show (查看某次提交具体的修改)

打补丁

  • rebase
  • cherry-pick(选择合并)

其他

  • reflog (查看本地HEAD指针变更记录)
  • blame (查看文件有哪些成员修改过, 如果你用过svn blame, 你应该很熟悉)
  • flow (开发工作流的封装命令, 具体用法请自行学习, 本文主要讲思路, 实现都是大同小异)

开发工作流

ref: https://nvie.com/posts/a-successful-git-branching-model/
git-branch-model
上面的文章和图建议仔细阅读, 会有不少收获

开发工作流总述:

环境

git工作流是基于git的分支功能而来. 也和开发环境有关系.
一般而言: 会有4个代码环境(根据实际情况, 可能会更多).

  1. 开发环境, 大家可以随时在这里调试(比如改了一行代码, 想看看效果)
  2. 公共测试环境(可能是测试人员用, 没有测试人员的, 就是开发人员公用)
  3. 预发布环境, 数据和代码与生产的几乎一样.
    为什么说几乎呢?
    因为数据可能是每天从生产环境同步一次, 在有新版本要发布时, 会先在预发布环境测试(此时功能已经确定, 可能会有小bug, api鲁棒性问题等).
  4. 正式环境/生产环境

分支名称及生命周期

注意看上图:
共有5个(确切来说, 有2个长驻(长期存在)的分支: devmaster, 还有三类分支: feature, release, hotfix.

实际使用时, 可以根据需要增加一些其他分支, 如bugfix分支,
是一些测试环境的bug修改, 由于也不是新特性, 所以叫
feature也不太好, 如"bugfix-get_user_list_no_response"

可以简单地这样认为:

  • feature - 开发环境, 代表了一些新功能特性的开发
  • dev - 公共测试环境, 代表了功能基本确定, 由feature合并进来
  • release - 预发布环境, 代表了某个版本功能完全确定,只有一些小bug的情况
  • master - 生产环境, 代表了代码经过测试后, 无bug的情况, 发布到生产环境.

如果环境没有那么多, 比如没有预发布环境, 需要根据情况取舍(比如预发布和公共测试环境为一套环境)

分支生成与删除

其中:

  • feature/bug分支是基于dev分支产生,
  • release分支基于dev分支产生,
  • hotfix分支基于master分支产生.
  • feature/bug开发完成后, 要合并到dev分支, 根据需要可即时删除或者后续删除.
  • release测试通过后, 要合并到devmaster, 根据需要可即时删除或者后续删除.
  • hotfix代表了正式环境的(紧急)bug, 测试通过后, 要合并到devmaster, 根据需要可即时删除或者后续删除.

开发工作流示例

准备: 以gitlab为例子, 建立群组(group)my-group, 然后再创建项目demo, 再基于分支master(gitlab自动创建的)建立分支dev. master为默认分支.
此时大概是这样的:

在这里插入图片描述
为了便于查看, 一些命令的输出会精简或者删除, 请注意!

clone

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace
$ cd /d/workspace/

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace
$ git clone [email protected]:my-group/demo.git

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace
$ cd demo/

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

分支准备

上面我们在 /d/worksapce 目录下, clone了demo项目. git status查看了下状态.
假设现在我们接到一个需求, 功能有"添加到购物车"和"支付", 版本定为1.0.
基于上述的工作流策略及分支命名规则,
我们可以这样:

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
# 查看所有分支, 发现远端有个dev没clone到本地
$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/dev
  remotes/origin/master

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
# 远端dev分支clone到本地, 并在本地建立同名分支
$ git pull origin dev:dev
From my.gitlab.com:my-group/demo
 * [new branch]      dev        -> dev
Already up to date.

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
# 基于dev开一个分支, 代表"添加到购物车"这个功能在这里开发
$ git checkout -b feature-shopping-car dev

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-shopping-car)
# 基于dev开一个分支, 代表"支付"这个功能在这里开发
$ git checkout -b feature-pay dev

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-pay)
# 切换分支到 feature-shopping-car, 准备开发 "添加到购物车"这个功能
$ git checkout feature-shopping-car

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-shopping-car)
# 查看一下现有分支, 我们正处于feature-shopping-car这个分支上
$ git br -a
  dev
  feature-pay
* feature-shopping-car
  master
  remotes/origin/HEAD -> origin/master
  remotes/origin/dev
  remotes/origin/master

开发"添加到购物车"这个功能

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-shopping-car)
# 模拟完成需求
$ echo "添加商品1到购物车" >> shopping-car.txt

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-shopping-car)
$ echo "添加商品N到购物车" >> shopping-car.txt

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-shopping-car)
$ echo "添加完成!" >> shopping-car.txt

# 查看当前状态, st为status的别名, 注意参看本文开始的配置, 下此类情况不再赘述
gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-shopping-car)
$ git st

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-shopping-car)
# 工作区变更添加到暂存区
$ git add shopping-car.txt

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-shopping-car)
# 提交到版本库
$ git cm "添加到购物车功能完成"

开发"支付"这个功能

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-shopping-car)
# 切换到feature-pay分支准备开发"支付功能"
$ git co feature-pay

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-pay)
# 模拟开发支付功能(计算有错误为故意设置, 后面会用到)
$ cat >pay.txt <<EOF
> 计算总价格: 10+20=40
> 计算完成
> 支付
> 支付完成
> EOF

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-pay)
# 添加到暂存区
$ git add pay.txt

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-pay)
# 支付功能开发完成, 提交到版本库
$ git cm "支付功能完成"

插曲

上面我们有个需求, 版本定为1.0, 我们根据需求开了两个分支:
“feature-shopping-cart"和"feature-pay”, 实际可以根据需要起名为: “feature-1.0-shopping-car”,
因为可能两个并行的版本有同一个功能都在开发, 以示区分.

feature分支我们没有演示推送(push)操作, 这个看情况, 为了避免"磁盘损坏"导致代码丢失的情况发生,
建议feature分支也推送, 用完删除即可.

进入预发布阶段

上面我们已经开发完了所有功能, 在测试没啥问题后, 就可以进入预发布环境了:

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (feature-pay)
# 进入dev分支准备合并
$ git co dev

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (dev)
# 合并"支付"功能到dev分支, 注意--no-ff会保留分支信息, 下同
$ git merge --no-ff feature-pay

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (dev)
# 合并"添加到购物车"功能到dev分支
$ git merge --no-ff feature-shopping-car

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (dev)
# 查看dev的记录, 可以看到分支合并的信息(分叉), git log --oneline --graph
$ git lgo
*   8efcbc0 (HEAD -> dev) Merge branch 'feature-shopping-car' into dev
|\
| * cd6dde0 (feature-shopping-car) 添加到购物车功能完成
* |   0cde2ea Merge branch 'feature-pay' into dev
|\ \
| |/
|/|
| * 2da3542 (feature-pay) 支付功能完成
|/
* 2471ab4 (origin/master, origin/dev, origin/HEAD, master) Initial commit

# 推送dev到远程
$ git push origin dev

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (dev)
# feature合并完了, 再基于dev开一个release分支出来
# 表示此次需求我们功能已经开发完成, 测试也没啥问题
# 进入预发布阶段
$ git co -b release-shoppin$ git co -b release-shopping

# 合并完成后, 可以删除已经合并过的feature相关分支了
gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (release-shopping)
# 删除feature分支
$ git br -d feature-shopping-car

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (release-shopping)
# 删除feature分支
$ git br -d feature-pay

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (release-shopping)
# 看看release分支的文件, 是我们期待的
$ ls
pay.txt  README.md  shopping-car.txt

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (release-shopping)
# 推送release分支, -u表示关联上游分支
$ git push -u origin release-shopping:release-shopping
......
 * [new branch]      release-shopping -> release-shopping
Branch 'release-shopping' set up to track remote branch 'release-shopping' from 'origin'.

插曲

"feature-*"分支很多时候由开发人员自己控制, 上面根据需求开了两个, 其实可以开更多, 比如
“购物车过期商品过滤”, "支付校验"等.
当众多分支合并到dev时, 导致日志输出可能非常混乱, 难以查看.

建议:
可以自己将feature的提交整理后再合并到dev分支, 可以参考以下几点:

  • 无意义或者意义不明确的多个可以合并为一个
    如两次提交: “添加一个按钮”, “为按钮增加点击事件”, 就可以合并为一个提交: “添加按钮, 点击执行xxx”
  • 如果功能分得太细, 导致分支很多, 可以另建立分支, 将分支合并后, 精简提交, 再合并到dev
  • feature也不能起得太"大", 比如此次需求起为"feature-shopping", 所有功能放到一个分支里,
    可能后续需求有变, 要减少功能时, 这时再想撤回就很麻烦了, 要人工摘出来代码.
  • 命名方面: feature-shopping-cart, feature-shopping_cart, feature/shopping-cart等都可以, 团队统一规范即可.
    一般建议全小写, 用"-"连接.

预发布环境再测试

预发布环境只是与正式环境尽可能一致, 当然不可能完全一致, 所以在这里可以再做一些测试.
测试过程中我们发现, 支付时, 金额计算的有误(看上面的支付修改的"代码"):

计算总价格: 10+20=40

在release分支上发现bug是正常的, 因为还没有上线(发到正式)嘛, 我们改下, 再提交

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (release-shopping)
# 为了演示, 用sed而不用vim改对
$ sed -i 's/10+20=40/10+20=30/g' pay.txt

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (release-shopping)
# 确认下, 我们改对了
$ cat pay.txt
计算总价格: 10+20=30
计算完成
支付
支付完成

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (release-shopping)
# 查看下状态, 工作区有发动
$ git st

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (release-shopping)
# 添加到暂存区
$ git add pay.txt

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (release-shopping)
# 提交到版本库
$ git cm "支付计算错误修改"

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (release-shopping)
# 推送到远程版本库, 注意这里没有写成 git push origin release-shopping,
* 是由于我们之前推送到release分支时,使用了 -u 选项, 已经和远程的release-shopping作了关联
$ git push

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (release-shopping)
# 查看下release分支现有的提交记录
$ git lgo
* 5d477fd (HEAD -> release-shopping, origin/release-shopping) 支付计算错误修改
*   8efcbc0 (dev) Merge branch 'feature-shopping-car' into dev
|\
| * cd6dde0 添加到购物车功能完成
* |   0cde2ea Merge branch 'feature-pay' into dev
|\ \
| |/
|/|
| * 2da3542 支付功能完成
|/
* 2471ab4 (origin/master, origin/dev, origin/HEAD, master) Initial commit

正式发布

经过测试, release分支没啥问题了, 我们来发布.
一般来说, 这一步需要管理员来处理, 毕竟是对master分支有改动的操作.
这里只是为了演示流程:

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (release-shopping)
# 切换到master
$ git co master

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
# 将release合并到master, 注意--squash选项是合并提交
# 这样merge后, 需要手动commit一下
$ git merge --squash release-shopping

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
# 查看master的日志还是老样子
$ git lgo
* 2471ab4 (HEAD -> master, origin/master, origin/dev, origin/HEAD) Initial commit

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
# 查看master的状态, 发现暂存区有变化
$ git st
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   pay.txt
        new file:   shopping-car.txt


gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
# 我们手动提交下
$ git cm "v1.1: 添加到购物车, 支付功能"

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
# 再查看日志
$ git lgo
* 142bb0b (HEAD -> master) v1.1: 添加到购物车, 支付功能
* 2471ab4 (origin/master, origin/dev, origin/HEAD) Initial commit

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
# master尽量做到一次提交(合并)打一个tag, 以后好回退
$ git tag v1.1 -m "添加到购物车, 支付功能"

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
# 查看tag
$ git tag -l -n
v1.1            添加到购物车, 支付功能

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
# 删除已经合并了的release分支
$ git br -d release-shopping

# 同时删除远程的release分支
$ git push --delete origin release-shopping
To my.gitlab.com:my-group/demo.git
 - [deleted]         release-shopping

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
# master推送到远程
$ git push origin master

gl@DESKTOP-U75AMEJ MINGW64 /d/workspace/demo (master)
# tag也推送上去
$ git push origin v1.1

v1.1开发完成

经过上述的操作, 我们已经看到了一个版本是如何用git工作流来完成的.
最后的结果展示:
在这里插入图片描述

dev分支提交图示

在这里插入图片描述

master分支提交图示

在这里插入图片描述

开发工作流注意事项

  • 非长期分支, 分支名称和说明以 “-” 来分隔,
    feature-add_button, release-v1.2_add_privilege, hotfix-1.2.1))
  • master仅允许管理员或授权人员推送, 合并等, 普通成员只有读权限.
  • ** 合并到master时, 最好git rebase后合并, 或者合并时用git merge --squash, 来保证提交日志简洁.
  • ** 合并到dev时, 如将feature-add_button合并到dev,
    先将 feature-add_button的提交精简(无意义的提交合并等)一些,
    然后在devgit merge --no-ff来合并, 保证dev上保留完整的历史.
  • feature根据需要可以开多个, 如果某次开发有多个功能,
    可按功能开多个feature分支, 避免只开一个分支, 合并时有的功能又不要了, 此时再"拆代码"就麻烦了

一些git服务端

一些git客户端

  • TortoiseGit, 界面和TortoiseSVN很像, 新手不建议使用,
    在熟悉命令之后再使用, 查看日志比较方便, 其他诸多特性请自行探索.
  • GitHub Desktop, Github官方出的一款git客户端, 新手不建议使用,
    界面比较简洁, 查看历史变更比较方便, 其他诸多特性请自行探索.
  • Sourcetree, 据说是最好用的git客户端, 本人没有试过.
  • more…

git学习资源

  • git help
  • https://git-scm.com/docs
  • https://www.liaoxuefeng.com/wiki/896043488029600
  • https://git-scm.com/book/zh/v1/Git-%E5%9F%BA%E7%A1%80
  • more…

参考

  • https://git-scm.com/docs
  • Git权威指南第二版.pdf

说明

图片来自互联网, 如有侵权, 请联系博主删除.

发布了231 篇原创文章 · 获赞 77 · 访问量 52万+

猜你喜欢

转载自blog.csdn.net/butterfly5211314/article/details/102675882