开发工具篇第二讲:git使用技巧 从基础到进阶(快速入门/高阶用法/git别名/项目实战/gitLab)

git 是一个开源的分布式版本控制系统,可以有效高速地处理从很小到非常大的项目版本管理。它是Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件,git作为版本管理工具,程序员是必须要掌握的。本文是开发工具篇第二讲:主要介绍了git的常规使用方法及在日常开发实战场景中git的应用。

文章目录

1、认识 git

  • Linux 内核项目组当时使用分布式版本控制系统 BitKeeper 来管理和维护代码。但是,后来开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使用 BitKeeper 时的经验教训,开发出自己的版本系统,而且对新的版本控制系统做了很多改进。

2、git 原理

2.1、git 与其他版本管理系统的主要区别

git 在保存和对待各种信息的时候与其它版本控制系统有很大差异,尽管操作起来的命令形式非常相近,理解这些差异将有助于防止你使用中的困惑。
下面我们主要说一个关于 Git 其他版本管理系统的主要差别:对待数据的方式。

  • Git采用的是直接记录快照的方式,而非差异比较。

    • 大部分版本控制系统(CVS、Subversion、Perforce、Bazaar 等等)都是以文件变更列表的方式存储信息,这类系统将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。具体原理如下图所示,理解起来其实很简单,每个我们对提交更新一个文件之后,系统记录都会记录这个文件做了哪些更新,以增量符号Δ(Delta)表示。
      在这里插入图片描述
  • 我们怎样才能得到一个文件的最终版本呢?

    • 很简单,高中数学的基本知识,我们只需要将这些原文件和这些增加进行相加就行。
  • 这种方式有什么问题呢?

    • 比如我们的增量特别特别多的话,如果我们要得到最终的文件是不是会耗费时间和性能。
  • Git 不按照以上方式对待或保存数据。 反之,Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个快照流。

2.2、git 原理

  • 类似于C语言中的指针

3、git 的三种状态

git 有三种状态,你的文件可能处于其中之一:

  1. 已提交(committed):数据已经安全的保存在本地数据库中。
  2. 已修改(modified):已修改表示修改了文件,但还没保存到数据库中。
  3. 已暂存(staged):表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中
    由此引入 Git 项目的三个工作区域的概念:Git 仓库(.git directoty)、工作目录(Working Directory) 以及 暂存区域(Staging Area) 。
    在这里插入图片描述
  • 基本的 Git 工作流程如下:
  1. 在工作目录中修改文件。(写代码的地方) 本地仓库,没有经过add
  2. 暂存文件,将文件的快照放入暂存区域。(临时存储) git add 后,未进行提交的部分
  3. 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。(历史版本)

4、git 快速使用

4.1、git 基础操作/增删改提交查询

序号作用命令备注
1在现有目录中初始化仓库: 进入项目目录运行git init 命令该命令将创建一个名为 .git 的子目录。
2从一个服务器克隆一个现有的 Git 仓库git clone [url] 自定义本地仓库的名字: git clone [url] directoryname
3检测当前文件状态git status查看工作区、暂存区状态
4提出更改(把它们添加到暂存区)git add filename (针对特定文件)、git add *(所有文件)、git add *.txt(支持通配符,所有 .txt 文件)将工作区的新建/修改添加到暂存区
5忽略文件.gitignore 文件可以保证分支的干净,不受编译器不同造成的影响
6提交更新git commit -m “代码提交信息” (每次准备提交前,先用 git status 看下,是不是都已暂存起来了, 然后再运行提交命令 git commit)将暂存区的内容提交到本地库
7跳过使用暂存区域更新的方式git commit -a -m “代码提交信息”git commit 加上 -a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤
8移除文件git rm
9删除文件并找回前提是删除前,文件已经提交到了本地库,操作:git reset --hard[指针位置] (删除操作已经提交到本地库:指针位置指向历史记录;删除操作没有提交,指针位置使用HEAD)
10对文件重命名git mv README.md README (这个命令相当于mv README.md README、git rm README.md、git add README 这三条命令的集合)
11远程仓库的移除与重命名将 test 重命名位 test1:git remote rename test test1 移除远程仓库 test1:git remote rm test1

4.2、一个好的 git 提交消息

