浅谈Git

206 阅读8分钟

前言

本文主要介绍Git的原理以及常用的命令。并分享一下在多人协同开发中常见的如代码冲突、修改被覆盖、暂存修改撤换分支、撤销commit、如何将漏提交的修改合并到上一个commit、分支回退到某个历史版本等等情况时,如何利用Git完美解决这些问题。

Git 是什么

Git是一个开源的分布式版本控制软件,Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

Git 对比 SVN

  • CVS/SVN

    集中式版本控制软件,所谓集中式就是只有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。

    • 因为要向中央服务器拉取/更新/推送代码,所以必须要联网,又会受网络影响
    • 严重依赖服务器端,如果服务器挂了那么版本控制也就无法使用了
  • Git

    基于分布式版本控制工具,它与集中式版本控制的区别在于每一个终端就是一个仓库,这个终端并不仅仅只是一个最新版本而是完整的将代码仓库镜像下来。

    • 直接记录快照,而非差异比较
    • 近乎所有操作都是本地执行
    • Git可以更优雅操作分支与合并,以及更强的撤销修改、恢复历史版本工作等
    • 速度更快,因为可以本地就是一个仓库
    • 误操作不会污染服务器

Git 的原理

Git分为工作区暂存区Git仓库,所有本地修改都是在操作工作区的内容 即文件的修改操作,当使用add 命令时 工作区的变动就会被添加到暂存区,使用commit命令将暂存区的内容提交到Git仓库。每次更新Git并不会记录更新的内容,而是将更新后的文件坐一个快照,并指针指向最新的快照

  • 工作区:就是你在电脑里能看到的目录
  • 版本库:工作区有一个隐藏目录.git,这个不算工作区,而是Git的本地版本库,你的所有版本信息都会存在这里
  • 暂存区:英文叫stage, 或index。一般存放在 ".git目录下" * 下的index文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)

Git 的开发规范

  • 在data_crm 中发现的一些问题

    • 与环境对应的保护分支可以被开发push代码,例如开发环境对应的develop分支 可以直接在上面push提交。
      • 带来的问题:容易造成在版本上线合并分支时将在develop分支上的commit漏掉。
  • 分支命名问题

    • 功能分支:feature-{功能名称/tapdId}
    • 修复紧急问题分支:hotfix-{功能名称/tapdId}
    • 已上分支都应该基于稳定分支拉取
  • 重复的commit

    • 带来的问题
      1.commit多而杂,在review时显得很乱
      2.比如多个功能在同一分支开发时,其中某个功能需要提前上线,这时就不得不将糅杂在一起的commit一个个的cherry-pick出来,会显得很麻烦。
      • 使用git rebase -i HEAD~x 压缩多个commit,教程:https://blog.csdn.net/MBuger/article/details/86241033
      • 使用 git commit --amend "xx" 将当前的更改合并入上一个commit,并更改新的commit注释
      • 直接使用gitlab自带的压缩功能(好像并不能自由选择要压缩的commit)
      • 合并后的提交历史
        并不非常建议大家频繁是用git rebase 来合并commit,因为使用git rebase压缩comit后,必须要使用push -f 强推到远端,push -f 是一个危险的操作
  • 避免Merge remote-tracking branch xx 这样无效的commit信息

    • 造成原因

    在进行 pull 操作的同时,其实就是 fetch+merge 的一个过程。我们从 remote 分支中拉取新的更新,然后再合并到本地分支中去。
    1. 如果 remote 分支超前于本地分支,并且本地分支没有任何 commit 的,直接从 remote 进行 pull 操作,默认会采用 fast-forward 模式,这种模式下,并不会产生合并节点,也就是说不会产生多余的那条 log 信息
    2. 如果想之前那样,本地先 commit 后再去 pull,那么此时,remote 分支和本地会分支会出现分叉,这个时候使用 pull 操作拉取更新时,就会进行分支合并,产生合并节点和 log 信息。

    • 解决办法

    可以使用 git pull --rebase 使用rebase 就不会产生分支合并操作 详细介绍

  • 如何避免代码覆盖

