Git常用命令总结

248 阅读20分钟

Git 区域理解

  • 远程仓库区:也就是我们代码最终提交的归宿,没啥好说的。
  • 远端分支本地副本:这个其实主要储存了远程仓库各分支数据在本地的一个副本,你可以打开你 Git 项目下的 .git 文件,里面有个 refs/remotes,这里就主要存的就是远程仓库的分支信息,一般你执行 push 或者 pull、fetch 都会往这里进行更新。
  • 本地分支:这里就是我们经常会打交道的区域,你在执行 commit 之后,本质上就是提交到了这个区域,你可以查看你的 .git 目录下的 refs/heads 目录,里面存的就是我们本地的分支代码信息。
  • 暂存区:这个区域就是我们每次执行 git add 之后会存到的区域,用来与本地仓库之间做一个缓存,同时也是 Git 底层设计上来说也算是比较重要的一个区域,它能帮助 Git 在做 diff 的时候提高查找性能。
  • 工作区:这个一般就是我们写代码的地方,比如你的 vscode 打开的项目,你可以进行代码编辑的地方。

stash

除此之外,还有一个特殊的区域,那就是本地的 git 储存区,它是用来干嘛的呢?一般来说你可能在某些场景下会用到它,我们有的时候本地改了代码,但是突然有个人过来问你另一个分支的问题,同时这个时候你在实现某个功能,实现一半,又不想提交到 Git 仓库中,那么你就可以考虑使用 git stash save "临时存一下",这个时候它就会帮你存到这个储存区,你去其他分支做完事情回来,再 git stash pop就好了。

  • git stash save 'xxx': 储存变更
  • git stash list: 查看储存区所有提交列表
  • git stash pop: 弹出并应用最近的一次储存区的代码提交
  • git stash drop stash@{n}: 删除某次储存记录
  • git stash clear: 清楚所有 stash 信息
  • git stash apply stashname 恢复指定贮藏代码到工作区和缓存区,会保留stashid

Git 简单工作流理解

日常工作中,我们可能在 Git 使用上频繁交互的流程大致会是这样的(不同规范下会有一些区别,但是大差不大):

  • 每次开发前确保本地master是和远程master同步的(在本地master执行git pull),然后基于master新建本地分支如yjj-feat,
  • 本地代码开发完以后git push推送到gitlab仓库,然后切换到本地test分支,执行git pull 保证本地test是最新test分支的代码(和远程test同步),
  • 然后把本地开发分支yjj-feat的修改合并到本地test分支,在本地test分支执行git merge yjj-feat有冲突就解决冲突,然后把本地test分支推送到远程test进行功能测试,
  • 如果有bug就切回本地开发分支yjj-feat进行修复,之后再git push推送到远程,之后再切换到本地test进行合并(git merge yjj-feat),并将本地test新的更改推到远程test
  • 测试环境测试完毕之后,就准备将本地分支的更改Merge Request到远程master了,首先切换到本地master 执行git pull保证本地master为最新,然后切换到本地开发分支yjj-feat执行git merge master将最新的master代码同步到开发分支(避免冲突),最后git push推送到远程,新建Merge Request到origin master

Git 工作流参考

- 开发阶段,示例如下
// 切换到 master 分支
git checkout master 

// 拉取最新的 master 代码
git pull origin master 

//新增并切换本地开发分支 yjj-feat-0629-apply
git checkout -b yjj-feat-0629-apply 

// 推送本地分支 yjj-feat-0629-apply 至远程分支 origin/yjj-feat-0629-apply 
git push --set-upstream origin yjj-feat-0629-apply

// 开发完成,切换至 本地 test 分支
git checkout test

// 拉取最新的 test 分支代码
git pull origin test

// 合并开发分支 yjj-feat-0629-apply 至 test 分支
git merge yjj-feat-0629-apply

// 解决 merge 时产生的冲突 重新提交commit 再次推送至 test 远程分支
git push origin test

// 观察仓库的CI/CD状态,查看测试环境是否部署成功 
// 提测过程中需要修复解决问题,需要切换回 开发分支进行修复,完成后重新合并至 test 分支上线部署测试环境


