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

251 阅读7分钟

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

Git 是什么

版本控制

版本控制即一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统;协助开发者更好的关注变更,了解每个版本的改动,方便对改动的代码进行检查,预防事故发送;也能随时切换到不同的版本,回滚误删误改的问题代码

本地版本控制

  • 依托于本地磁盘进行版本控制(RCS)

集中式版本控制

  • 存在一个统一的远端服务器,用于版本控制,本地不存储版本控制(SVN)

分布式版本控制

  • 每个库都拥有所有的版本控制信息,远端服务器用于不同库之间进行版本信息同步(Git)

代表性工具: Git基本原理:

1.每个库都存有完整的提交历史,可以直接在本地进行代码提交

2.每次提交记录的都是完整的文件快照,而不是记录增量 3.通过 Push等操作来完成和远端代码的同步 优点: 1.分布式开发,每个库都是完整的提交历史,支持本地提交,强调个体

2.分支管理功能强大,方便团队合作,多人协同开发 3.校验和机制保证完整性,一般只添加数据,很少执行删除操作,不容易导致代码丢失

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

git的基本使用方式

1.为什么我明明配置了Git配置,但是依然没有办法拉取代码?

  • 免密认证没有配。
  • Instead Of 配置没有配,配的 SSH免密配置,但是使用的还是 HTTP 协议访问。

2.为什么我 Fetch了远端分支,但是我看本地当前的分支历史还是没有变化

Fetch 会把代码拉取到本地的远端分支,但是并不会合并到当前分支,所以当前分支历史没有变化。

项目初始化

git init

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

524tree.png

  • HEAD:表示所指向的当前目录
  • config:当前目录的配置
  • objects:相关文件信息
  • refs:分支信息

524ml.png

git配置

  • 用户名配置
git config --global user.name "xxx"
git config --global user.email "xxx@xxx.com"
复制代码
  • Instead of 配置(替换源类型:ssh转https等)
git config --global url.git@github.com:.insteadOf https://github.com
复制代码
  • Git命令别名配置
git config --global alias.cin "commit -- amend --no-edit"

Git Remote

Git Remote 配置,分成 SSH 和 HTTP 两种协议实现,不同协议有不同的免密配置方式

  • 查看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
复制代码
  • 同一个origin设置不同的Push和Fetch URL
git remote add origin git@github.com:git/git
git remote set-url --add --push origin git@github.com:MY_REPOSITY/git
HTTP Remote
  • 免密配置

内存:git config --global credential.helper 'cache --timeout=3600'

硬盘:git config --global credential.helper 'store --file /path/to/credential-file' (不指定目录的情况默认是~/.git-credential)

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

SSH Remote
  • 免密配置:

SSH可以通过公私钥的机制,将生成公钥放在服务端,从而实现免密访问。目前的Key的类型有四种,分别是dsa、rsa、ecdsa、ed25519。默认是rsa,由于一些安全问题,现在不推荐使用dsa和rsa,优先推荐使用ed25519

  • 公钥配置
ssh-keygen -t ed25519 -C "your_eamil@example.com" 密钥默认存在~/.ssh/id_ed25519.pub
复制代码

生成后读取公钥便可在GitHub等仓库上进行配置处理

代码提交

  • Git Add

将代码从工作区提交到暂存区

  • Git Commit

将暂存区代码提交到 Git 存储,commit后会生成后续的commit object

  • 追溯历史版本

可以通过git log查看最新的commit id

修改历史版本
  1. commit --amend 通过这个命令可以修改最近的一次 commit 信息,修改之后 commit id 会变

  2. rebase 通过 git rebase -i HEAD~3 可以实现对最近三个 commit 的修改:

    1. 合并 commit
    2. 修改具体的 commit message
    3. 删除某个 commit
  3. filter --branch 该命令可以指定删除所有提交中的某个文件或者全局修改邮箱地址等操作

Objects

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

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

分支(branch)操作相关命令

查看本分支:$ git branch

查看远程分支:$ git branch -r

创建本地分支:$ git branch [name] ----注意新分支创建后不会自动切换为当前分支

