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

151 阅读10分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第17篇笔记

为什么要学习git

协同工作

业界绝大多数公司都是基于Git进行代码管理,因此 Git是一个程序员的必备技能

开源社区

目前绝大多数的开源项目都是基于Git维护的,参与这些项目的开发都需要使用Git。

常见问题

  • 入职后按照文档进行Git配置,但是配置后依然拉取代码有问题,缺少自己排查配置问题的能力
  • 研发流程中进行一些异常操作,不符合研发规范,不清楚保护分支,MR/PR等概念

git是什么

版本控制是什么

一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统

为什么需要版本控制

更好的关注变更,了解到每个版本的改动是什么,方便对改动的代码进行检查,预防事故发生;也能够随时切换到不同的版本,回滚误删误改的问题代码;

image.png

本地版本控制

最初的方式 通过本地复制文件夹,来完成版本控制,一般可以通过不同的文件名来区分版本

解决方案

开发了一些本地的版本控制软件,其中最流行的是RCS

基本原理 本地保存所有变更的补丁集,可以理解成就是所有的Diff,通过这些补丁,我们可以计算出每个版本的实际的文件内容

缺点 RCS这种本地版本控制存在最致命的缺陷就是只能在本地使用,无法进行团队协作,因此使用的场景非常有限,因此衍生出了集中式版本控制

image.png

集中版本控制

代表性工具: SVN

基本原理:

  • 提供一个远端服务来保存文件,所有用户的提交都提交到该服务器中
  • 增量保存每次提交的Diff,如果提交的增量中和远端现存的文件存在冲突,则需要本地提前解决冲突 有很多美术/游戏团队因为SVN对大文件支持更友好,仍在使用SVN

优点

  • 学习简单,更容易操作
  • 支持二进制文件,对大文件支持更友好

缺点

  • 本地不存储版本管理的概念,所有提交都只能联上服务器后才可以提交
  • 分支上的支持不够好,对于大型项目团队合作比较困难
  • 用户本地不保存所有版本的代码,如果服务端故障容易导致历史版本的丢失

image.png

分布式版本控制

代表性工具: Git

基本原理

  • 每个库都存有完整的提交历史,可以直接在本地进行代码提交
  • 每次提交记录的都是完整的文件快照,而不是记录增量
  • 通过Push等操作来完成和远端代码的同步

优点

  • 分布式开发,每个库都是完整的提交历史,支持本地提交,强调个体
  • 分支管理功能强大,方便团队合作,多人协同开发
  • 校验和机制保证完整性,一般只添加数据,很少执行删除操作,不容易导致代码丢失

缺点

  • 相对SVN 更复杂,学习成本更高
  • 对于大文件的支持不是特别好(git-lfs 工具可以弥补这个功能)

不推荐在git提交大文件

image.png

Git发展历史

作者 Linus Torvalds(就是Linux这个项目的作者,同时也是Git的作者)。 开发原因 怀疑Linux团队对BitKeeper (另一种分布式版本控制系统,专有软件)进行了逆向工程,BitKeeper不允许Linux团队继续无偿使用。因此决定自己开发一个分布式版本控制系统。

开发时间 大概花了两周时间,就完成了Git的代码第一个版本,后续 Linux项目就开始使用Git进行维护。


Github 全球最大的代码托管平台,大部分的开源项目都放在这个平台上。

全球最大的同性交友网站 /doge

Gitlab 全球最大的开源代码托管平台,项目的所有代码都是开源的,便于在自己的服务器上完成 Gitlab 的搭建。

Gerrit

由Google 开发的一个代码托管平台,Android这个开源项目就托管在 Gerrit之上。

Git的基本使用方式

image.png

Git目录介绍

项目初始化

image.png

其他参数

--initial-branch 初始化的分支
--bare 创建一个裸仓库(纯Git目录,没有工作目录)
--template 可以通过模版来创建预先构建好的自定义git目录

Git仓库

windows 我打查看目录结构命令没有反应

image.png

image.png

工作区&暂存区

image.png

HEAD表示当前指向的分支

image.png

### config是当前仓库的配置
### hooks配置hook
### onjects存储的一些文件信息
### refs存储分支信息

Git Config

每个级别的配置可能重复,但是低级别的配置会覆盖高级别的配置

image.png

常见Git配置

用户名配置

git config --global user.name
git config --global user.email

Instead of配置

git config --global url.git@github.com:.insteadOf https://github.com/

Git命令别名配置

git config --global alias.cin "commit --amend --no edit"

Git Remote

查看Remote

git remote -v

添加Remote

git remote add origin_ssh git@github.com:git/git.git  

git remote add origin_http https://github.com/git/git.git

image.png 添加完Remote再次查看

image.png 查看一下config文件内容

image.png

git remote -h
帮助命令

image.png

如何设置不同的push和fetch

image.png

image.png 再次查看config文件

image.png

HTTP Remote

免密配置

内存: git config --global credential.helper 'cache --timeout=3600'
硬盘: git config --global credential.helper "store --file /path/to/credential-file"  

不指定目录的情况默认是~/.git-credentials

将密钥信息存在指定文件中
具体格式:${scheme}://${user}:${password}@github.com

SSH Remote

