Git版本控制、分支策略与代码评审

  • Git版本控制、分支策略与代码评审
    • Git介绍
    • Git工具与代码托管网站
      • 在Windows上安装使用Git
      • IDE支持Git
      • 代码托管网站
    • Git原理
      • Git工作流
      • Git 分支
        • 概述
        • 理解如何使用Git分支
    • Git常用命令
      • 参考文档
    • Git使用场景
      • 场景1: 在GitLab上新建一个repository
      • 场景2:将GitLab上一个repository clone到本地
      • 场景3:创建一个本地分支
      • 场景4:将本地分支的改动push到GitLab
      • 场景5:用户A/B同时修改同一个文件,A先提交,B后提交,解决冲突后push到GitLab
      • 场景6:在本地改动后,在git add 前回退
      • 场景7:在本地改动后,在git push 前回退
      • 场景8:在本地改动后,在git push后回退
      • 场景9:查看文件修改历史
      • 场景10:在GitLab上设置分支保护,要求只有通过merge request才能合并代码
      • 场景11:本地改动后,push到本地分支,然后在GitLab上发起merge request
      • 场景12:代码审查,通过merge request
      • 场景13:代码审查,拒绝merge request
      • 场景14:将本地的一个新项目放在GitLab上
    • 使用GitHub来学习提升
    • 分支策略
      • 主流分支策略比较
      • 如何选择分支策略
    • 代码评审
    • 思考: 什么文件不应该放在Git上?

Git版本控制、分支策略与代码评审

Git介绍

  • Git是一种免费的、开源的分布式版本控制系统(DVCS)
  • Git是目前使用最为广泛的版本控制系统
  • Git是由Linus Torvalds开发的 (这位IT界的大神同时也是Linux的作者)
  • Git的第一个版本(v0.99)在2005年7月发布,最新版本是v2.17(截止2018年4月)
  • Git支持Windows, Linux/Unix, Mac
  • 和商业版本控制系统相比,Git免费、开源且更强大、稳定、快速
  • 和传统版本控制系统(比如CVS, SVN)相比,Git具有支持轻量级分支、分布式开发、高性能等优点。
  • Git支持命令行操作,可以非常方便地集成到CI/CD pipeline中

参考文档:

  • https://en.wikipedia.org/wiki/Git

  • Git官网: https://git-scm.com

Git工具与代码托管网站

在Windows上安装使用Git

登录Git官网: https://git-scm.com/ 下载Git for Windows,安装后有3个程序:

  • Git Bash (模拟Linux终端)
  • Git CMD (Windows终端)
  • Git GUI (Windows界面)

推荐使用Git Bash,Git CMD和Git GUI可以忽略。

在Linux/Unix和Mac上使用Git和在Windows上使用Git Bash一样,不再赘述。

IDE支持Git

现在主流的IDE(比如Intellij Idea和Eclipse) 都对Git有非常好的支持。

推荐通过IDE + Git Bash 方法来使用Git。

代码托管网站

  • 全世界最大的代码托管(同性交友)网站: https://github.com/
  • 可以免费搭建自己的代码托管网站的GitLab: https://gitlab.com/
  • Atlassian公司全家桶的过气网红: https://bitbucket.org/

如何选择?

  • 土豪自己搭建GitHub Enterprise
  • Atlassian铁粉用BitBucket
  • 中产用GitLab Enterprise
  • 屌丝自己搭建GitLab CE (我是屌丝我自豪)
  • 最屌丝用国内的代码托管网站…

Git原理

Git工作流

下图列出了一个简明的Git工作流:

Git版本控制、分支策略与代码评审_第1张图片

Git 分支

概述

  • 分支(branch)可以理解为是仓库(repository)的一种隔离视图。
  • 分支的目的为了支持多人、多版本开发。

  • Git支持多分支开发,并鼓励使用本地多分支,这些分支之间是彼此完全独立的。

  • 和传统的版本控制工具(比如CSV, SVN)相比,Git对分支的隔离是逻辑隔离而不是物理隔离,这意味着创建分支非常快速、存储分支不会占用过多磁盘空间
  • 常见的分支操作包括:创建分支、切换分支、合并分支、删除分支。

参考文档:

  • https://git-scm.com/about/branching-and-merging

理解如何使用Git分支

这里以GitHub Flow为例 来演示常见的分支操作。