一个好的 Git 提交消息如下: 标题行:用这一行来描述和解释你的这次提交

  • 主体部分可以是很少的几行,来加入更多的细节来解释提交,最好是能给出一些相关的背景或者解释这个提交能修复和解决什么问题
  • 主体部分当然也可以有几段,但是一定要注意换行和句子不要太长。因为这样在使用 “git log” 的时候会有缩进比较好看。提交的标题行描述应该尽量的清晰和尽量的一句话概括。这样就方便相关的 Git 日志查看工具显示和其他人的阅读。

4.3、推送改动到远程仓库

  • 如果你还没有克隆现有仓库,并欲将你的仓库连接到某个远程服务器,你可以使用如下命令添加:git remote add origin < server>,比如我们要让本地的一个仓库和 Github 上创建的一个仓库关联可以这样 git remote add origin https://github.com/qiwenjie/test.git
  • 将这些改动提交到远端仓库:git push origin master (可以把 master 换成你想要推送的任何分支)
  • 如此你就能够将你的改动推送到所添加的服务器上去了。

4.4、查看提交历史

  • 在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。 完成这个任务最简单而又有效的工具是 git log 命令。git log 会按提交时间列出所有的更新,最近的更新排在最上面。
    • 可以添加一些参数来查看自己希望看到的内容:
      • 例如:只看某个人的提交记录:git log --author=qiwenjie

4.5、撤销操作 (类似于c/c++中指针的操作)

有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 --amend 选项的提交命令尝试重新提交:

  • 重新提交 git commit --amend

    使用场景
    1、合并多次提交 提交记录没有变多,在同一个提交里面新文件被提交进去了,并且提交的描述也有所变化。
    对于这个新功能你改了多次就重复这个操作多次就可以了,只会产生一个提交。
    参考博客:https://blog.csdn.net/liuxiaoheng1992/article/details/104029345

  • 取消暂存的文件 git reset filename

  • 撤消对文件的修改: git checkout – filename

  • 回退提交记录(基于索引值)

    • git reset --hard 目标hash值
    • 经常使用
  • 回退提交记录(基于HEAD,只能后退,没法前进)

    • git reset --hard HEAD^ (^数量代表回退提交记录的个数)
    • git reset --hard HEAD~3 (~3 代表回退提交记录的个数)
    • reset三个参数的对比
      • – soft参数 仅仅在本地库移动HEAD指针
      • – hard 在本地库移动HEAD指针,并重置暂存区
      • – mixed参数 在本地库移动HEAD指针,并重置暂存区,并重置工作区
  • 假如你想丢弃你在本地的所有改动与提交,可以到服务器上获取最新的版本历史,并将你本地主分支指向它:

    • git fetch origin
    • git reset --hard origin/master

4.6、实战场景 git 恢复之前版本的三种方法reset、revert、rebase

1、问题描述
  • 在利用github实现多人合作程序开发的过程中,我们有时会出现错误提交的情况,此时我们希望能撤销提交操作,让程序回到提交前的样子,
  • 有三种解决方法:
    • 重置(reset)
    • 反做(revert)
    • 变基(rebase)
2、解决方案
方法一:git reset

原理:

  • git reset的作用是修改HEAD的位置,即将HEAD指向的位置改变为之前存在的某个版本

具体实现:

  • 查看版本号 使用命令git log查看;
  • 使用“git reset --hard 目标版本号”命令将版本回退;
  • 再用“git log”查看版本信息,此时本地的HEAD已经指向之前的版本;
  • 最后使用“git push -f”强制提交到远程更改

注意

  • 此时如果用“git push”会报错,因为我们本地库HEAD指向的版本比远程库的要旧
  • 要使用强制命令
方法二:git revert

原理:

  • git revert是用于“反做”某一个版本,以达到撤销该版本的修改的目的。
  • 比如,我们commit了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有bug),想要撤销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的东西,但撤销了版本二的东西。

适用场景

  • 如果我们想撤销之前的某一版本,但是又想保留该目标版本后面的版本,记录下这整个版本变动流程,就可以用这种方法。

具体实现:

  • 1.查看 使用命令git log查看commit提交记录;
  • 2.使用git revert 版本号反做,并使用git commit -m 版本名提交;
    • 注意: 左开右闭
  • 3.使用git push推上远程库.
