Git 的正确使用姿势

笔者参加了今年字节跳动举办的后端青训营,在听了其中一节关于教导如何正确使用Git的课后,作下本篇笔记。

本篇笔记主要讲述了如何正确地使用Git进行代码版本管理。

前言

为什么要学习Git?

  • 协同工作:业界绝大多数公司都是基于Git进行代码管理
  • 开源社区:目前绝大多数的开源项目都是基于Git维护的

Git是什么

Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

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

分布式版本控制:

  1. 每个库都存有完整的提交历史,可以直接在本地进行代码提交
  2. 每次提交记录的都是完整的文件快照,而不是记录增量
  3. 通过Push等操作来完成和远端代码的同步
    优点:
  4. 分布式开发,每个库都是完整的提交历史,支持本地提交,强调个体
  5. 分支管理功能强大,方便团队合作,多人协同开发
  6. 校验和机制保证完整性,一般只添加数据,很少执行删除操作,不容易导致代码丢失
    缺点:
  7. 相对比较复杂,学习成本更高
  8. 对于大文件的支持不是特别好(git-lfs工具可以弥补这个功能)
     

Git的实践

初始化

Git仓库初始化:git init

其他参数:

  • --initial-branch:git的默认初始化分支是master分支,也可以通过指定参数初始化为别的分支
  • --bare:创建一个裸仓库(纯Git目录,没有工作目录)
  • ---template:可以通过模板来创建预先构建好的自定义git目录

配置

Git的配置级别从高到低分别有:

  • --global:配置存在于~/.gitconfig
  • --system:配置存在于$(prefix)/etc/gitconfig
  • ---local:配置存在于.git/config
    每个级别的配置可能重复,但是低级别的配置会覆盖高级别的配置。

用户名配置:

  • git config --global user.name "username":其中username字符串为自定义用户名
  • git config --global user.email xxx@xxx.com:其中xxx@xxx.com为自定义用户邮箱

Instead of 配置

  • git config --global url.git@github.com:.insteadOf https://github.com/:本例中将ssh协议换为http协议

Git命令别名配置

  • git config --global alias.cin "commit --ammend --no-edit:本例中添加了cin命令表示commit --ammend --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

同一个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 免密配置

URL:https://github.com/git/git.git
将密码存储在内存或硬盘,可以快速跳过认证阶段

  • 内存: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

一般情况下不建议使用HTTP的方式访问Git,因为不够安全,使用SSH的方式会更常用。

URL:git@github.com:git/git.git。

SSH的免密配置方式通过公私钥的机制,将生成公钥存放在服务器,从而实现免密访问。

目前Key的类型分为四种,分别是dsa、rsa、ecdsa、ed25519

默认使用的是rsa,由于一些安全问题,现在已经不推荐使用dsa和rsa,优先推荐使用ed25519。

  • ssh-keygen -t ed25519 -C "your_email@example.com"
  • 密钥默认存储在 ~/.ssh/id_ed25519.pub

执行以上语句后,控制台会输出密钥保存的目录,进入这个目录,打开对应的pub文件,将文件内容复制并设置到github上setting中Access - SSH and GPG keys中的对应位置即可

Git Add

  • git add .:将变更的内容存储在暂存区

Git Commit

  • git commit -m "注释":将新增内容添加到git目录中

Objects

blob、tree、commit在git中都统一称为Object:

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

以上三个信息串联在一起的方式:

  1. 通过Commit寻找到Tree信息,每个Commit都会存储对应的Tree ID
  2. 通过Tree存储的信息,获取到对应的目录树信息
  3. 从tree中获得blob的ID,通过Blob ID获取对应的文件内容

image.png

Refs

refs的内容就是对应的Commit ID,因此把ref当做指针,指向对应的Commit来表示当前Ref对应的版本。

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

Branch

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

Tag

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

Annotation Tag

附注标签:一种特殊的Tag,可以给Tag提供一些额外的信息。
通过git tag -a命令来完成附注标签的创建。

追溯历史版本

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

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

  1. 修改文件并提交,创建新的commit
  2. 查看最新的commit,新增了parent信息。

修改历史版本

  1. commit --amend:通过这个命令可以修改最近的一次commit信息,修改之后commit id会变
  2. rebase:通过git rebase -i HEAD~3可以实现对最近三个commit的修改:
    1. 合并commit
    2. 修改具体的commit message
    3. 删除某个commit
  3. filter --branch:该命令可以指定删除所有提交中的某个文件或者全局修改邮箱地址等操作

Git GC

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

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

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

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

完整的Git视图

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来完成强制推送,一般不推荐主干分支进行该操作,正常都应该解决冲突后再进行推送

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

常见问题

  1. 为什么配置了Git配置,依然没有办法拉取代码?
    • 免密认证没有配
    • instead Of配置没有配,配的是SSH免密配置,但是使用的依然是HTTP协议访问
  2. 为什么Fetch了远端分支,但本地当前的分支历史仍然没有变化?
    • Fetch会把代码拉取到本地的远端分支,但是不会合并到当前分支,所以当前分支历史没有变化
       

Git研发流程

不同的工作流

image.png

集中式工作流

集中式工作流只依托于master分支进行研发活动。

工作方式

  1. 获取远端master代码
  2. 直接在master分支完成修改
  3. 提交前拉取最新的master代码和本地代码经合并(使用rebase),如果有冲突需要解决冲突
  4. 提交本地代码到master

集中式工作流-Gerrit

Gerrit是由Google开发的一款代码托管平台,主要的特点就是能够很好的进行代码评审。在aosp (android open source project) 中使用的很广,Gerrit的开发流程就是一种集中式工作流。