打开GitHub Flow ,点击动画上的不同区域来理解常见的分支操作:

  1. 创建分支

    • 给分支名取一个简短且有意义的名字
  2. 提交改动

    • 填写有意义的commit message: what & why

    • 推荐的commit message格式为 : ,比如:

      512: 增加了对NFS的支持

  3. 发起合并请求

    • 同时发起code review和讨论
    • 不经过review的代码不允许合并进public branch
  4. 代码评审和讨论

    • 审查代码是否满足功能、规范、测试结果、测试覆盖等要求
    • 审查合格才允许合并合进public branch,不合格的代码退回重新修改
    • 鼓励使用同行评审(Peer Review)而不是上下级评审,鼓励积极反馈、互动
    • 评审对事不对人,多建议,不批评,不指责
    • 评审过程对reviewer和代码提交者双方都是一个学习提升的过程
  5. 部署

    • 使用经过完整测试的分支来部署到生产环境
    • 部署后发生问题需要回退时,用上一个版本进行回退
  6. 合并回master分支

    • 部署成功,没有问题后才合并到master分支

GitHub Flow是一种非常好的Git分支策略,但不是唯一的Git分支策略。

在后面的分支策略中会对Git分支策略作深入探讨。

Git常用命令

参考文档

  • git no deep shit: http://rogerdudler.github.io/git-guide/
  • 常用Git命令清单(阮一峰): http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html
  • git操作规范(Miss_Ye): https://segmentfault.com/a/1190000014461898

除了查看网上的参考文档之外,还可以在Git Bash中执行git --helpgit --help 来查看Git帮助文档。

Git使用场景

下面列出常用的Git使用场景。

场景1: 在GitLab上新建一个repository

如果不想让公众都能看到你的代码,需要将repository设置为private,并将相应权限授给团队成员。

强烈推荐为每个repository创建README.md和.gitignore文件。

强烈推荐对repository的public branch进行保护,只有通过merge request才能合并代码进public branch。

场景2:将GitLab上一个repository clone到本地

GitLab支持SSH和HTTP(S)两种方式。

  • SSH方式需要将本地的SSH public key添加到GitLab账号的setting中
  • HTTP(S)方式第一次push到GitLab时需要提供用户名和密码登陆

设置git config的user.nameuser.email :

# 查看git config
git config --list

# 设置Global的user.name和user.email
git config --global user.name 
git config --global user.email 

# 查看git config确认
git config --list

从GitLab上clone一个repository到本地:

# SSH 方式
git clone 

# HTTP 方式
git clone 

克隆后的缺省分支是GitLab repository上的缺省分支,一般是master分支。

场景3:创建一个本地分支

Git创建分支时,缺省从当前分支(base branch)来创建,如果当前分支不是想要的base branch,需要先切换分支。

# 切换当前分支(base branch)
git checkout 

# 创建一个新分支,并切换到该分支
git checkout -b 

场景4:将本地分支的改动push到GitLab

# 更新working directory
git pull

# 改动代码...此处省略2048个字

# 查看working tree状态
git status

# 比较改动前后,查看改动了什么 (用IDE来比较更加直观)
git diff 

# 添加新建的文件到Git Stage中
git add 

# 或添加全部新建的文件到Git Stage中
git add .

# 提交改动到local repository
git commit -m ""

# 推送到remote repository,创建remote new branch,并设置upstream reference
git push -u origin 

如果第一次push时不通过-u 来push新的branch,在git pull时会提示要先设置upstream reference。

# 设置upstream reference
git branch --set-upstream-to=origin/ 

之后的push可以不需要再加-u参数:

git push origin 

场景5:用户A/B同时修改同一个文件,A先提交,B后提交,解决冲突后push到GitLab

此时B在git push 时会遇到错误,错误信息为:

error: failed to push some refs to …
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., ‘git pull …’) before pushing again.
hint: See the ‘Note about fast-forwards’ in ‘git push –help’ for details.

B执行git pull 试图自动合并A先前提交的改动,自动合并失败的情况下需要手工合并:

Auto-merging …
CONFLICT (content): Merge conflict in …
Automatic merge failed; fix conflicts and then commit the result.

B打开并编辑发生冲突的文件,手工修改后,保存文件。

参照场景4,重新提交完成合并冲突(合并冲突也是一种改动)的文件到GitLab。