方法三:git rebase -i (commit-id)git rebase -i HEAD~(回退几步)
  • 1.查看
    • 使用命令“git log”查看commit提交记录在这里插入图片描述
  • 2.git rebase -i (commit-id)选择回退版本之前的一次commit版本
  • 3.编辑文件命令行输入 a,切换到编辑模式 将要删除的commit之前的 pick 单词改为drop
    • 使用drop是恢复之前的版本,使用squash是合并提交
      在这里插入图片描述
  • 4.按下Esc(退出)键 命令行输入 :wq 保存文件退出
  • 5.使用git push -f强制提交到远程更改
    在这里插入图片描述
  • 6.在使用 git rebase -i (commit-id) 过程中若撤销回退可以使用 git rebase --abort 撤回
    • 若在 git rebase后出现(xxx|REBASE-i)这种情况
      在这里插入图片描述
    • 原因:用git推送数据到仓库的时候会出现推送失败
    • 解决方案: 使用git rebase --abort 代码回退 回到git rebase之前的状态
3、这几种方法的区别
  • git revert 和 git reset的区别
  1. git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit。
  2. 在回滚这一操作上看,效果差不多。但是在日后继续merge以前的老版本时有区别。
    • 因为git revert是用一次逆向的commit“中和”之前的提交,因此日后合并老的branch时,导致这部分改变不会再次出现,但是git reset是之间把某些commit在某个branch上删除,因而和老的branch再次merge时,这些被回滚的commit应该还会被引入。 (踩过坑)
    • 看自己的使用场景,选择合适的命令
4、回退后,后悔了怎么办

Git 的版本回退速度非常快,因为 Git 在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git 仅仅是把HEAD从指向add distributed:然后顺便把工作区的文件更新了。所以你让HEAD指向哪个版本号,你就把当前版本定位在哪。

现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的commit id怎么办?

  • 好在 Git 提供了一个命令git reflog用来记录你的每一次命令,当你用git reset --hard HEAD^回退到wrote a readme file版本时,再想恢复到add distributed,就可以通过git reflog命令找到add distributed的commit id。
$ git reflog
50ed06b (HEAD -> master) HEAD@{0}: reset: moving to HEAD~
e55063a HEAD@{1}: reset: moving to HEAD
e55063a HEAD@{2}: commit: add distributed
50ed06b (HEAD -> master) HEAD@{3}: commit (initial): wrote a readme file
ESC

从上面的输出可以看到,add distributed的commit id是e55063a,现在,我们就可以通过 git reset --hard e55063a 切换到最新的版本上了。

4.7、简单对比git pull和git pull --rebase的使用 常用

使用下面的关系区别这两个操作:

git pull = git fetch + git merge
git pull --rebase = git fetch + git rebase

现在来看看git merge和git rebase的区别。
假设有3次提交A,B,C。
在这里插入图片描述
在远程分支origin的基础上创建一个名为"mywork"的分支并提交了,同时有其他人在"origin"上做了一些修改并提交了。
在这里插入图片描述
其实这个时候E不应该提交,因为提交后会发生冲突。如何解决这些冲突呢?有以下两种方法:

1、git merge
用git pull命令把"origin"分支上的修改 pull 下来与本地提交合并(merge)成版本M,但这样会形成图中的菱形,让人很困惑。
在这里插入图片描述
2、git rebase
创建一个新的提交R,R的文件内容和上面M的一样,但我们将E提交废除,当它不存在(图中用虚线表示)rebase的好处是避免了菱形的产生,保持提交曲线为直线,让大家易于理解。
在这里插入图片描述

  • 在rebase的过程中,有时也会有conflict,这时Git会停止rebase并让用户去解决冲突,解决完冲突后,用git add命令去更新这些内容,然后不用执行git -commit,直接执行git rebase --continue,这样git会继续apply余下的补丁。
    • 使用 idea 提供的 git 工具,更方便些
  • 在任何时候,都可以用 git rebase --abort 参数来终止rebase的行动,并且mywork分支会回到rebase开始前的状态。

4.8、合并提交的两种方式