项目上线 PR 步骤
- 代码开发完成,准备上线工作
- 切换至开发分支 :git checkout yjj-feat-0629-apply
- 查看提交日志:git log
- 复制创建分支时的 commit id 如图所示

- 执行commit 合并操作:git rebase -i f2e1dbca2040b90e68246a3f4c6677f31f2bf427 
- 然后进入如图所示的界面 按 i 键进入编辑模式 
- 修改后续的 pick 为 s 然后按 esc 键退出编辑模式
- 然后按 :wq 保存修改


命令说明
 pick:保留该commit(缩写:p)
 reword:保留该commit,但我需要修改该commit的注释(缩写:r)
 edit:保留该commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e)
 squash:将该commit和前一个commit合并(缩写:s)
 fixup:将该commit和前一个commit合并,但我不要保留该提交的注释信息(缩写:f)
 exec:执行shell命令(缩写:x)
 drop:我要丢弃该commit(缩写:d)
- 再次查看: git log 已合并为一条commit

- 拉取最新的分支代码 git pull origin yjj-feat-0629-apply
- 推送到远程开发分支 git push origin yjj-feat-0629-apply
- 打开项目git仓库,选择 new merge requests

- 勾选要合并的 yjj-feat-0629-apply 开发分支,  以及目标分支 master 。然后点击继续


- 编辑本次 PR 的标题,代码变更描述,以及指定过 PR 的同学。然后点击提交即可


- 负责 PR 审查的同学进入 merge requst 选项,审查改动的代码,选择通过或者驳回(说明驳回理由)

git如何把多次commit合并成一次提交

1.用rebase把commit合并 , git rebase -i commitid 进入,这里的commitid需要查看git tree 然后找到本次提交分支头的前一次提交的id,进入后把除第一个分支前的pick都改成s表示和第一次提交合并

问题1

问题描述:

1.在一个新项目第一次开始开发时,执行正常的操作流程----基于本地master新建本地dev,且在本地dev执行git pull origin dev时,在本地会出现大量变更。

原因分析:这是因为第一次本地dev是由本地master切出来的,且当前master版本和线上dev版本差别较大,导致了本地dev和origin dev差距较大,导致出现了很多变更。

解决方法:1.可以重新切回master 先同步最新的master,之后再切回dev,merge变更,这样保证dev是最新的,再执行git pull origin dev

2.可以使用vscode上的GUI方式直接在第一次新建本地dev时就基于远程dev,这样就直接建立了联系,之后在每次提交时直接git pull即可保证当前dev和origin dev同步

结论:这次 Merge branch 'dev' of git.xxxxxxx. into dev并不是一个错误的提交,只是可以直接在本地dev更新到最新就不需要这次同步

问题2

问题描述:当发现dev莫名有很多变更即dev被污染了,

解决方法:可以选择直接把本地dev删了(切到master),重新在vscode GUI新建一个dev就好

将本地项目提交到github仓库步骤

  • 在github新建一个仓库
  • 本地git clone这个仓库,然后将要添加的代码写进去
  • git init
  • git commit -m "xxxx"
  • git branch -M main // 修改本地的默认分支为main 因为github默认分支由master改为main了
  • git remote add origin github.com/xxx.git
  • git push -u origin main

git remote

用于和远程仓库进行关系绑定处理等等操作。

  • git remote add: 添加一个远程版本库关联
  • git remote rm: 删除某个远程版本库关联

比如我们本地有个初始化好的仓库,同时还有一个创建好的远程空仓库,那么我们就可以执行一下操作让他们关联起来:

  1. git remote add origin xxx.git先添加到本地仓库
  2. git push -u origin master:表示把当前仓库的 master 分支和远端仓库的 master 分支关联起来,后面我们执行 push 或者 pull 都可以非常方便的进行操作了。

git branch