免密配置

  • SSH可以通过公私钥的机制,将生成公钥存放在服务端,从而实现免密访问
  • 目前的Key的类型四种,分别是dsa、rsa、ecdsa、ed25519 默认使用的是rsa,由于一些安全问题,现在已经不推荐使用dsa和rsa 了,优先推荐使用ed25519
ssh-keygen -t ed25519 -C" your_email@example.com"   
密钥默认存在 ~/.ssh/id_ed25519.pub

image.png windows存在

image.png

(多对公私钥参考这篇文章)

Git Add

创建readme.md目录没有任何变化

image.png

image.png

image.png git add .时报错,百度后发现windows直接鼠标创建吧

image.png

image.png 再次git add .并查看状态

image.png


查看文件内容,我在windows下查看不了那个目录结构

image.png

image.png 但是查看文件失败。。。

总之就是查看的objects下的文件的内容是add 的内容

发现连之前的目录的名字也加上就成功查看了

image.png

image.png

Git Commit

image.png object目录

image.png

image.png 查看下文件内容

目录树信息

image.png 存储目录树及作用及信息

image.png

image.png

Object

commit / tree / blob 在 git里面都统一称为Object,除此之外还有个tag的 object.

Blob 存储文件的内容 Tree 存储文件的目录信息 Commit 存储提交信息,一个Commit可以对应唯一版本的代码

如何串联在一起

  1. 通过Commit寻找到Tree信息,每个Commit都会存储对应的TreeID

image.png 2. 通过Tree 存储的信息,获取到对应的目录树信息。

image.png 3. 从tree中获得blob的ID,通过 Blob ID 获取对应的文件内容。

image.png

image.png

Refs

image.png 新建分支

git checkout -b test

Refs 文件存储的内容

image.png

refs的内容就是对应的Commit ID

因此把ref当做指针,指向对应的 Commit来表示当前Ref对应的版本。

不同种类的ref

refs/heads前缀表示的是分支,除此之外还有其他种类的 ref,比如 refs/tags 前缀表示的是标签。

Branch

git checkout -b 可以创建一个新分支
分支一般用于开发阶段,是可以不断添加Commit进行迭代的

Tag

标签一般表示的是一个稳定版本,指向的 Commit一般不会变更

Annotation Tag

附注标签:一种特殊的Tag,可以给Tag 提供一些额外的信息。

git tag -a 创建

image.png 查看内容

image.png

image.png

image.png

追溯历史版本

  • 获取当前版本代码

通过 Ref 指向的Commit可以获取唯一的代码版本。

  • 获取历史版本代码

Commit里面会存有parent commit字段,通过commit 的串联获取历史版本代码。

  1. 修改文件,并提交,创建新的commit

image.png 2. 查看最新的commit,新增了parent信息

image.png

image.png

image.png

  1. 新增tree object 3a

  2. 新增blob object 55

  3. 新增commit object 64

test ref 指向新的commit

image.png

修改历史版本

commit - amend 通过这个命令可以修改最近的一次 commit 信息,修改之后commit id会变 rebase 通过git rebase -i HEAD~3可以实现对最近三个 commit 的修改: 1.合并commit 2.修改具体的commit message 3.删除某个commit

filter - branch

该命令可以指定删除所有提交中的某个文件或者全局修改邮箱地址等操作

通过git commit --amend命令,尝试修改一下commit message.

image.png 通过git log查看最新的commit已经从 63ae840dfe33df2975f9653903cb533f2912bc1c

变成

7fb4383e68df781422df87876c30af884aeae86e

Object

  • 新增的Object

修改Commit后我们可以发现git object 又出现了变化

  • 新增commit object 7f

  • 但是之前的commit object 63并没有被删除

  • 悬空的Object 顾名思义就是没有ref 指向的object

image.png

image.png

Git GC

GC

通过git gc命令,可以删除一些不需要的object ,以及会对object 进行一些打包压缩来减少仓库的体积。

Reflog

reflog是用于记录操作日志,防止误操作后数据丢失,通过reflog来找到丢失的数据,手动将日志设置为过期。

指定时间

git gc prune=now指定的是修剪多久之前的对象,默认是两周前

image.png

Git 视图

image.png

image.png

Git Clone & Pull & Fetch

Clone

拉取完整的仓库到本地目录,可以指定分支,深度。

Fetch

将远端某些分支最新代码拉取到本地,不会执行merge操作,会修改refs/remote内的分支信息,如果需要和本地代码合并需要手动操作。

Pull

拉取远端某分支,并和本地代码进行合并,操作等同于git fetch + git merge,也可以通过git pull --rebase完成 git fetch + git rebase 操作。

可能存在冲突,需要解决冲突。

Git Push

常用命令

  • —般使用git push origin master命令即可完成 冲突问题

1.如果本地的commit记录和远端的commit 历史不一致,则会产生冲突,比如git commit--amend or git rebase都有可能导致这个问题。 2. 如果该分支就自己一个人使用,或者团队内确认过可以修改历史则可以通过git push origin master -f来完成强制推送,一般不推荐主干分支进行该操作,正常都应该解决冲突后再进行推送。

推送规则限制

  • 可以通过保护分支,来配置一些保护规则,防止误操作,或者一些不合规的操作出现,导致代码丢失。