git commit --amend 做的是一边提交一边合并

  1. 我们新开一个分支并且将新开发的功能模块提交后有如下的log在这里插入图片描述
    从图中的红框可以看出该提交新添加了一个文件。这时当我们发现新写的模块有问题时,我们需要更改代码重新提交,但是又不希望有新的提交记录,可以安装下面操作进行:
  2. 修改需要修改的代码,这里为了方便解释,添加了一个新的文件叫做fixed_bug.txt
  3. git add fixed_bug.txt
  4. git commit --amend 执行这一行能看见下图所示,里面你可以更改提交所需要的日志语句,也就是类似于git commit -m "(log)"语句log里面的内容,也可以不修改,这里改成”add new module and fixed a bug“
    在这里插入图片描述
    经过上面操作可以看见如下面所示的log记录,可以看出提交记录没有多,在同一个提交里面叫新的文件提交进去了,并且提交的描述也有所变化。
    在这里插入图片描述
    对于这个新功能你改了多次就重复这个操作多次就可以了,只会产生一个提交
  • git rebase -i 是将已提交的提交合并
    在这里插入图片描述
    因为"fixed bug"的这些提交都是修的新添加功能的bug,现在需要将"fixed bug"的这些提交都合并到"add new module"这个提交里面,操作如下:
  1. git rebase -i 518e73。这里需要注意的一点是,你想将哪些提交合并,那你-i后面跟的提交号,应该是这些提交中最早的那个提交的前一个提交,比如你想将所有的fixedbug提交和add new module这个提交合并,那你需要在git rebase -i后面写的提交号是add new module这个提交的前一个提交的提交号,即first commit的提交号。
    在这里插入图片描述
  2. 编辑提交文件,设置需要合并的提交。执行完第一步后你可以看到下面第一幅图所示的界面。上面的红框里面为你之前的提交信息,下面的红框告诉你怎么编辑这些提交,如果我们要将多余的提交压缩到最早的提交里面,也就是说需要将"fixed bug"那些提交合并至"add new module"里面,变成一个提交。编辑方式如下,将待合并的提交前面的pick改成s即可,如下面第二幅图所示。然后保存并退出该界面。
    在这里插入图片描述
    在这里插入图片描述
  3. 修改提交信息。执行第二步后可以看到下面第一幅图所示界面,红框里面的就是之前的提交信息。你可以不修改,也可以按照自己想要的提交信息修改,下面我们删掉除"add new module"以外的所有提交信息,如下面第二幅所示。
    在这里插入图片描述
    在这里插入图片描述
    这样合并提交就完成了,我们可以看到最后的log信息如下图。可以看出提交合并成了一个,并行描述信息也只有"add new module",但是修改文件都在这个提交中提交了
    在这里插入图片描述

5、git 高阶用法(定制提交/回滚/查看历史/暂存/清理)

这份小抄的主题是 git 的一些「高级」用法。
在这里插入图片描述

5.1、导航–跳到之前的分支

  • git checkout -

5.2、查看历史

  • git log 查看历史记录(最详细)
  • git log --pretty=oneline(每个提交在一行内显示)
  • git log --oneline(每个提交在一行内显示,hash值缩短了)
    • 常用
    • 空格向下翻页 b向上翻页 q退出
  • git reflog(可以查看移动到当前版本需要移动几步)
    • 空格向下翻页 b向上翻页 q退出

5.3、在所有提交日志中搜索包含「homepage」的提交

  • git log --all --grep=‘homepage’

5.4、获取某人的提交日志

  • git log --author=“qiwenjie”

5.5、之前重置了一个不想保留的提交,但是现在又想要回滚?

  • 获取所有操作历史 git reflog
  • 然后重置到相应提交 git reset HEAD@{4}
  • 或者git reset --hard <提交的哈希值>

5.6、哎哟:我把本地仓库搞得一团糟,应该怎么清理?

  • 第一步:git fetch origin
  • 第二步:git checkout master
  • 第三步:git reset --hard origin/master

5.7、查看我的分支和 master 的不同

  • git diff master…my-branch 比较文件差异
  • git diff [文件名] 将工作区中的文件和暂存区进行比较
  • git diff [本地库中历史版本] [文件名] 将工作区中的文件和本地库历史记录比较 不带文件名可以比较多个文件
适用场景:代码上线前,比对开发分支与master分支差异

避免造成线上故障的方法之一:比对开发分支与master分支差异

  • 选中项目,然后如下图操作
    在这里插入图片描述

5.8、定制提交

  • 编辑上次提交 git commit --amend -m “更好的提交日志”

5.9、在上次提交中附加一些内容,保持提交日志不变

  • 第一步:git add .
  • 第二步:git commit --amend --no-edit

5.10、空提交–可以用来重新触发 CI 构建

  • git commit --allow-empty -m “chore: re-trigger build”

5.11、squash 提交 比方说我想要 rebase (变基)最近 3 个提交

  • git rebase -i HEAD~3 保留第一行的 pick,剩余提交替换为 squash 或 s