在拿到一个项目之后,你首先还是应该看一下当前仓库现在有哪些分支,不要待会创建新分支发现名字重复之类的问题,那这个时候我们就可以使用 git branch 来查看一下相关的分支了。

  • git branch:查看本地所有分支信息
  • git branch -r:查看远程仓库所有分支
  • git branch -a:查看本地和远程仓库所有分支
  • git push origin --delete [branch_name]: 删除远程分支

一般来说如果分支太多的话,还是建议使用可视化工具来查看分支信息,比如 vscode 或者 source tree 等软件等等。

当然 IDEA 也是可以的。

git checkout

如果我们想以当前分支为基准,创建一个新的分支并切换过去,可以使用如下命令。

  • 创建并切换到指定新分支:git checkout -b branch1

  • 基于远程分支创建一个本地分支: git checkout -b branch origin branch

git rm

这个其实也挺有用的,比如我们项目中有个文件叫 .env,这个文件是一个私有的,不能被提交到远程的,但是我们不小心提交到了本地仓库中,这个时候我们把这个文件添加到 .gitignore 文件中,表示需要被 git 忽略提交,但是由于我们已经提交到本地仓库了,所以如果不先从 git 仓库删除是没用的。

如果直接右键删除,那么这个文件的记录还是会被保存到远端仓库,别人还是能看得到你这个信息,所以我们需要先从 git 仓库中删掉这个文件才行。

  • git rm .env:执行完这个命令就表示 .env 文件从 git 仓库中删除了,配合 .gitignore 就能保证以后所有的 .env 文件变更都不用担心被提交到远程仓库。
  • git rm -r dist:如果我们要删除的是一个目录,那么加上 -r 参数就好了。

git push

接下来我们想要把刚创建好得分支推送到远端,一般来说我们可能会需要用到 git push,但我们这是个新分支,根本没和远端仓库建立任何联系,那么我们就需要加点参数,让他们关联上:

  • 推送分支并建立关联关系:git push --set-upstream origin branch1

完事之后我们可以再去远程仓库看一眼就会发现我们创建的新分支已经推上去了。接下来可能会有小伙伴要问了,那如果远端仓库已经有了这个分支名咋整?

这里就分两种:

  1. 一种就是你本地的代码和远端代码没有冲突的情况下,并且你本地有新增提交,那么你可以仍然执行上述命令,这样就会直接将当前本地分支合远程分支关联上,同时把你的改动提交上去。
  2. 另一种就是本地分支和远端分支存在冲突,这个时候你执行上述命令就会出现提示冲突,那么接下来就需要你先把远端当前分支的代码拉下来,解决一下冲突了,就需要用到 git pull 命令了。

git pull

通常情况下,如果当前分支已经和远端分支建立了联系,那么我们想要合并一下远端分支,只需要执行 git pull 就好了,不用带其他参数,但如果和上面提到的 git push 时产生了冲突,还没有建立联系的时候,我们就需要指定需要拉取哪个分支的代码下来进行合并了。

  • 拉取指定远端分支合并到本地当前分支:git pull origin branch1

这里的 origin 是我们对远端仓库的命名,想改也是可以的,不过一般都是用的 origin。

回到上面提到的冲突问题,我们可以直接使用 git pull 然后指定合并当前本地分支想要建立联系的远程分支,然后本地解决一下冲突,然后提交一下改动,再执行 git push --set-upstream origin branch1 命令就大功告成了。

git fetch

了解完上面描述的 git pull,命令之后,其实这个命令也很好理解了,特定时候,可能我们只是想把远端仓库对应分支的变更拉到本地而已,并不想自动合并到我的工作区(你当前正在进行代码变更的工作区),等晚些时候我写完了某部分的代码之后再考虑合并,那么你就可以先使用 git fetch

fetch 完毕之后,我提交了自己当前工作去的变更到本地仓库,然后想合并一下远端分支的更改,这个时候执行一下 git merge origin/[当前分支名](默认一般是用 origin 表示远端的分支前缀)即可。

git merge

合并指定分支代码到当前分支。一般来说,我们用的比较多的场景可能是,远端仓库 master 分支有变更了,同时这个时候我们准备提 MR 了,那么就需要先合一下 master 的代码,有冲突就解决下冲突,那这个时候我们可以做以下操作:

  1. 切到 master 分支,git pull 拉一下最新代码
  2. 切回开发分支,执行 git merge master 合并一下 master 代码