基本原理:

  1. 依托于Change ID概念,每个提交生成一个单独的代码评审。
  2. 提交上去的代码不会存储在真正的refs/heads/下的分支中,而是存在一个refs/for/的引用下。
  3. 通过refs/meta/config下的文件存储代码的配置,包括权限,评审等配置,每个Change都必须要完成Review后才能合入。

优点:

  1. 提供强制的代码评审机制,保证代码的质量
  2. 提供更丰富的权限功能,可以针对分支做细粒度的权限管控
  3. 保证master的历史整洁性
  4. Aosp多仓的场景支持更好

缺点:

  1. 开发人员较多的情况下,更容易出现冲突
  2. 对于多分支的支持较差,想要区分多个版本的线上代码时,更容易出现问题
  3. 一般只有管理员才能创建仓库,比较难以在项目之间形成代码复用,比如类似的fork操作就不支持。

分支管理工作流

image.png

分支管理工作流-GitFlow

Git Flow是比较早期出现的分支管理策略,包含五种类型的分支:

  • Master: 主干分支
  • Develop: 开发分支
  • Feature: 特性分支
  • Release: 发布分支
  • Hotfix:热修复分支

优点: 如果能按照定义的标准严格执行,代码会很清晰,并且很难出现混乱。

缺点: 流程过于复杂,上线的节奏会比较慢。由手太复杂,研发容易不按照标准执行,从而导致代码出现混乱。

分支管理工作流-Github Flow

Github 的工作流,只有一个主干分支,基于Pull Request往主干分支中提交代码。

选择团队合作的方式:

  1. owner创建好仓库后,其他用户通过Fork 的方式来创建自己的仓库,并在 fork的仓库上进行开发
  2. owner创建好仓库后,统一给团队内成员分配权限,直接在同一个仓库内进行开发

分支管理工作流-Gitlab Flow

Gitlab推荐的工作流是在GitFlow和Gthub Flow上做出优化,既保持了单一分支的简便,又可以适应不同的开发环境。

原则:upstream first上游优先

只有在上游分支采纳的代码才可以进入到下游分支,一般上游分支就是master

image.png

代码合并

Fast-Forward:不会产生一个merge 节点,合并后保持一个线性历史,如果target分支有了更新,则需要通过rebase 操作更新source branch后才可以合入。

image.png

Three-Way Merge:三方合并,会产生一个新的merge节点。

image.png

如何选择合适的工作流

选择原则:没有最好的,只有最合适的

针对小型团队合作,推荐使用 Github 工作流即可

  1. 尽量保证少量多次,最好不要一次性提交上千行代码
  2. 提交Pull Request后最少需要保证有Code Review后再合入
  3. 主干分支尽量保持整洁,使用fast-forward合入方式,合入前进行rebase

大型团队合作,根据自己的需求指定不同的工作流,不需要局限在某种流程中。

常见问题

  1. 在Gerrit平台上使用Merge的方式合入代码。

    • Gerrit是集中式工作流,不推荐使用Merge方式合入代码,应该是在主干分支开发后,直接Push。
  2. 不了解保护分支,Code Review,Cl等概念,研发流程不规范。

    • 保护分支: 防止用户直接向主干分支提交代码,必须通过Pull Request来进行合入。
    • Code Review,CI:都是在合入前的检查策略,Code Review是人工进行检查,CI则是通过一些定制化的脚本来进行一些校验。
  3. 代码历史混乱,代码合并方式不清晰。

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

写在最后

由于课程时间有限,课程中提到的大部分内容都是浅尝辄止,当做是对Git进行入门了解还是不错的。在日后的开发工作后,要注意使用Git的规范,进而提高开发效率。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用Git LFS(Large File Storage)可以更好地管理大文件,以避免将它们包含在Git仓库中导致仓库过大的问题。下面是使用Git LFS的一些基本步骤: 1. 安装Git LFS:首先,您需要在您的计算机上安装Git LFS。您可以从Git LFS的官方网站(https://git-lfs.github.com/)下载适用于您操作系统的安装程序,并按照安装指南进行安装。 2. 配置Git LFS:安装完成后,您需要告诉Git LFS要跟踪和管理哪些大文件。在您的Git仓库目录中,运行以下命令配置Git LFS: ``` git lfs install ``` 3. 跟踪大文件:要跟踪某个大文件,您可以使用以下命令: ``` git lfs track <path_to_large_file> ``` 这将在.gitattributes文件中添加一个规则,指定要使用Git LFS来处理指定路径下的大文件。 4. 提交和推送:一旦您完成了对大文件的跟踪,您可以像正常提交和推送其他文件一样提交和推送您的更改。Git LFS会自动将大文件上传到Git LFS服务器,并在提交时替换为Git LFS的指针。 5. 克隆仓库和获取LFS对象:当您克隆带有Git LFS的仓库时,您需要获取LFS对象,即实际存储在仓库之外的大文件。在克隆后的仓库目录中,运行以下命令获取LFS对象: ``` git lfs fetch ``` 6. 检出文件:一旦您获取了LFS对象,您可以使用以下命令将文件检出到您的工作目录: ``` git lfs checkout ``` 这将从Git LFS服务器下载大文件并将其放置在正确的位置。 请注意,如果您只想下载特定的大文件而不是克隆整个仓库,可以使用`git lfs pull`命令,后跟特定的文件路径: ``` git lfs pull --include "<path_to_large_file>" ``` 这样,您就可以使用Git LFS来管理和下载大文件了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值