git
方向介绍
Git是什么
Git是目前世界上最先进的分布式版本控制系统
版本控制
-
版本控制
一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统
-
版本控制的重要性
更好的关注变更,了解到每个版本的改动是什么,方便对改动的代码进行检查,预防事故发生;也能随时切换到不同的版本,回滚误删误改的问题代码。
本地版本控制
最初的方式:
通过本地复制文件夹,来完成版本控制,一般可以通过不同的文件名来区分版本
解决方案:
开发了一些本地的版本控制软件,其中最流行的是RCS
基本原理:
本地保存所有变更的补丁集,可以理解成就是所有的Diff,通过这些补丁,我们可以计算出每个版本的实际的文件内容。
缺点:
RCS这种本地版本控制存在最致命的缺陷就是只能在本地使用,无法进行团队合作,因此使用的场景非常有限,因此衍生出了集中式版本控制。
集中式版本控制
代表性工具:SVN
基本原理:
- 提供一个远端服务来保存文件,所有用户的提交都提交到该服务器中。
- 增量保存每次提交的Diff,如果提交的增量中和远端现存的文件存在冲突,则需要本地提前解决冲突
优点:
- 学习简单,更容易操作
- 支持二进制文件,对大文件支持更友好。
缺点:
- 本地不存储版本管理的概念,所有提交都只能联上服务器后才可以提交。
- 分支上的支持不够好,对于大型项目团队合作比较困难。
- 用户本地不保存所有版本的代码,如果服务端故障比较容易导致历史版本的丢失。
分布式版本控制
代表性工具:Git
基本原理:
- 每个库都存有完整的提交历史,可以直接在本地进行代码提交
- 每次提交记录的都是完整的文件快照,而不是记录增量。
- 通过Push等操作来完成和远端代码的同步。
优点:
- 分布式开发,每个库都是完整的提交历史,支持本地提交,强调个体
- 分支管理功能强大,方便团队合作,多人协同开发
- 校验和机制保证完整性,一般只添加数据,很少执行删除操作,不容易导致代码丢失。
缺点:
- 相对SVN更复杂,学习成本更高。
- 对于大文件的支持不是特别好(git-Ifs工具可以弥补这个功能)
graph LR;
git-->github
git-->gitlab
git-->gerrit
Git基本使用方式
graph LR;
a(git命令)-->b(配置)-->e(git config)
b-->f(git remote)
a-->c(提交代码)-->g(git add)
c-->h(git commit)
a-->d(远端同步)-->i(拉取代码)-->clone
i-->pull
i-->fetch
d-->j(推送代码)-->push
常见问题:
-
配置了Git配置,但是仍然没有办法拉取代码?
没有配置密钥(配置权限)
-
fetch了远端分支,但是本地分支历史还是没有变化?
Git目录介绍:
项目初始化:
mkdir study
cd study
git init
其他参数:
--init-branch 初始化的分支
--bare 创建一个裸仓库(纯Git目录,没有工作目录)
--template 可以通过模板来创建预先
tree .git
objects: 文件信息
refs:分支信息
Git config配置
- global ~/.gitconfig
- system $(prefix)/etc/gitconfig
- local .git/config
每个级别的配置可能会重复,但是低级别的配置会覆盖高级别的配置
常见的git配置
- 用户名配置
- Instead of 配置
- Git 命令别名配置
Git Fetch
在Git中,git fetch
命令用于从远程仓库获取最新的代码更新,但不会自动合并到当前分支。它主要用于检查远程仓库是否有新的提交或分支,并将这些信息更新到本地仓库中。
当你运行git fetch
命令时,Git会执行以下操作:
- 从远程仓库下载最新的提交、分支和标签信息。
- 更新本地仓库中与远程仓库对应的分支(如
origin/master
)的指针位置,以反映远程仓库的最新状态。 - 在本地仓库中创建或更新远程仓库的分支(如
origin/master
)的引用(指针)。
通过执行git fetch
,你可以及时了解远程仓库的变化,并决定是否将这些变化合并到你的本地分支中。一旦你运行git fetch
,你可以使用其他命令(如git merge
或git rebase
)将远程仓库的更新合并到你的工作分支中。
总结起来,git fetch
是用于更新本地仓库的远程分支和相关引用,以反映远程仓库的最新状态,但不会自动合并到当前分支。它是保持你的本地仓库和远程仓库同步的重要步骤。
Git Remote
- 查看Remote
git remote -v
- 添加Remote
git remote add origin github.com/xxxx
同一个origin设置不同的Push和Fetch URL
git remote set-url --push origin <push URL>
git remote set-url --add --push origin <push URL>
git remote set-url --add --push origin <push URL>
执行完上述命令后,你可以同时推送到三个仓库。通过这些命令,你已经为远程仓库origin
设置了三个推送URL,因此可以同时将更改推送到这三个仓库。
git push --all origin
git push --all upstream
git push --all another-remote
第一条命令将推送所有分支到origin
仓库,第二条命令将推送所有分支到upstream
仓库,第三条命令将推送所有分支到another-remote
仓库。
请注意,在同时推送到多个仓库时,你需要确保这些仓库的状态和分支结构保持一致。推荐在使用这种方式前先仔细检查和确认每个仓库的状态,并确保你的推送操作不会引起不一致的问题。
在Git中,使用git remote set-url --add --fetch <remote> <fetch URL>
命令可以同时设置多个远程仓库的拉取(fetch)URL。
当你同时设置多个fetch URL时,Git会为每个URL创建一个远程仓库别名,并将它们添加到同一个远程仓库(如origin
)中。这样,你可以通过这个远程仓库别名从多个源获取代码更新。
例如,假设你有两个远程仓库,一个名为origin
,另一个名为upstream
。你可以使用以下命令同时为它们设置fetch URL:
git remote set-url --add --fetch origin <fetch URL for origin>
git remote set-url --add --fetch upstream <fetch URL for upstream>
上述命令将为origin
和upstream
创建两个远程仓库别名,并将它们添加到同一个远程仓库(origin
)中。
设置多个fetch URL的好处是,你可以使用一个命令(如git fetch
)从多个源拉取代码更新。例如,执行以下命令将从两个远程仓库获取代码更新:
git fetch origin
git fetch upstream
这样,你可以方便地从多个源获取最新的代码,并将其合并到本地分支中。
HTTP Remote
SSH Remote
Git Add
git cat-file -p xxxxxxx
可以查看文件内容
Git Commit
git log 可以看到操作记录
Objects
commit / tree / blob 在git里面都统一称为Object,除此之外还有个tag的Object
Blob:存储文件的内容
Tree:存储文件的目录信息
Commit:存储提交信息,一个commit可以对应唯一版本的代码
Refs
Annotation Tag
一种特殊的tag,可以给tag提供一些额外的信息
通过git tag -a 命令来完成附注标签的创建
追溯历史版本
-
获取当前版本代码
通过Ref指向的Commit可以获取唯一的代码版本
-
获取历史版本代码
Commit里面会存有parent commit字段,通过commit的串联获取历史版本代码
-
修改文件,并提交,创建新的commit
-
查看最新的commit,新增了parent信息
-
修改历史版本
-
commit --amend
通过这个命令可以修改最近的一次commit信息,修改之后commit id会变
-
rebase
通过 git rebase -i HEAD~3 可以实现对最近三个commit的修改:
- 合并commit
- 修改具体的commit message
- 删除某个commit
-
filter --branch
该命令可以指定删除所有提交中的某个文件或者全局修改邮箱地址等操作。
Git GC
GC
通过git gc命令,可以删除一些不需要的object,以及会对object进行一些打包压缩来缩小减少仓库的体积
Reflog
reflog是用于记录操作日志,防止误操作后数据丢失,通过reflog来找到丢失的数据,手动将日志设置为过期
指定时间
git gc prune=now 指定的是修剪多久之前的对象,默认是两周前。
完整的git视图
Git Clone & Pull & Fetch
Git Push
Push是将本地代码同步至远端的方式
Git rebase 和 Git merge的区别
git rebase
和git merge
是Git中常用的两种合并代码的方式,它们有一些区别:
- 提交历史展示方式:
git merge
会在合并时创建一个新的合并提交,将两个分支的提交历史合并在一起,形成一个分叉的提交历史。而git rebase
会将当前分支的提交逐个应用到目标分支上,重新生成一系列新的提交,使得提交历史是线性的。 - 合并冲突处理方式: 在合并过程中,如果发生冲突,
git merge
会在合并提交中保留冲突的部分,并生成一个新的合并提交。而git rebase
会在应用每个提交时检查冲突,并在每个提交应用前让你解决冲突,然后手动完成提交。 - 提交历史清晰度: 由于
git rebase
的提交历史是线性的,它可以产生一个更加整洁、简单的提交历史,使得提交记录更易于阅读和理解。相比之下,git merge
会创建合并提交,导致提交历史中出现分叉,可能会显得更加复杂。 - 合并远程分支的方式: 当合并远程分支时,
git merge
会将远程分支的最新提交合并到本地分支上,生成一个新的合并提交。而git rebase
可以将本地分支的提交应用到远程分支上,以使得本地提交基于远程分支的最新提交。
综上所述,git rebase
和git merge
在合并代码时有不同的工作方式和影响结果。选择使用哪种方式取决于个人的需求和项目的特定情况。如果你更关注提交历史的整洁性和线性性,或者想将本地分支的提交基于远程分支的最新提交,那么可以选择使用git rebase
。如果你希望保留合并的记录和分叉的提交历史,或者合并过程中可能存在多个冲突需要处理,那么可以选择使用git merge
。
Git研发流程
常见问题:
-
在Gerrit平台上使用Merge的方式合并代码
Gerrit是集中式工作流,不推荐使用merge方式合入代码,应该是在主干分支开发后,直接push
-
不了解保护分支,Code Review,CI等概念,研发流程不规范
保护分支:防止用户直接向主干分支提交代码,必须通过PR来进行合入
Code Review,CI:都是在合入前的检查策略,Code Review是人工进行检查,CI则是通过一些定制化的脚本来进行一些校验。
-
代码历史混乱,代码合并方式不清晰
不理解Fast Forward和Three Way Merge的区别,本地代码更新频繁的使用Three Way方式,导致生成过多的Merge节点,使提交历史变得复杂不清晰。
集中式工作流
只依托于master分支进行研发活动
工作方式
- 获取远端master代码
- 直接在master分支完成修改
- 提交前拉取最新的master分支代码和本地代码进行合并(使用rebase),如果有冲突需要解决冲突
- 提交本地代码到master
Gerrit
优点:
- 提供强制的代码评审机制,保证代码的质量
- 提供更丰富的权限功能,可以针对分支做细粒度的权限管控
- 保证master的历史整洁性
- Aosp多仓的场景支持更好
缺点:
- 开发人员较多的情况下,更容易出现冲突
- 对于多分支的支持较差,想要区分多个版本的线上代码时,更容易出现问题
- 一般只有管理员才能创建仓库,比较难以在项目之间形成代码复用,比如类似的fork操作就不支持。
分支管理工作流
Git Flow
包含五种分支:
master、develop、feature、release、hotfix
优点:
如果能按照定义的标准严格执行,代码会很清晰,并且很难出现混乱
缺点:
流程过于复杂,上线的节奏会比较慢。由于太复杂,研发容易不按照标准执行,从而导致代码出现混乱。
Github Flow
Gitlab Flow
在GitFlow和Github Flow上做出优化,既保持了单一主分支的简便,又可以适应不同的开发环境。
代码合并
fast-forward
不会产生一个merge节点,合并后保持一个线性历史,如果target分支有了更新,则需要通过rebase操作更新source branch后才可以合入。
Three-Way-Merge
三方合并,会产生一个新的merge节点