同理,上面介绍的 git merge origin/xxx 也是一样的用法。

  • git merge [branchName] 将branchName合并到当前分支

git log

顾名思义,就是日志的意思,执行这个命令之后,我们能看到当前分支的提交记录信息,比如 commitId 和提交的时间描述等等,大概长下面这样:

commit e55c4d273141edff401cbc6642fe21e14681c258 (HEAD -> branch1, origin/branch1)
Author: 陌小路 <44311619+STDSuperman@users.noreply.github.com>
Date:   Mon Aug 1 23:16:11 2022 +0800

    Initial commit
复制代码

这个时候可能有读者会问了,这个都用来干啥的,简单的用法呢就是看看有谁提交了啥,还有更重要的用法呢就是进行代码版本的回滚,或者其他有意思的操作,且听笔者为你微微道来。

git reset

  • git reset [--soft | --mixed | --hard] [HEAD]

关于 HEAD:

  • HEAD 表示当前版本
  • HEAD^ 上一个版本
  • HEAD^^ 上上一个版本
  • HEAD^^^ 上上上一个版本

参数解析

以下解析均基于后接参数为 HEAD^,也就是git reset HEAD^

  • --soft: 重置你最新一次提交版本,不会修改你的暂存区和工作区。
  • --mixed: 默认参数,用于重置暂存区的文件与上一次的提交(commit)保持一致,工作区文件内容保持不变。
  • --hard: 重置所有提交到上一个版本,并且修改你的工作区,会彻底回到上一个提交版本,在代码中看不到当前提交的代码,也就是你的工作区改动也被干掉了。

说了半天似乎不是很好理解,我们举个栗子理解下:

比如:

  1. 我改动了我的 README 文件,在我们的工作区就产生了一次改动,但是这个时候还没有提交到暂存区,在 vscode 里会显示为工作区修改的标记
  2. 接着我们执行 git add,这个时候你查看暂存区,会发现这次改动被提交进去了,同时被 vscode 标记为已被提交至暂存区
  3. 然后再执行 git commit,这个时候就完成了一次提交

接下来我们想撤回这次提交,以上三种参数所体现的表现会是这样的:

  • --soft:我们对 README 的更改状态现在变成已被提交至暂存区,也就是上面 2 的步骤。
  • --mixed: 我们对 README 的更改变成还未被提交至暂存区,也就是上面 1 的步骤。
  • --hard:我们对 README 的所有更改全没了,git log 中也找不到我们对 README 刚刚那次修改的痕迹。

默认情况下我们不加参数,就是 --mixed,也就是重置暂存区的文件到上一次提交的版本,文件内容不动。一般会在什么时候用到呢?

场景一

当你某个改动提交到本地仓库之后,也就是 commit 之后,这个时候你想撤回来,再改点其他的,那么就可以直接使用 git reset HEAD^。这个时候你会惊奇的发现,你上一版的代码改动,全部变成了未被提交到暂存区的状态,这个时候你再改改代码,然后再提交到暂存区,然后一起再 commit 就可满足你的需求了。

除了这种基础用法,我们还可以配合其他命令操作一下。

场景二

某一天你老板跟你说,昨天新加的功能不要了,给我切回之前的版本看看效果,那么这个时候,你可能就需要将工作区的代码回滚到上一个 commit 版本了,操作也十分简单:

  • git log 查看上一个 commit 记录,并复制 commitId
  • git reset --hard commitId 直接回滚。

场景三

如果某一个你开发需求正开心呢,突然发现,自己以前改的某个东西怎么不见了,你想起来好像是某次合并,没注意被其他提交冲掉了,你心一想,完了,写了那么多,怎么办?很简单,回到有这份代码的那个版本就好了(前提你提交过到本地仓库)。

假设我们有这么两个提交记录,我们需要下面那个 365 开头 commitId 的代码:

commit e62b559633387ab3a5324ead416f09bf347d8e4a (HEAD -> master)
Author: xiaohang.lin <xiaohang.lin@alibaba-inc.com>
Date:   Sun Aug 14 18:08:56 2022 +0800

    merge

commit 36577ea21d79350845f104eee8ae3e740f19e038 (origin/master, origin/HEAD)
Author: 陌小路 <44311619+STDSuperman@users.noreply.github.com>
Date:   Sun Aug 14 15:57:34 2022 +0800

    Update README.md
复制代码
  1. 抢救第一步 git log 找到有你这个代码的那个 commitId(也就是 36577ea21d79350845f104eee8ae3e740f19e038)
  2. 抢救第二步 git reset --hard commitId
  3. 第三步:Ctrl + c 你的目标代码

这个时候你想把复制好的代码写回去,该怎么办呢,你可能会再 git log 看一下我们 reset 之前的 commitId,你会发现,完了,之前的 commitId 都没了,只有这个 365 了。

commit 36577ea21d79350845f104eee8ae3e740f19e038 (origin/master, origin/HEAD)
Author: 陌小路 <44311619+STDSuperman@users.noreply.github.com>
Date:   Sun Aug 14 15:57:34 2022 +0800

    Update README.md
复制代码

不要慌,请记住一句话,只要你不删你本地的 .git 仓库,你都能找回以前所有的提交。

git log 看不到的话,我们就可以祭出我们的绝招了:git reflog

36577ea (HEAD -> master, origin/master, origin/HEAD) HEAD@{0}: reset: moving to 36577ea21d79350845f104eee8ae3e740f19e038
e62b559 HEAD@{1}: reset: moving to e62b559633387ab3a5324ead416f09bf347d8e4a
复制代码

这里我们可以看到两行记录,一个是我们执行 reset 到 365 的记录,另一条不知道是啥,不重要,我们想回到我们刚刚 reset 之前的状态也很简单,直接复制它上一次的变动也就是这个 e62b559,然后执行 git reset --hard e62b559,然后你会惊奇的发现,你之前的代码又回来了。

接下来把你以前版本的代码,再 Ctrl + v 放进来就完成了。

git reflog

介绍:用来查看你的所有操作记录。

既然 git log 看不到我之前 commitId 了,那么就回到 reset 之前的状态吧!

git revert

当然了,如果是针对 master 的操作,为了安全起见,一般还是建议使用 revert 命令,他也能实现和 reset 一样的效果,只不过区别来说,reset 是向后的,而 revert 是向前的,怎么理解呢?简单来说,把这个过程当做一次时光穿梭,reset 表示你犯了一个错,他会带你回到没有犯错之前,而 revert 会给你一个弥补方案,采用这个方案之后让你得到的结果和没犯错之前一样。

举个栗子: 假设你改了 README 的描述,新增了一行文字,提交上去了,过一会你觉得这个写了有问题,想要撤销一下,但是又不想之前那个提交消失在当前历史当中,那么你就可以选择使用 git revert [commitId],那么它就会产生一次新的提交,提交的内容就是帮你删掉你上面新增的内容,相当于是一个互补的操作。

PS D:\Code\other\git-practice> git revert 3b18a20ad39eea5264b52f0878efcb4f836931ce
On branch branch2
Your branch is ahead of 'origin/branch2' by 1 commit.
  (use "git push" to publish your local commits)
复制代码

这个时候,它会提示你可以把新的改动 push 上去了。

其实你如果在 gitlab 进行 mr 之后,想要回滚这个 mr,一般它会给你一个 revert 的按钮选项,让你进行更安全的回滚操作。

scope

feat: 新功能、新特性 fix: 修改 bug

perf: 更改代码,以提高性能(在不影响代码内部行为的前提下,对程序性能进行优化)

refactor: 代码重构(重构,在不影响代码内部行为、功能下的代码修改)

docs: 文档修改

style: 代码格式修改, 注意不是 css 修改(例如分号修改)

test: 测试用例新增、修改

build: 影响项目构建或依赖项修改

revert: 恢复上一次提交

ci: 持续集成相关文件修改