这是我参与「第三届青训营-后端场」笔记创作活动的的第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)
将密钥信息存在指定文件中:{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