场景6:在本地改动后,在git add 前回退

撤销本地改动:

git checkout -- 

推荐使用IDE的revert功能,更加简单直观。

Intellij Idea: Version Control -> Local Changes -> Right click the file -> Revert

场景7:在本地改动后,在git push 前回退

用远程repository强制替换本地文件:

git fetch origin
git reset --hard origin/

推荐使用IDE的Reset功能,更加简单直观。

Intellij Idea: Version Control -> Log -> Right click the last commit marked with remote branch (origin/) -> Reset current branch to here -> Select “Hard”

场景8:在本地改动后,在git push后回退

方法一:

手工回退后,重新提交改动去覆盖remote branch。

方法二:

使用IDE的revert功能。

Intellij Idea: Version Control -> Log -> Right click the commit -> Revert, and then commit and push a “revert commit” to override both remote and local branches.

方法三:

# 查看git commit log,复制要回退的版本commit id
git log --graph --oneline --decorate --all

# 将本地回退到指定版本
git reset --hard 

# 查看working tree状态,此时本地落后于远程1个commit
git status

# 强制用本地覆盖远程(危险操作!!)
git push -f origin 

# 再次查看working tree状态,此时本地和远程应该同步
git status

推荐使用方法二

方法三比较危险,不推荐使用;强烈建议通过protect branch来防止强制用本地覆盖远程分支。

场景9:查看文件修改历史

# 按树形结构显示
git log --graph --oneline --decorate --all [file]

# 按列表显示
git log --pretty=oneline [file]

# 简单粗暴显示
git log [file]

推荐使用IDE的查看历史功能。

Intellij Idea:

  • 查看全部文件修改历史:Version Control -> Log
  • 查看单个文件修改历史:Right click the file -> Git -> Show history

场景10:在GitLab上设置分支保护,要求只有通过merge request才能合并代码

用Admin或Owner或Master账号登录,打开指定project -> Settings -> Repository -> Protected Branches

选择要protect的branch (支持wildcard):

  • 要求每个人都要通过merge request才能合并代码进入public branch:选择”Allowed to merge”为”Developers + Masters”,选择”Allow to push” 为”No one“
  • 对public branch都要设置protect branch

参考文档:

https://docs.gitlab.com/ee/user/project/protected_branches.html

在我使用的GitLab CE 10.6.4 貌似有一个bug,当选择”Allowed to
push”为”Masters“时,Developer角色的用户仍然可以push。
这个问题的原因可能是在向gitlab作git push时,读取的是在Windows上Manage WindowsCredentials中的另一个较高权限的username,所以gitlab才允许作git push

场景11:本地改动后,push到本地分支,然后在GitLab上发起merge request

参照场景4,创建一个新的本地分支,改动后push到远程的该分支。

然后在GitLab上发起merge request:

  1. 打开项目,点击左边的Merge Requests,选择New merge request
  2. 选择Source branch和Target branch
  3. 点击”Compare branches and continue“
  4. 选择Assignee (不要选择Assign to me)
  5. 如果是临时branch,勾选“Remove source branch when merge request is accepted”
  6. 检查无误后,点击“Submit merge request”按钮

GitLab CE貌似不能做到防止自己来merge自己的merge request,这一点和GitHub Enterprise还是有差距的。

所以只能流程上要求大家不要merge自己的merge request。

如果发起merge request后,在该merge request关闭之前,继续往source branch分支push代码,这些commit也会被自动包含在该merge request里。

场景12:代码审查,通过merge request

  1. 打开项目,点击左边的Merge Request,选择Open查看Open状态的merge request

  2. 点击Commit查看详细信息

  3. 点击Changes查看具体的改动

  4. 写comment,并和代码提交者讨论。

  5. 审查通过后,点击”Merge”按钮

场景13:代码审查,拒绝merge request

如果审查不通过,写清楚comment,并在和代码提交者讨论后,要求其进行修改。

场景14:将本地的一个新项目放在GitLab上

在GitLab上创建一个同名的空project (不包含任何文件),设置好用户权限。

用Owner/Master角色账号将本地项目push到GitLab:

# 设置Global的user.name和user.email (如果需要)
git config --global user.name 
git config --global user.email 

# 将本地项目转成Git项目
git init

# 检查对应的remote repository
git remote -v

# 添加远程的remote repository
git remote add origin 