5.12、修正提交信息

比方说想在提交 fed14a4c 加上一些内容。
在这里插入图片描述

git 提交分支
git add .
git commit --fixup HEAD~1
# 或者也可以用提交的哈希值(fed14a4c)替换 HEAD~1

git rebase -i HEAD~3 --autosquash
# 保存并退出文件(VI 中输入 `:wq`)

rebase 的时候在每个提交上执行命令

  • 如果特性很多,一个分支里可能有多个提交。如果测试失败了,你希望能找到导致测试失败的提交。这时候你可以使用 rebase --exec 命令在每个提交上执行命令。
# 在最近 3 个提交上运行 `npm test` 命令
git rebase HEAD~3 --exec "npm test"

在这里插入图片描述

5.13、暂存

  • 暂存不止有 git stashgit stash pop
# 保存所有正在追踪的文件
git stash save "日志信息"

# 列出所有的暂存项
git stash list

# 获取并删除暂存项
git stash apply stash@{1}
git stash drop stash@{1}

# ……或使用一条命令……
git stash pop stash@{1}

5.14、清理

# 移除远程仓库上不存在的分支
git fetch -p

# 移除所有包含 `greenkeeper` 的分支
git fetch -p 
git branch --remote | fgrep greenkeeper | sed 's/^.\{9\}//' | xargs 
git push origin --delete

GitHub = Git + Hub
我把 Hub 当成 git 的一个封装来用。你如果也想这么做,可以设置一个别名:alias git=‘hub’

# 打开浏览器访问仓库 url(仅限 GitHub 仓库)
git browse

6、git 别名(可提高开发效率)

序号命令作用
1alias g=‘git’
2alias glog=‘git log --oneline --decorate —graph’
3alias gst='git status’
4alias gp='git push’
5alias ga='git add .’
6alias gc='git commit —m’
7alias yolo=‘git push —force’
8alias gpull=‘git pull’

7、git 各分支作用

7.1、分支的基本操作

  • 分支是用来将特性开发绝缘开来的。
  • 在你创建仓库的时候,master 是“默认的”分支。在其他分支上进行开发,完成后再将它们合并到主分支上。
  • 我们通常在开发新功能、修复一个紧急 bug 等等时候会选择创建分支。单分支开发好还是多分支开发好,还是要看具体场景来说。

创建一个名字叫做 test 的分支

  • git branch test

切换当前分支到 test

  • 当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。 Git 会自动添加、删除、修改文件以确保此时你的工作目录和这个分支最后一次提交时的样子一模一样
  • git checkout test

在这里插入图片描述

你也可以直接这样创建分支并切换过去(上面两条命令的合写)

  • git checkout -b feature_x
# 切换到主分支
git checkout master

# 合并分支(可能会有冲突)
git merge test

# 把新建的分支删掉
git branch -d feature_x

#将分支推送到远端仓库(推送成功后其他人可见):
git push origin 

7.2、git 各分支作用及使用时机

分支使用时机注意事项
hotfix/问题描述紧急修复bug-
feature/功能个人开发的功能/模块-
release/日期本期要开发的所有需求,在feature上开发完毕后合并到该分支-
master主分支,拉取最新分支,直至最终合并代码-

7.3、实战场景 使用git 将 master 分支合到自己的开发分支

  • 背景:一般开发自己的分支都是从最新的master上拉取,但中间master会有改动(可能有其他团队提交过代码),此时需要将最新的master合到自己的分支中
    命令:
# 1. 查看当前的分支,星号标识为当前分支;(如果查询结果有master分支,则跳到第4步)
git branch

# 2. 查看该工程所有的分支,星号标识为当前分支
git branch -a

# 3. 切到master分支
git checkout master

# 4. 拉取master最新代码
git pull origin master

# 5. 切到你的分支
git checkout feature/20201126

# 6. 将master meger到你的分支中
git merge master 或者是 git merge --no-ff master

# 7. 将你的分支从本地push到远程
git push

7.4、实战场景2 git 合并时 --no-ff 的作用

在许多介绍 git 工作流的文章里,都会推荐在合并分支时,加上 --no-ff 参数:

 git checkout develop
 git merge --no-ff feature

–no-ff 在这的作用是禁止快进式合并。–no-ff是什么意思??(强行关闭fast-forward方式)

Git 合并两个分支时,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合并两者时,只会简单地把指针右移,叫做“快进”(fast-forward),比如下图:

          A---B---C feature
         /
