Git 的正确使用姿势与最佳实践 | 青训营笔记

106 阅读7分钟

Git

  1. Git的重要性
  • 协同工作 业界绝大多数公司都是基于Git进行代码管理,因此Git是一个程序员的必备技能
  • 开源社区 目前绝大多数的开源项目都是基于Git维护的,参与这些项目的开发都需要使用Git
  1. 版本控制
  • what:是一种记录一个或若干个文件内容变化,以便将来查阅特定版本修订情况的系统
  • why:更好的关注变更;了解到每个版本的改动是什么;方便对改动的代码进行检查,预防事故发生;也能够随时切换到不同的版本,回滚误删误改的问题代码
  • 类型:
    1. 本地版本控制 - RCS
      • 最初的方式:通过本地复制文件夹,来完成版本控制,一般可以通过不同的文件名来区分版本
      • 解决方案:开发了一些本地的版本控制软件,其中最流行的是RCS
      • 基本原理:本地保存所有变更的补丁集,可以理解成就是所有的Diff,通过这些补丁,我们可以计算出每个版本的实际的文件内容
      • 缺点:只能在本地使用,无法进行团队合作,因此使用的场景非常有限,故衍生出了集中式版本控制
    2. 集中版本控制 - SVN
      • 基本原理:
        • 提供一个远端服务来保存文件,所有用户的提交都提交到该服务器中
        • 增量保存每次提交的Diff,如果提交的增量中和远端现存的文件存在冲突,则需要本地提前解决冲突
          image.png
      • 优点:
        • 学习简单,更容易操作
        • 支持二进制文件,对大文件支持更友好
      • 缺点:
        • 本地不存储版本管理的概念,所有提交都只能连上服务器后才可以提交
        • 分支上的支持不够好,对于大型项目团队合作比较困难
        • 用户本地不保存所有版本的代码,如果服务端故障容易导致历史版本的丢失
    3. 分布式版本控制 - Git
      • 基本原理:
        • 每个库都有完整的提交历史。可以直接在本地进行代码提交
        • 每次提交记录的都是完整的文件快照,而不是记录增量
        • 通过Push等操作来完成和远端代码的同步 image.png
      • 优点:
        • 分布式开发,每个库都是完整的提交历史,支持本地提交,强调个体
        • 分支管理功能强大,方便团队合作,多人协调开发
        • 校验和机制保证完整性,一般只添加数据,很少执行删除操作,不容易导致代码丢失
      • 缺点:
        • 相对于SVN更复杂,学习成本更高
        • 对于大文件的支持不是特别好(git-lfs工具可以弥补这个功能)
  1. 衍生出来的平台
  • Github:全球最大的代码托管平台,大部分的开源项目都放在这个平台上
  • Gitlab:全球最大的开源代码托管平台,项目的所有代码都是开源的,便于在自己的服务器上完成Gitlab的搭建
  • Gerrit:由Google开发的一个代码托管平台,Android这个开源项目就托管在Gerrit之上