切换分支:$ git checkout [name]

创建新分支并立即切换到新分支:$ git checkout -b [name]

删除分支:$ git branch -d [name] ---- -d选项只能删除已经参与了合并的分支,对于未有合并的分支是无法删除的。如果想强制删除一个分支,可以使用-D选项

合并分支:$ git merge [name] ----将名称为[name]的分支与当前分支合并

创建远程分支(本地分支push到远程):$ git push origin [name]

删除远程分支:$ git push origin :heads/[name]

我从master分支创建了一个issue5560分支,做了一些修改后,使用git push origin master提交,但是显示的结果却是'Everything up-to-date',发生问题的原因是git push origin master 在没有track远程分支的本地分支中默认提交的master分支,因为master分支默认指向了origin master 分支,这里要使用git push origin issue5560:master 就可以把issue5560推送到远程的master分支了。

如果想把本地的某个分支test提交到远程仓库,并作为远程仓库的master分支,或者作为另外一个名叫test的分支,那么可以这么做。

$ git push origin test:master // 提交本地test分支作为远程的master分支 [ 这句命令会删掉远端的master分支 。。。]

$ git push origin test:test // 提交本地test分支作为远程的test分支

如果想删除远程的分支呢?类似于上面,如果:左边的分支为空,那么将删除:右边的远程的分支。

$ git push origin :test // 刚提交到远程的test将被删除,但是本地还会保存的,不用担心

版本(tag)操作相关命令

查看版本:$ git tag

创建版本:$ git tag [name]

删除版本:$ git tag -d [name]

查看远程版本:$ git tag -r

创建远程版本(本地版本push到远程):$ git push origin [name]

删除远程版本:$ git push origin :refs/tags/[name]

Ref

Ref内容

Ref文件存储的内容就是对应的Commit ID,因此把ref当作指针,指向对应的Commit id就是当前Ref对应的版本

Ref种类

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

  • Branch 仓库分支

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

  • Tag 仓库标签

通过git tag命令生成tag,标签一般表示的是一个稳定版本,指向的Commit一般不会变更

通过git tag -a v.xxx -m "add feature"新建附注标签,此时会新建tag类型的object

Object

commit、tree、blob在git中都统一称为object,除此之外还有个tag的object

  • Blob 存储文件内容信息
  • Tree 存储目录树信息
  • Commit 存储提交信息,一个commit可以对应唯一版本的代码
  • Tag 存储附注标签信息
commit、tree、blob关联

当进行commit处理的时候,会在objects文件夹中生成对应commit、tree、blob对象

查询是否悬空的object

git fsck --lost-found

Git GC

  • GC

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

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

  • 指定时间

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

代码同步

  • Git Clone

将代码从远端拉取到本地,可以指定分支和深度

  • Git Fetch

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

  • Git 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 gitrebase 都有可能导致这个问题。
  2. 如果该分支就自己一个人使用,或者团队内确认过可以修改历史则可以通过 git push origin master -f 来完成强制推送,一般不推荐主干分支进行该操作,正常都应该解决冲突后再进行推送。
  • Git Merge
  1. Fast-Forward:不会产生以恶搞merge节点,合并后保持一个线性历史,如果target分支有了更新,则需要通过rebase操作更新source branch后才可以合入
  • 命令:git merge 目标分支 --ff-only

git流程

常见问题

1.在 Gerrit平台上使用Merge 的方式合入代码。 Gerrit是集中式工作流,不推荐使用Merge方式合入代码,应该是在主干分支开发后,直接Push。

2.不了解保护分支,Code Review,CI等概念,研发流程不规范。 保护分支:防止用户直接向主干分支提交代码,必须通过PR来进行合入。 Code Review,C:都是在合入前的检查策略,Code Review是人工进行检查,CI则是通过一些定制化的脚本来进行一些校验。

3.代码历史混升代应△#之中—生nuu 9793

不理解Fast Forward和Three Way Merge的区别,本地代码更新频繁的使用Three Way的方式,导致生成过多的 Merge节点,使提交历史变得杂不清晰。