D---E---F master

要把 feature 合并到 master 中,执行以下命令

$ git checkout master
$ git merge feature

结果就会变成

          A---B---C feature
         /          master
D---E---F 

因为 feature 就在 master 的下游,所以直接移动了 master 的指针,master 和 feature 都指向了 C。而如果执行了 git merge --no-ff feature 的话,是下面的结果:

          A---B---C feature
         /         \
D---E---F-----------G master

由于 --no-ff 禁止了快进,所以会生成一个新的提交,master 指向 G

从合并后的代码来看,结果其实是一样的,区别就在于 --no-ff 会让 Git 生成一个新的提交对象。为什么要这样?

  • 通常我们把 master 作为主分支,上面存放的都是比较稳定的代码,提交频率也很低,而 feature 是用来开发特性的,上面会存在许多零碎的提交,快进式合并会把 feature 的提交历史混入到 master 中,搅乱 master 的提交历史。所以如果你根本不在意提交历史,也不爱管 master 干不干净,那么 --no-ff 其实没什么用。不过,如果某一次 master 出现了问题,你需要回退到上个版本的时候,比如上例,你就会发现退一个版本到了 B,而不是想要的 F,因为 feature 的历史合并进了 master 里

7.5、git merge --no-ff 和 git merge --squash 有什么区别?

  • fast-forward方式就是当条件允许的时候,git直接把HEAD指针指向合并分支的头,完成合并。属于“快进方式”,不过这种情况如果删除分支,则会丢失分支信息。因为在这个过程中没有创建commit

    • 不推荐
  • git merge --squash 是用来把一些不必要commit进行压缩,比如说,你的feature在开发的时候写的commit很乱,那么我们合并的时候不希望把这些历史commit带过来,于是使用–squash进行合并,此时文件已经同合并后一样了,但不移动HEAD,不提交。需要进行一次额外的commit来“总结”一下,然后完成最终的合并。

  • 总结:

    • –no-ff: 不使用fast-forward方式合并,保留分支的commit历史
    • –squash: 使用squash方式合并,把多次分支commit历史压缩为一次
      在这里插入图片描述

7.6、实战场景4 删除本地那些远程仓库不存在的分支

# 查看所有本地分支和远程分支
git branch -a 

# 只查看远程分支
git branch -r

# 查看remote地址,远程分支,还有本地分支与之相对应关系等信息。
git remote show origin

此时我们可以看到那些远程仓库已经不存在的分支
在这里插入图片描述

# 删除本地那些远程仓库不存在的分支
git remote prune origin

8、使用git中造成的故障review 20201226

1、在日常开发中,使用 git 的建议

  • 1、在合并release分支,或者上生产环境之前,全局比对开发分支代码与master分支的差异,避免线上故障。
  • 2、不要反向合并代码,因为某些代码会比你上线晚;不要删除还未上线的代码并提交,会导致同事在合并代码时,因此git的head指针滞后于master分支,合并代码后代码会丢失。

场景1:git merge时代码合并丢失,这是为啥?

我的使用方式:首先在自己的feature分支或release分支上开发完代码,先pull最新的代码,然后提交;然后切换到公共分支,使用git merge --no-ff feature/my分支,如果遇到冲突,使用idea自带的客户端工具merge代码,偷懒时会使用idea自带的工具来合并没有冲突的代码,如果没有冲突,push代码,完毕。

原因:暂时未知

最规范的方法,只在自己独有的分支上写代码,不要在合并分支里面写代码提交。 这样不会出任何问题。

场景2:.gitignore 造成的问题,丢失了一天的开发量

  • 刚刚犯了一个错,将文件夹命名为log,而该文件名在gitignore中配置过了,导致了整个文件夹都没有被git追踪,而且我还把本地文件删除了。我TMD。

.gitignore使用注意事项
1、要注意ignore忽略的文件如果已经在git仓库中存在,那么ignore将会不起作用

  • 不能在已经在版本库管理下的文件为忽略目标,如果需要必须在版本库中删除对应文件才可以执行忽略。