Git的基本使用方式

  1. 工作区 & 暂存区
    工作区:如修改代码的地方
    暂存区:将代码文件add进暂存区
    git目录:将暂存区的内容通过commit进入git目录
    image.png

  2. Git Config

    1. 不同级别的Git配置
      • 级别从高到低:system - global- local
      • 每个级别的配置可能重复,但是低级别的配置会覆盖高级别的配置
    2. 常见Git配置
      • 用户名配置
        • git config --global user.name "newname"
        • git config --global user.email xxx@xx.com
      • Instead of配置
        • git config --global url.git@github.com:.insteadOf httos://github.com/
      • Git命令别名配置
        • git config --global alias.cin "commit --amend --no-edit"
  3. Git Remote - 用于在远程仓库的操作

  • 显示所有远程仓库:git remote -v
  • 添加Remote:
    • push与fetch设置相同的url:git remote add [shortname] [url]
    • push与fetch设置不同的url:git remote set-url --add --push [shortname] [url]
  • 免密设置
    1. HTTP Remote
      • 内存:git config --global credential.helper 'cache --timeout=3600'
      • 硬盘:git config --global credential.helper "store --file/path/to/credential-file" 不指定目录的情况默认是~/.git-credentials
      • 将密钥信息存在指定文件中 schme://{schme}://{password}@github.com
    2. SSH Remote
      SSH 可以通过公私钥的机制,将生成公钥存放在服务端,从而实现免密访问目前的Key的类型四种,分别是dsa、rsa、ecdsa、ed25519
  1. Git Add - 将该文件添加到暂存区
    添加一个或多个文件到暂存区:git add [file1] [file2] ...
    添加指定目录到暂存区,包括子目录:git add [dir]
    添加当前目录下的所有文件到暂存区:git add .

  2. Git Commit - 将暂存区内容添加到本地仓库中
    提交暂存区到本地仓库中:git commit -m [message] ,[message] 可以是一些备注信息
    提交暂存区的指定文件到仓库区:git commit [file1] [file2] ... -m [message]
    -a 参数设置修改文件后不需要执行 git add 命令,直接提交:git commit -a

  3. Object
    commit/tree/blob/tag在git里面统称为Object Blob:存储文件的内容
    Tree:存储文件的目录信息
    Commit:存储提交信息,一个Commit可以对应唯一版本的代码
    三者关系

    1. 通过Commit寻找到Tree信息,每个Commit都会存储对应的Tree ID
    2. 通过Tree存储的信息,获取到对应的目录树信息
    3. 从tree中获得blob的ID,通过Blob ID获取对应的文件内容 image.png
  4. Refs
    refs的内容就是对应的Commit ID,因此把ref当作指针,指向对应的Commit来表示当前Ref对应的版本
    不同种类的ref:

    • refs/heads前缀表示的是分支
    • refs/tags前缀表示的是标签
      Branch
      创建一个新分支:git checkout -b
      分支一般用于开发阶段,是可以不断添加Commit进行迭代的
      Tag
      标签一般表示的是一个稳定版本,指向的Commit一般不会变更。
      生成tag:git tag
  5. Annotation Tag - 附属标签

  • what:一种特殊的Tag,可以给Tag提供一些额外的信息
  • 创建:git tag -a
  1. 追溯历史版本
    • 获取当前版本代码:通过Ref指向的Commit可以获取唯一的代码版本
    • 获取历史版本代码:Commit里面会存有parent commit字段,通过commit的串联获取历史版本代码 image.png
  2. 修改历史版本
  • commit-amend:可以修改最近的一次commit信息,修改之后commit id会变
  • rebase:通过git rebase -i HEAD~3可以实现对最近三个commit的修改
  • filter-branch:可以指定删除所有提交中的某个文件或者全局修改邮箱地址等操作
  1. Object的变化 - 新增的Object:修改Commit后有新增Object,但之前的Object依然存在 - 悬空的Object:就是没有ref指向的object
  2. Git GC - 删除悬空的Object
  • GC
    • 通过git gc命令,可以删除一些不需要的object
    • 可以对object进行一些打包压缩来减少仓库的体积
  • Reflog
    • reflog是用于记录操作日志、防止误操作后数据丢失
    • 通过reflog来找到丢失的数据,手动将日志设置为过期(才能将object删去)
  • 指定时间
    • gitgc prune=now指定的是修建多久之前的对象,默认是两周前

image.png 13. Git Clone & Pull & Fetch

  • Clone:拉取完整的仓库到本地目录,可以指定分支,深度。
  • Fetch:将远端某些分支最新代码拉取到本地,不会执行 merge 操作, 会修改 refs/remote 内的分支信息,如果需要和本地代码合并需要手动操作。
  • Pull:拉取远端某分支,并和本地代码进行合并,操作等同于 git fetch + git merge,也可以通过 git pull --rebase 完成 git fetch + git rebase 操作。 可能存在冲突,需要解决冲突。
  1. Git Push (Push - 将本地代码同步至远端的方式)
    • 常用命令: 一般使用 git push origin master 命令即可完成
    • 冲突问题:
      1. 如果本地的commit 记录和远端的 commit 历史不一致,则会产生冲突,比如 git commit --amend or git rebase 都有可能导致这个问题。
      2. 果分支就自己一个人使用,或者团队内确认过可以修改历史则可以通过 git push origin master -f来完成强制推送,一般不推荐主干分支进行该操作,正常都应该解决冲突后再进行推送。
    • 推送规则限制: 可以通过保护分支,来配置一些保护规则,防止误操作,或者一些不合规的操作出现,导致代码丢失,