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

180 阅读6分钟

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

一、git是什么

关键词:分布式

版本控制的发展历史

本地控制:RCS
集中控制:SVN,只在云端保存,大型团队不适用,无法进行分支操作,更好的支持大文件和二进制文件,部分游戏团队会用
分布控制:git,每个仓库都能记录版本历史,每次提交记录的都是完整的文件快照而不是记录增量,支持分支管理,校验和机制保证完整性,一般只添加数据很少删除,大文件不友好(git-lfs可以弥补)

git的发展历史

github:大部分开源项目都在此平台上
gitlab:便于管理,可定制化
gerrit:Google开发的,便于Android开发

二、git基本使用方式

初始化

git init
--initial-branch 初始化分支
--bare 创建一个裸仓库(没有工作目录)
--template 可以通过模板来创建预先构建好的自定义git目录
tree .git查看文件树
本地分支默认叫master,但可以更改 git branch -m <名字>

工作区&暂存区

stage fixes: working directory->staging area
commit: staging area->.git dirctory(repository)
checkout the project: .git dirctory(repository)->working directory

配置

git config

--local: 存在本地.git/config目录
--global:存在当前用户的gitconfig目录
--system:存在/etc/gitconfig目录
用户名配置
--global user.name "wxc"
--global user.email 15299071432@163.com
可以在ssh http等协议之间互相转换
--global url.git@github.com:.insteadOf github.com/
可以替换命令名,把commit...换成cin
--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 github.com/git/git.git
如果打错了 就git remote remove xxx
同一个origin设置不同的push和fetch URL:
git remote add origin git@github.com:git/git.git
git remote set-url --add --push origin git@github.com:my_repo/git.git

免密配置

http remote

内存:git config --global credential.helper 'cache --timeout=3600'
硬盘:git config --global credential.helper "store --file /path/to/credential-file"(不指定目录的话就是~/.git-credentials)
将密钥信息存在指定文件中:scheme://{scheme}://{user}:${password}@github.com
缺点:不安全

ssh remote

通过公私钥机制,生成公钥存放在服务端
key的类型:dsa,rsa,ecdsa,ed25519,但有些已经不支持前两种了,推荐最后一种
ssh-keygen -t ed25519 -C "15299071432@163.com"
密钥默认存在~/.ssh/id_ed25519.pub

提交代码

add

git add <文件名> 或者 git add . 提交所有
git status 查看状态
git rm --cached <文件名> 撤销上次add
git cat-file -p <object下文件夹名+文件名> 查看此加密文件内容

commit

git commit -m <要注释的内容>
可以再看到objects多出的两个文件存放commit的信息
git log 查看日志

objects

blob 存储文件的内容
tree 存储文件的目录信息
commit 存储提交信息,一个commit对应唯一版本的代码
关联:通过commit找到tree id,通过tree存储的信息获取到blob id,获取文件内容。
git fsck --lost-found 查找悬空object(被修改了commit并改变了id,但旧id还在,悬空commit;add了但撤回了,悬空blob)
git reflog expire --expire=now --all
记录操作日志,防止误操作后数据丢失。
git gc --prune=now 可以删除一些不需要的object,指定修剪多久之前的,默认两周前,执行后会将各种objects压缩起来,cat .git/packed-refs可以查看它们的id

refs

存放版本信息
分支信息内存放commit id,把ref当做指针,指向对应的commit来表示当前ref对应的版本。
refs/heads前缀表示分支,还有其他种类如refs/tags表示标签。
git checkout -b 创建新分支
git tag <版本号> 生成标签,是稳定版本,指向某一个commit,相比branch一般用于开发版本,可以不断添加commit进行迭代
git tag -a <版本号> -m "写点什么" 附注标签

历史版本

当前版本代码:通过ref指向的commit
历史版本代码:commit存有parent commit字段,通过串联获取历史版本
commit --amend 修改最近一次commit信息,修改以后id会变,只改commit文件,blob和tree不变,还能找到上一次提交id
git rebase -i HEAD~3 实现对最近三个commit的修改:合并commit、修改具体的commit message、删除某个commit
filter --branch 指定删除所有提交中的某个文件或全局修改邮箱地址

远端同步

拉取

clone 拉取仓库到本地目录,可以指定分支和深度
pull 拉取远端分支与本地合并,= fetch + merge,也可以用git pull --rebase = fetch + rebase。可能存在冲突
fetch 拉取远端分支到本地,不会merge,会修改refs/remote 内的分支信息,如果需要与本地代码合并需要手动操作

推送

git push origin master
冲突问题:
本地的commit和远端不一样,--amend或rebase都可能导致
强制推送:git push origin master -f
推送限制:保护分支
常见问题:
配置了git还是无法拉取代码:密钥配置有问题 or 配置的ssh免密协议,却用的是http
fetch了远程分支,但本地不变:只能更新origin分支,而且只能拉到本地而非合并

三、git研发流程

依托代码管理平台gitlab/github/gerrit进行代码开发

工作流

集中式:gerrit/svn,只依托于主干分支开发,fast-forward合入
分支管理:github/gitlab,多分支再PR合并,可以多方式自定义合入

gerrit

依托于change ID概念,每个提交生成一个单独的代码评审
提交上去的代码不会存储在真正的refs/heads下的分支中,而是refs.for的引用中
通过resf/meta/config下的文件存储代码的配置(权限、评审等),每个change都要完成review后才能合入
优点:
强制代码评审机制,保证代码质量;提供更丰富权限功能,细粒度权限管控;master历史整洁性;aosp多仓场景支持更好
缺点:
开发人员多的时候容易冲突;多个版本代码容易出问题;不支持fork,不方便代码复用

分支管理

git flow

分支类型丰富,规范严格
master主干、develop开发、feature特性、release发布、hotfix热修复
优点:代码结构清晰
缺点:流程过于复杂

github flow

只有主干和开发分支,简单
团队合作:owner创建好仓库,其他用户fork到自己的仓库进行开发 or 分配权限,在同一个仓库开发

gitlab flow

主干和开发上构建环境、版本分支,满足不同发布 or 环境的需求
规则:上游优先

代码合并

fast-forward:不会产生新的merge节点,合并后保持线性历史,target分支更新后必须rebase更新
source branch后才能合入
切换到主分支,git merge <需要合并过来的分支名> --ff-only
three-way:产生新的merge节点
git merge <需要合并过来的分支名> --no-ff

规则

小型团队:少量多次,PR之后保证有CR后再合入,使用fast-forward,合入前rebase