2、配置规则

  • # 开头的文件标识注释,可以使用反斜杠进行转义
  • ! 开头的模式标识否定,该文件将会再次被包含,如果排除了该文件的父级目录,则使用 ! 也不会再次被包含。可以使用反斜杠进行转义
  • / 结束的模式只匹配文件夹以及在该文件夹路径下的内容,但是不匹配该文件 tmd整个文件夹下的文件都没有提交
  • / 开始的模式匹配项目跟目录
  • 如果一个模式不包含斜杠,则它匹配相对于当前 .gitignore 文件路径的内容,如果该模式不在 .gitignore 文件中,则相对于项目根目录
  • ** 匹配多级目录,可在开始,中间,结束
  • ? 通用匹配单个字符
  • [] 通用匹配单个字符列表

场景3:提交错了分支,如何弥补?

使用cherry-pick + git rebase 来回滚操作

  • cherry-pick作用:将指定的提交应用于其他分支
  • git rebase作用:回滚代码
    举例来说,代码仓库有master和feature两个分支。
a - b - c - d   Master
         \
           e - f - g Feature

现在将提交f应用到master分支
步骤1、切换到 master 分支

  • $ git checkout master

步骤2、Cherry pick 操作

  • $ git cherry-pick f

上面的操作完成以后,代码库就变成了下面的样子。

a - b - c - d - f   Master
         \
           e - f - g Feature

转移多个提交
Cherry pick 支持一次转移多个提交。

  • $ git cherry-pick 将 A 和 B 两个提交应用到当前分支
  • $ git cherry-pick A…B 转移从 A 到 B 的所有提交,提交 A 将不会包含在 Cherry pick 中
  • $ git cherry-pick A^…B hashA不包含,hashB包含

使用 git rebase -i (commit-id) 或 git rebase -i HEAD~(回退几步)

1.查看 使用命令“git log”查看commit提交记录
2.git rebase -i (commit-id) 选择回退版本之前的一次commit版本
3.编辑文件命令行输入 a,切换到编辑模式 将要删除的commit之前的 pick 单词改为drop
4.按下Esc(退出)键 命令行输入 :wq 保存文件退出
5.使用 “git push -f ” 提交到远程更改
6.在使用 git rebase -i (commit-id) 过程中若撤销回退可以使用 git rebase --abort 撤回

注意事项

  • 比对代码,避免删除其他人的代码 使用git diff比较两次提交的差异
  • git diff HEAD 显示工作目录与git 仓库之间的差异;
  • git diff HEAD^ 是比较上次的提交;
  • git diff HEAD~ 2 是比较上两次提交,于是有了,git diff HEAD~n 是比较上n次提交与现在工作区间的关系

场景4:git从某个 commit 提交,检出为新的分支

从某一个 commit 开始创建本地分支

  • 1、git log 查看提交
  • 2、通过checkout 跟上commitId 即可创建制定commit之前的本地分支
    • git checkout commitId -b 本地新branchName
  • 3、上传到远程服务器
    • git push origin HEAD:远程新branchName
  • 非常实用的技巧

场景5:合并代码错误导致的问题

场景:某次合并代码时,不小心将test环境合并分支的代码合到了自己的本地分支,由于本地分支涉及两个需求的大量代码,并且提交多次,因此没有新建分支,重新提交代码,又由于test合并分支的合并时,冲突巨多,因此没有选择reset代码。我是这样做的,从master拉一个新分支,然后比对本地分支和这个新分支的代码差异,将上线时间比自己晚的代码移除掉,然后顺利上预发,发布真线了。
但是,被移除代码的同事,在合并test分支代码时,遇到了问题,合并后,代码无冲突,而且代码在合并后丢了。

原因:git 提交记录使用指针在记录,本地的指针是滞后于master分支指针的,合并后本地代码就会被master分支的代码覆盖

解决方案:从master新拉特性分支,然后比对本地分支和特性分支代码差异,将代码挪到特性分支上,即可。

场景6:git 如何取消当前合并时的冲突

背景:有人对这个分支进行了代码文件进行改动,一般为合并分支时,并提交push;其他人使用这个分支时,出现merging状态并显示很多冲突文件。
解决方案:

  • 1、取消这次合并
    • git merge --abort #丢弃当前合并
  • 2、回退该分支的正确的版本号
    • git reflog #查询回退到的版本号
    • git reset --hard f82cfd2 #回退到这个版本号

参考资料
1、https://blog.csdn.net/zombres/article/details/82179122
2、https://blog.csdn.net/weixin_44781409/article/details/107560533
3、http://www.ruanyifeng.com/blog/2020/04/git-cherry-pick.html


版权声明:本文为qq_28959087原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。