建议在提交代码前按顺序执行
1.git stash 或者 git add/git commit
2.git pull --rebase (使用 rebase 策略替代默认的 merge 策略)
3.git apply 如果第一步不是git stash 则跳过
4.有冲突解决冲突
5.git add/git commit/git push

  • 如何解决冲突

1.解决特性分支合并时产生的冲突时时比较容易的,在本地解决完后,在提交到远程。

2.如果是在将特性分支合并到master等受保护的分支时产生冲突,应该首先基于master分支拉去一条fix 冲突的分支,将特性分支先合并到fix 冲突分支,然后解决完冲突后在将fix 冲突分支合进master,此时特性分支和master将不在冲突。
应严厉禁止将develop/master 等不稳定分支合并进自己的特性分支,因为这样会导致特性分支在合并入release时污染release

3.在碰到冲突时,因该用=======下半段覆盖上版段,如果使用idea解决时应该用右边文件内容为最新内容

4.直接在Gitlab提交合并请求是更合理的做法。

  • 首先不应该直接向保护分支直接push代码
  • 其次在本地进行分支合并容易忘记更新代码而直接使用merge操作,比如:要将featrue/task-job合并到develop ,在你切换到develop分支很可能本地develop远远落后于远程develop,而恰好你又忘记git pull而直接执行了git merge featrue/task-job,此时你将无法推送到远端,并且git会提醒你进行pull,然后你会发现你的本地代码会出现大量冲突等待你解决。

5.tapd关联Gitlab,使需求能和某个分支/某个commit 关联起来,更能直观的感受某个commit是为了修复那个问题,某条分支是为了完成那个功能。
TAPD项目与源码仓库进行关联
Gitlab关联TAPD Gtlab关联Jira

Git 常用命令

基本命令

  • git clone <url> 克隆代码仓库到本地

  • git pull 拉取远程分支最新变动到本地

  • git fetch 拉取远程仓库所有分支的变动到本地

  • git push 推送本地变动到远程分支

  • git checkout <xxx.java> 取消工作空间xxx.java文件的所有变动 git checkout . 取消工作空间所有变动文件的改动

  • git checkout <dev> 切换dev分支

  • git branch <dev> 创建本地一个名为dev的分支

  • git checkout -b <dev> 创建一个名为dev的本地分支并切换到该分支

  • git branch 查看所有本地分支 git branch -a 查看所有本地分支+远程分支

  • git fetch -p 清除本地无效远程分支(远程代码仓库该分支已被删除)

  • git branch -d <dev> 删除名为dev的本地分支

  • git push --set-upstream origin <dev> 将本地dev推到远程

  • git push origin --delete <dev> 删除远程dev分支

  • git rebase 详细介绍 分支与分叉

  • git rebase -i HEAD~<x> 可以做合并commit的操作,x代表若干个commit

  • git rebase <dev> 合并名为dev的分支到当前分支

  • git diff 查看本地改动

  • git log --graph

  • git branch --merged/git branch --no-merged 查看已合并当前的分支/未合并的分支

配置Git别名

Git 如何处理日常开发中的各种异常情况

  • 想修改已经提交到暂存区的commit信息

    git commit --amend -m"新的备注"

  • 将遗漏的修改重新提交

    git commit --amend --no-edit

  • 远程分支被删除,推自己本地分支到远端

    git push --set-upstream origin <分支名>

  • 撤销add到暂存区的文件

    git reset <文件名>/git reset HEAD <文件名>

  • 撤销改动过的文件

    git checkout ./git checkout <文件名>/git clean -df

  • 将本地分支回退到指定版本

    git reset HEAD^ 回退一个版本 修改会保留在工作空间
    git reset --hard <commit SHA> 回退到某个commit版本 丢弃所有修改
    git reset --soft <commit SHA> 回退到某个commit 修改保留在暂存空间
    git reset HEAD^ 回退一个版本

  • 将其他分支上的某个提交合并到另一条分支

    git cherry-pick <commit SHA>

  • 将本地改动暂存起来(还可以恢复)

    暂存git stash 恢复git stash apply/git stash pop

于2020-7-15 周三 深圳