# 确认remote repository
git remote -v

# 推送到remote branch
git status
git add .
git commit -m "Inital commit"
git push -u origin 

在我使用的GitLab CE 10.6.4 有一个bug,当创建好一个空的项目后,用Owner账号将本地项目作git push,会遇到”You are not allowed to push code to this project“的错。

这个问题的原因是在向gitlab作git push时,读取的是在Windows上Manage Windows Credentials中的另一个错误的username,所以被gitlab拒绝了。

解决的方法是,在Manage Windows Credentials中删除原来的credentials,作git push时会提示输入用户名和密码,输入owner的用户名和密码后,就可以git push成功了。

这应该是GitLab的一个设计缺陷,也就是GitLab假设在一个GitLab上,你总是只用一个账号来登陆。

参考文档:https://gitlab.com/gitlab-com/support-forum/issues/207

使用GitHub来学习提升

打开GitHub Explore 来查看GitHub上最流行的项目,也可以通过搜索来查看比如最流行的Java项目、最流行的JavaScript项目等等。

国内有一些走歪道的程序员上淘宝买GitHub加star服务(鄙视),所以看到一些流行的中文项目时,要持保留态度。

大部分开源软件都把代码托管在GitHub上,可以通过GitHub来clone开源软件的代码到本地作源代码研读,是提升编程的内功的有效方法。如果想加快clone速度可以在git clone后加上--depth=1 的参数来只clone最新版本。

你也可以把你的点子变成一个项目放到GitHub上,分享给开源社区。

分支策略

分支策略就是在哪个分支上开发、在哪个分支上部署、分支同步和分支合并的原则。

项目团队应该遵循统一的分支策略。

现代主流的分支策略包括:

  • Git Flow
  • GitHub Flow
  • Trunk based development

注意:这里讲的分支不包括个人的private branch,不管哪种分支策略都要求先在private branch开发测试后再发起merge request/pull request, 通过代码评审后才能合并进入public branch。

主流分支策略比较

下表对上面的三种现代主流的分支策略进行了比较:

Git版本控制、分支策略与代码评审_第2张图片

Trunk based development官网将Trunk based development和一些现代主流的分支策略进行了比较:

https://trunkbaseddevelopment.com/alternative-branching-models/

下面这篇文章也详细地论述了比较了不同的分支策略:

https://www.continuousdeliveryconsulting.com/blog/version-control-strategies/

如何选择分支策略

分支策略没有好坏,适合自己的才最重要。

通常的建议是,项目团队选择一种比较符合自己团队的主流分支策略,在实际工作中加以调整,最终发展成符合团队开发节奏的分支策略。

分支越多,复杂度越大,分支同步与分支合并的成本越大,团队内部的沟通成本也越大。

选择分支策略的原则:

  • 始终保持master处在可发布状态
  • 始终保持构建处在成功状态
  • 降低代码合并的复杂度和风险
  • 降低团队沟通成本
  • 符合团队的现状

代码评审

前面章节也已经描述了如何作代码评审,这里再对代码评审的一些原则作小结:

  • Four eyes check
  • 不要既当运动员,又当裁判员

  • 审查代码是否满足功能、规范、测试结果、测试覆盖等等

  • 借助工具来提升代码审查的效率
  • 审查合格才允许合进public branch,不合格的代码退回重新修改
  • 不怕犯错,但是不要犯重复的错
  • 提交审查前,自己先检查一遍
  • 鼓励使用同行评审(Peer Review)而不是上下级评审,鼓励积极反馈、互动
  • 评审对事不对人,多建议,不批评
  • 己所不欲,勿施于人
  • 评审过程对reviewer和代码提交者双方都是一个学习提升的过程
  • 保持开放的心态,相互学习,共同成长

思考: 什么文件不应该放在Git上?

你应该把几乎一切文件都作版本控制,除了:

  • 敏感信息,比如用户名、密码、Token等
  • 编译后产生的二进制文件,比如jar/war包、class文件
  • 测试结果
  • 日志
  • 临时文件
  • IDE自动生成的配置文件

  • 空文件夹(如果一定要加入Git,在该目录下添加一个.gitkeep 文件)

使用.gitignore来让Git忽略上述的文件和文件夹。

参考文档:

  • GitHub gitignore: https://github.com/github/gitignore

你可能感兴趣的:(Git,GitLab)