Git之深入解析常用命令速查大全与Reset模式分析

Posted Forever_wj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Git之深入解析常用命令速查大全与Reset模式分析相关的知识,希望对你有一定的参考价值。

一、Git 工作流程

  • 四个区:
    • Workspace:工作区
    • Index / Stage:暂存区(和 git stash 命令暂存的地方不一样)
    • Repository:仓库区(或本地仓库)
    • Remote:远程仓库
  • 五种状态:
    • 未修改 Origin
    • 已修改 Modified
    • 已暂存 Staged
    • 已提交 Committed
    • 已推送 Pushed

① 工作区

  • 程序员进行开发改动的地方,是我们当前看到的,也是最新的。
  • 平常我们开发就是拷贝远程仓库中的一个分支,基于该分支进行开发。在开发过程中就是对工作区的操作。

② 暂存区

  • .git 目录下的 index 文件,暂存区会记录 git add 添加文件的相关信息(文件名、大小、timestamp,…),不保存文件实体,通过 id 指向每个文件实体。可以使用 git status 查看暂存区的状态,暂存区标记了当前工作区中,哪些内容是被 git 管理的。
  • 当完成某个需求或功能后需要提交到远程仓库,那么第一步就是通过 git add 先提交到暂存区,被 git 管理。

③ 本地仓库

  • 保存了对象被提交过的各个版本,比起工作区和暂存区的内容,它要更旧一些。
  • git commit 后同步 index 的目录树到本地仓库,方便从下一步通过 git push 同步本地仓库与远程仓库的同步。

④ 远程仓库

  • 远程仓库的内容可能被分布在多个地点的处于协作关系的本地仓库修改,因此它可能与本地仓库同步,也可能不同步,但是它的内容是最旧的。

⑤ 小结

  • 任何对象都是在工作区中诞生和被修改;
  • 任何修改都是从进入 index 区才开始被版本控制;
  • 只有把修改提交到本地仓库,该修改才能在仓库中留下痕迹;
  • 与协作者分享本地的修改,可以把它们 push 到远程仓库来共享。
  • 如下所示,直接阐述了四个区域之间的关系:

二、常用的 Git 命令

① HEAD

  • 在掌握具体命令前,先理解下 HEAD:

  • HEAD 始终指向当前所处分支的最新的提交点,所处的分支变化了,或者产生了新的提交点,HEAD 就会跟着改变。

② add

  • add 主要实现将工作区修改的内容提交到暂存区,交由 git 管理:
命令描述
git add添加当前目录的所有文件到暂存区
git add < dir>添加指定目录到暂存区,包括子目录
git add < file1>添加指定文件到暂存区

③ commit

  • commit 主要实现将暂存区的内容提交到本地仓库,并使得当前分支的 HEAD 向后移动一个提交点:
命令描述
git commit -m 提交暂存区到本地仓库,message 代表说明信息
git commit -m 提交暂存区的指定文件到本地仓库
git commit --amend -m 使用一次新的 commit,替代上一次提交

④ branch

  • 涉及到协作,自然会涉及到分支。关于分支,大概有四种操作:
    • 展示分支
    • 切换分支
    • 创建分支
    • 删除分支
  • 分支是用来标记特定代码的提交,每一个分支通过 SHA1sum 值来标识,所以对分支的操作是轻量级的,改变的仅仅是 SHA1sum 值:
命令描述
git branch列出所有本地分支
git branch -r列出所有远程分支
git branch -a列出所有本地分支和远程分支
git branch < branch-name>新建一个分支,但依然停留在当前分支
git checkout -b < branch-name>新建一个分支,并切换到该分支
git branch --track < branch>新建一个分支,与指定的远程分支建立追踪关系
git checkout < branch-name>切换到指定分支,并更新工作区
git branch -d < branch-name>删除分支
git push origin --delete < branch-name>删除远程分支

⑤ fetch

命令描述
git fetch <远程主机名>将某个远程主机的更新全部取回本地
git fetch <远程主机名> <分支名>取回特定分支的更新
git fetch origin master取回 origin 主机的 master 分支
  • 一旦远程主机的版本库有了更新(Git 术语叫做 commit),需要将这些更新取回本地,这时就要用到 git fetch 命令:
$ git fetch <远程主机名>
  • 上面命令将某个远程主机的更新,全部取回本地。git fetch 命令通常用来查看其他人的进程,因为它取回的代码对你本地的开发代码没有影响。默认情况下,git fetch 取回所有分支(branch)的更新。如果只想取回特定分支的更新,可以指定分支名。
  • 取回更新后,会返回一个 FETCH_HEAD,指的是某个 branch 在服务器上的最新状态,可以在本地通过它查看刚取回的更新信息:
$ git log -p FETCH_HEAD
  • 取回 origin 主机的 master 分支:
$ git fetch origin master
  • 所取回的更新,在本地主机上要用“远程主机名/分支名”的形式读取,比如 origin 主机的 master,就要用 origin/master 读取。git branch 命令的 -r 选项,可以用来查看远程分支,-a 选项查看所有分支:
$ git branch -r
origin/master

$ git branch -a
 * master
  remotes/origin/master
  • 上面命令表示,本地主机的当前分支是 master,远程分支是 origin/master。取回远程主机的更新以后,可以在它的基础上,使用 git checkout 命令创建一个新的分支。如下所示,在 origin/master 的基础上,创建一个新分支:
$ git checkout -b newBrach origin/master
  • 此外,也可以使用 git merge 命令或者 git rebase 命令,在本地分支上合并远程分支:
$ git merge origin/master
# 或者
$ git rebase origin/master
  • 上面命令表示在当前分支上,合并 origin/master。

⑥ merge

  • merge 命令把不同的分支合并起来,如上图,在实际开放中,我们可能从 master 分支中切出一个分支,然后进行开发完成需求,中间经过 R3、R4、R5 的 commit 记录,最后开发完成需要合入 master 中,这便用到了 merge。
命令描述
git fetch < remote>merge 之前先拉一下远程仓库最新代码
git merge < branch>合并指定分支到当前分支
  • 一般在 merge 之后,会出现 conflict,需要针对冲突情况,手动解除冲突,主要是因为两个用户修改了同一文件的同一块区域。
    • 中断合并:
git merge --abort
    • 撤销合并:撤销合并时采用 git reset/revert 操作。

⑦ rebase

  • rebase 又称为衍合,是合并的另外一种选择。在开始阶段,我们处于 new 分支上,执行 git rebase dev,那么 new 分支上新的 commit 都在 master 分支上重演一遍,最后 checkout 切换回到 new 分支。这一点与 merge 是一样的,合并前后所处的分支并没有改变。
  • git rebase dev,通俗的解释就是 new 分支想站在 dev 的肩膀上继续下去,rebase 也需要手动解决冲突。
  • rebase 与 merge 的区别:
    • 现在有这样的两个分支:test 和 master,提交如下:
      D---E test
     /

A---B---C---F master
    • 在 master 执行 git merge test,然后会得到如下结果:
      D--------E
     /          \\

A---B---C---F----G   test, master
    • 在 master 执行 git rebase test,然后得到如下结果:
A---B---D---E---C'---F'   test, master
    • 可以看到,merge 操作会生成一个新的节点,之前的提交分开显示,而 rebase 操作不会生成新的节点,是将两个分支融合成一个线性的提交。
  • 如果想要一个干净的,没有 merge commit 的线性历史树,那么应该选择 git rebase;如果想保留完整的历史记录,并且想要避免重写 commit history 的风险,应该选择使用 git merge。

⑧ reset

  • reset 命令把当前分支指向另一个位置,并且相应的变动工作区和暂存区:
命令描述
git reset —soft < commit>只改变提交点,暂存区和工作目录的内容都不改变
git reset —mixed < commit>改变提交点,同时改变暂存区的内容
git reset —hard < commit>暂存区、工作区的内容都会被修改到与提交点完全一致的状态
git reset --hard HEAD让工作区回到上次提交时的状态

⑨ revert

  • git revert 用一个新提交来消除一个历史提交所做的任何修改:
  • revert 与 reset 的区别:

  • 说明:
    • git revert 是用一次新的 commit 来回滚之前的 commit,git reset 是直接删除指定的commit;
    • 在回滚这一操作上看,效果差不多,但是在日后继续 merge 以前的老版本时有区别。因为 git revert 是用一次逆向的 commit “中和”之前的提交,因此日后合并老的 branch 时,导致这部分改变不会再次出现,减少冲突。但是 git reset 是之间把某些 commit 在某个 branch 上删除,因而和老的branch 再次 merge 时,这些被回滚的 commit 应该还会被引入,产生很多冲突。关于这一点,请参考:git reset revert 回退回滚取消提交返回上一版本
    • git reset 是把 HEAD 向后移动了一下,而 git revert 是 HEAD 继续前进,只是新的 commit 的内容和要 revert 的内容正好相反,能够抵消要被 revert 的内容。

⑩ push

  • 上传本地仓库分支到远程仓库分支,实现同步:
命令描述
git push < remote>上传本地指定分支到远程仓库
git push < remote> --force强行推送当前分支到远程仓库,即使有冲突
git push < remote> --all推送所有分支到远程仓库
  • git push 命令用于将本地分支的更新,推送到远程主机,它的格式与 git pull 命令相仿:
$ git push <远程主机名> <本地分支名>:<远程分支名>
  • 注意,分支推送顺序的写法是<来源地>:<目的地>,所以 git pull 是<远程分支>:<本地分支>,而 git push 是<本地分支>:<远程分支>。如果省略远程分支名,则表示将本地分支推送与之存在“追踪关系”的远程分支(通常两者同名),如果该远程分支不存在,则会被新建。
  • 将本地的 master 分支推送到 origin 主机的 master 分支,如果后者不存在,则会被新建:
$ git push origin master
  • 如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。删除 origin 主机的 master 分支,如下所示:
$ git push origin :master
# 等同于
$ git push origin --delete master
  • 如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略。将当前分支推送到 origin 主机的对应分支:
$ git push origin
  • 如果当前分支只有一个追踪分支,那么主机名都可以省略:
$ git push
  • 如果当前分支与多个主机存在追踪关系,则可以使用 -u 选项指定一个默认主机,这样后面就可以不加任何参数使用 git push。将本地的 master 分支推送到 origin 主机,同时指定 origin 为默认主机,后面就可以不加任何参数使用 git push:
$ git push -u origin master
  • 不带任何参数的 git push,默认只推送当前分支,这叫做 simple 方式。此外,还有一种 matching 方式,会推送所有有对应的远程分支的本地分支。Git 2.0 版本之前,默认采用 matching 方法,现在改为默认采用 simple 方式,如果要修改这个设置,可以采用 git config 命令:
$ git config --global push.default matching
# 或者
$ git config --global push.default simple
  • 还有一种情况,就是不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机,这时需要使用 --all 选项,将所有本地分支都推送到 origin 主机:
$ git push --all origin
  • 如果远程主机的版本比本地版本更新,推送时 Git 会报错,要求先在本地做 git pull 合并差异,然后再推送到远程主机。这时,如果一定要推送,可以使用 --force 选项。如下所示,使用 --force 选项,结果导致远程主机上更新的版本被覆盖,除非很确定要这样做,否则应该尽量避免使用 --force 选项:
$ git push --force origin 
  • 最后,git push 不会推送标签(tag),除非使用 --tags 选项:
$ git push origin --tags

⑪ pull

  • git pull 命令的作用是,取回远程主机某个分支的更新,再与本地的指定分支合并。它的完整格式稍稍有点复杂:
$ git pull <远程主机名> <远程分支名>:<本地分支名>
  • 比如,取回 origin 主机的 next 分支,与本地的 master 分支合并,需要写成下面这样:
$ git pull origin next:master
  • 如果远程分支是与当前分支合并,则冒号后面的部分可以省略:
$ git pull origin next
  • 上面命令表示,取回 origin/next 分支,再与当前分支合并。实质上,这等同于先做 git fetch,再做 git merge:
$ git fetch origin
$ git merge origin/next
  • 在某些场合,Git 会自动在本地分支与远程分支之间,建立一种追踪关系(tracking)。比如,在 git clone 的时候,所有本地分支默认与远程主机的同名分支,建立追踪关系,也就是说,本地的 master 分支自动“追踪” origin/master 分支。
  • Git 也允许手动建立追踪关系,如下,指定 master 分支追踪 origin/next 分支:
git branch --set-upstream master origin/next
  • 如果当前分支与远程分支存在追踪关系,git pull 就可以省略远程分支名。如下,本地的当前分支自动与对应的 origin 主机"追踪分支"(remote-tracking branch)进行合并:
$ git pull origin
  • 如果当前分支只有一个追踪分支,连远程主机名都可以省略。如下,当前分支自动与唯一一个追踪分支进行合并:
$ git pull
  • 如果合并需要采用 rebase 模式,可以使用 --rebase 选项。
$ git pull --rebase <远程主机名> <远程分支名>:<本地分支名>
  • 如果远程主机删除了某个分支,默认情况下,git pull 不会在拉取远程分支的时候,删除对应的本地分支。这是为了防止,由于其他人操作了远程主机,导致 git pull 不知不觉删除了本地分支。
    但是,我们可以改变这个行为,加上参数 -p 就会在本地删除远程已经删除的分支:
$ git pull -p
# 等同于下面的命令
$ git fetch --prune origin 
$ git fetch -p

⑫ stash

命令描述
git stash save “xx”执行存储,并添加备注,只执行 git stash 也是可以的,但查找时不方便识别
git stash list查看 stash 了哪些存储
git stash show显示做了哪些修改,默认 show 第一个存储,如果要显示其他存储,后面加stash@{$num},比如第二个 git stash show stash@{1}
git stash show -p显示第一个存储的改动,如果想显示其他存储,加上 stash@{$num},比如第二个:git stash show stash@{1} -p
git stash apply应用某个存储,但不会把存储从存储列表中删除,默认使用第一个存储,即 stash@{0},如果要使用其他的,git stash apply stash@{$num} , 比如第二个:git stash apply stash@{1}
git stash pop命令恢复之前缓存的工作目录,将缓存堆栈中的对应 stash 删除,并将对应修改应用到当前的工作目录下,默认为第一个 stash,即 stash@{0},如果要应用并删除其他 stash,命令:git stash pop stash@{$num},比如应用并删除第二个:git stash pop stash@{1}
git stash drop stash@{$num}丢弃 stash@{$num} 存储,从列表中删除这个存储
git stash clear删除所有缓存的stash

  • 注意:没有在 git 版本控制中的文件,是不能被 git stash 存起来的。如果新增了一个文件,直接执行 git stash 是不会存起来的,可以先执行 git add,再执行 git stash。这个时候,想切分支就再也不会报错有改动未提交了。
  • 如果要应用这些 stash,直接使用 git stash apply 或者 git stash pop 就可以再次导出来了。
  • 总结:
    • git add 只是把文件加到 git 版本控制里,并不等于就被 stash 起来了,git add 和 git stash 没有必然的关系,但是执行 git stash 能正确存储的前提是文件必须在 git 版本控制中才行。
    • 常规 git stash 的一个限制是它会一下暂存所有的文件。有时,只备份某些文件更为方便,让另外一些与代码库保持一致。一个非常有用的技巧,用来备份部分文件:
      • add 不想备份的文件;
      • 调用 git stash –keep-index,只会备份那些没有被 add 的文件;
      • 调用 git reset 取消已经 add 的文件的备份,继续自己的工作。

⑬ clone

  • 远程操作的第一步,通常是从远程主机克隆一个版本库,这时就要用到 git clone 命令:
$ git clone <版本库的网址>
  • 例如,克隆 jQuery 的版本库:
$ git clone https://github.com/jquery/jquery.git
  • 命令会在本地主机生成一个目录,与远程主机的版本库同名。如果要指定不同的目录名,可以将目录名作为 git clone 命令的第二个参数:
$ git clone <版本库的网址> <本地目录名>
  • git clone 支持多种协议,除了 HTTP(s) 以外,还支持 SSH、Git、本地文件协议等,如下:
$ git clone http[s]://example.com/path/to/repo.git/
$ git clone ssh://example.com/path/to/repo.git/
$ git clone git://example.com/path/to/repo.git/
$ git clone /opt/git/project.git 
$ git clone file:///opt/git/project.git
$ git clone ftp[s]://example.com/path/to/repo.git/
$ git clone rsync://example.com/path/to/repo.git/
  • SSH 协议还有另一种写法:
$ git clone [user@]example.com:path/to/repo.git/
  • 通常来说,Git 协议下载速度最快,SSH 协议用于需要用户认证的场合。

⑭ remote

  • 为了便于管理,Git 要求每个远程主机都必须指定一个主机名,git remote 命令就用于管理主机名。不带选项的时候,git remote 命令列出所有远程主机:
$ git remote
origin
  • 使用 -v 选项,可以参看远程主机的网址:
$ git remote -v
origin  git@github.com:jquery/jquery.git (fetch)
origin  git@github.com:jquery/jquery.git (push)
  • 上面命令表示,当前只有一台远程主机,叫做 origin,以及它的网址。克隆版本库的时候,所使用的远程主机自动被 Git 命名为 origin,如果想用其他的主机名,需要用 git clone 命令的 -o 选项指定:
$ git clone -o jQuery https://github.com/jquery/jquery.git
$ git remote
jQuery
  • 上面命令表示,克隆的时候,指定远程主机叫做 jQuery。git remote show 命令加上主机名,可以查看该主机的详细信息:
$ git remote show <主机名>
  • git remote add 命令用于添加远程主机:
$ git remote add <主机名> <网址>
  • git remote rm 命令用于删除远程主机:
$ git remote rm <主机名>
  • git remote rename 命令用于远程主机的改名:
$ git remote rename <原主机名> <新主机名>

⑮ 其他命令

命令描述
git status显示有变更的文件
git log显示当前分支的版本历史
git diff显示暂存区和工作区的差异
git diff HEAD显示工作区与当前分支最新 commit 之间的差异
git diff --cached查看到暂存区和本地仓库之间的差异
git diff master查看到master和本地仓库之间的差异
git cherry-pick < commit>选择一个 commit,合并进当前分支

三、Git Reset 三种模式

  • 使用 Git 时有可能 commit 提交代码后,发现这一次 commit 的内容是有错误的,那么有两种处理方法:
    • 修改错误内容,再次 commit 一次;
    • 使用 git reset 命令撤销这一次错误的 commit;
    • 第一种方法多一条 commit 记录;第二种方法,错误的 commit 不会被保留下来。
  • 一句话概括 git reset:
git reset:Reset current HEAD to the specified state // 让 HEAD 指针指向其他的地方
  • 例如,有一次 commit 不是很满意,需要回到上一次的 Commit 里面,那么这个时候就需要通过 reset,把 HEAD 指针指向上一次的 commit 的点,它有三种模式:soft、mixed、hard,如下所示:

  • 理解了这三个模式,对于使用这个命令很有帮助。在理解这三个模式之前,需要略微知道一点 Git 的基本流程,如下所示:
    • Working Tree 当前的工作区域;
    • Index/Stage 暂存区域,和 git stash 命令暂存的地方不一样,使用 git add xx,就可以将 xx 添加近 Stage 里面;
    • Repository 提交的历史,即使用 git commit 提交后的结果。

  • 简单叙述一下把文件存入 Repository 流程:
    • 刚开始 working tree、index 与 repository(HEAD) 里面的内容都是一致的:

    • 当 git 管理的文件夹里面的内容出现改动后,此时 working tree 的内容就会跟 index 及 repository(HEAD) 的不一致,而 Git 知道是哪些文件(Tracked File)被改动过,直接将文件状态设置为 modified (Unstaged files):
    • 当执行 git add 后,会将这些改变的文件内容加入 index 中 (Staged files),所以此时 working tree 跟 index 的内容是一致的,但与 repository(HEAD) 内容不一致:

    • 接着执行 git commit 后,将 Git 索引中所有改变的文件内容提交至 Repository 中,建立出新的 commit 节点(HEAD)后,working tree、index 与 repository(HEAD) 区域的内容又会保持一致:

① reset --hard:重置 stage 区和工作目录

  • reset --hard 会在重置 HEAD 和 branch 的同时,重置 stage 区和工作目录里的内容。当在 reset 后面加了 --hard 参数时,stage 区和工作目录里的内容会被完全重置为和 HEAD 的新位置相同的内容,换句话说,就是没有 commit 的修改会被全部擦掉:

  • 然后,执行 reset 并附上了 --hard 参数:
git reset --hard
  • HEAD 和当前 branch 切到上一条 commit 的同时,工作目录里的新改动和已经 add 到 stage 区的新改动也一起全都消失:

  • 可以看到,在 reset --hard 后,所有的改动都被擦掉。

② reset --soft:保留工作目录,并把重置 HEAD 所带来的新的差异放进暂存区

  • reset --soft 会在重置 HEAD 和 branch 时,保留工作目录和暂存区中的内容,并把重置 HEAD 所带来的新的差异放进暂存区。
  • 那么,什么是“重置 HEAD 所带来的新的差异”?如下所示:

  • 由于 HEAD 从 4 移动到了 3,而且在 reset 的过程中工作目录和暂存区的内容没有被清理掉,所以 4 中的改动在 reset 后就也成了工作目录新增的“工作目录和 HEAD 的差异”,这就是上面一段中所说的“重置 HEAD 所带来的差异”。
  • 此模式下会保留 working tree 工作目录的内容,不会改变到目前所有的 git 管理的文件夹的内容;也会保留 index 暂存区的内容,让 index 暂存区与 working tree 工作目录的内容是一致的。就只有 repository 中的内容的更变需要与 reset 目标节点一致,因此原始节点与 reset 节点之间的差异变更集合会存在与 index 暂存区中(Staged files),所以可以直接执行 git commit 将 index 暂存区中的内容提交至 repository 中。
  • 当我们想合并“当前节点”与“reset 目标节点”之间不具有太大意义的 commit 记录(可能是阶段性地频繁提交)时,可以考虑使用 Soft Reset 来让 commit 演进线图较为清晰点:

    • 同样的情况下,把修改后的 AppDelegate.h 文件 add 到 stage 区,修改后的 AppDelegate.m 保留在工作目录:

    • 查看当前最新的 commit 记录:
git show --stat

    • 这时,执行 git reset --soft HEAD^:

    • 那么除了 HEAD 和它所指向的 branch1 被移动到 HEAD^ 之外,原先 HEAD 处 commit 的改动(README.md 文件)也会被放进暂存区。
  • 这就是 --soft 和 --hard 的区别:–hard 会清空工作目录和暂存区的改动,而 --soft 则会保留工作目录的内容,并把因为保留工作目录内容所带来的新的文件差异放进暂存区。

③ reset --mixed:保留工作目录,并清空暂存区

  • reset 如果不加参数,那么默认使用 --mixed 参数,它的行为是:保留工作目录,并且清空暂存区。也就是说,工作目录的修改、暂存区的内容以及由 reset 所导致的新的文件差异,都会被放进工作目录。简而言之,就是把所有差异都混合(mixed)放在工作目录中。
  • 以上面的情况为例:

  • 工作目录的内容和 --soft 一样会被保留,但和 --soft 的区别在于,它会把暂存区清空,并把原节点和 reset 节点的差异的文件放在工作目录。总而言之,工作目录的修改、暂存区的内容以及由 reset 所导致的新的文件差异,都会被放进工作目录。

④ Reset 三种模式总结

  • reset 的本质:移动 HEAD 以及它所指向的 branch。实质上,reset 这个指令虽然可以用来撤销 commit,但它的实质行为并不是撤销,而是移动 HEAD ,并且捎带上 HEAD 所指向的 branch(如果有的话)。也就是说,reset 这个指令的行为其实和它的字面意思“重置”十分相符:它是用来重置 HEAD 以及它所指向的 branch 的位置的。
  • 而 reset --hard HEAD^ 之所以起到了撤销 commit 的效果,是因为它把 HEAD 和它所指向的 branch 一起移动到了当前 commit 的父 commit 上,从而起到了“撤销”的效果:

  • Git 的历史只能往回看,不能向未来看,所以把 HEAD 和 branch 往回移动,就能起到撤回 commit 的效果。所以同理,reset --hard 不仅可以撤销提交,还可以用来把 HEAD 和 branch 移动到其他的任何地方:
git reset --hard branch2

⑤ reset 三种模式区别和使用场景

  • 区别:
    • –hard:重置位置的同时,直接将 working Tree 工作目录、index 暂存区及 repository 都重置成目标 Reset 节点的内容,所以效果看起来等同于清空暂存区和工作区;
    • –soft:重置位置的同时,保留 working Tree 工作目录和 index 暂存区的内容,只让 repository 中的内容和 reset 目标节点保持一致,因此原节点和 reset 节点之间的【差异变更集】会放入 index 暂存区中(Staged files)。所以效果看起来就是工作目录的内容不变,暂存区原有的内容也不变,只是原节点和 Reset 节点之间的所有差异都会放到暂存区中。
    • –mixed(默认):重置位置的同时,只保留 Working Tree 工作目录的内容,但会将 Index 暂存区和 Repository 中的内容更改和 reset 目标节点一致,因此原节点和 Reset 节点之间的【差异变更集】会放入 Working Tree 工作目录中。所以效果看起来就是原节点和 Reset 节点之间的所有差异都会放到工作目录中。
  • 使用场景:
    • –hard:
      • 要放弃目前本地的所有改变时,即去掉所有 add 到暂存区的文件和工作区的文件,可以执行 git reset -hard HEAD 来强制恢复 git 管理的文件夹的内容及状态;
      • 真的想抛弃目标节点后的所有 commit(可能觉得目标节点到原节点之间的 commit 提交都是错了,之前所有的 commit 有问题)。
    • –soft:
      • 原节点和 reset 节点之间的【差异变更集】会放入 index 暂存区中(Staged files),所以假如之前工作目录没有改过任何文件,也没 add 到暂存区,那么使用 reset --soft 后,可以直接执行 git commit 将 index 暂存区中的内容提交至 repository 中。为什么要这样呢?
      • 这样做的使用场景是:假如想合并「当前节点」与「reset 目标节点」之间不具太大意义的 commit 记录(可能是阶段性地频繁提交,就是开发一个功能的时候,改或者增加一个文件的时候就commit,这样做导致一个完整的功能可能会好多个 commit 点,这时假如需要把这些 commit 整合成一个 commit 的时候)时,可以考虑使用 reset --soft 来让 commit 演进线图较为清晰。总而言之,可以使用 --soft 合并 commit 节点。
    • –mixed(默认):
      • 使用完 reset --mixed 后,可以直接执行 git add 将這些改变果的文件内容加入 index 暂存区中,再执行 git commit,将 Index 暂存区 中的内容提交至 Repository 中,这样一样可以达到合并 commit 节点的效果(与上面 --soft 合并 commit 节点差不多,只是多了 git add 添加到暂存区的操作);
      • 移除所有 Index 暂存区中准备要提交的文件(Staged files),可以执行 git reset HEAD 来 Unstage 所有已列入 Index 暂存区的待提交的文件(有时候发现 add 错文件到暂存区,就可以使用命令);
      • commit 提交某些错误代码,或者没有必要的文件也被 commit 上去,不想再修改错误再 commit (因为会留下一个错误 commit 点),可以回退到正确的 commit 点上,然后所有原节点和 reset 节点之间差异会返回工作目录,假如有个没必要的文件的话就可以直接删除了,再 commit 上去就 OK 了。

四、撤销修改

① 已修改,未暂存

  • 如果只是在编辑器里修改了文件,但还没有执行 git add .,这时候文件还在工作区,并没有进入暂存区,可以用:
git checkout .
  • 或者
git reset --hard
  • 来进行撤销操作。


  • 可以看到,在执行完 git checkout . 之后,修改已被撤销,git diff 没有任何内容了。git add . 的反义词是 git checkout .,做完修改之后,如果想向前走一步,让修改进入暂存区,就执行git add .,如果想向后退一步,撤销刚才的修改,就执行 git checkout .。

② 已暂存,未提交

  • 已经执行 git add .,但还没有执行 git commit -m “comment”:
git reset   // 退回到 git add . 之前,即本地文件处于已修改未暂存状态
git checkout .   // 撤销修改
  • 或者
git reset --hard
  • 可以发现两种情况都可以用同一个命令 git reset --hard 来完成,这个强大的命令,可以一步到位地把你的修改完全恢复到未修改的状态。

③ 已提交,未推送

  • 执行 git commit 后,代码已经进入了本地仓库:
git reset --hard origin/master
  • 还是这个 git reset --hard 命令,只不过这次多了一个参数 origin/master,正如上文中的,origin/master 代表远程仓库,既然已经污染了本地仓库,那么就从远程仓库把代码取回来。

④ 已推送

  • 如果执行了 git add -> git commit -> git push 了,这时代码已经进入远程仓库。如果想恢复的话,只需要先撤销本地修改,再强制 push 到远程仓库:
git reset --hard HEAD^
git push -f

五、同时 push 到多个远程仓库

  • 进入项目目录,打开 .git/config 文件(.git 是隐藏目录,需要打开显示隐藏文件):

  • 只需要在 [remote “origin”] 下增加一条 url 地址,就可以在 push 时推送到多个远程仓库:

  • 如果使用 sourceTree 软件进行 git 操作,那么需要注意在 settings -> 高级下面,多个远程地址对应的验证信息是否正确:

以上是关于Git之深入解析常用命令速查大全与Reset模式分析的主要内容,如果未能解决你的问题,请参考以下文章

超级级牛的Git常用命令清单大全,速查表

超级级牛的Git常用命令清单大全,速查表

Git操作手册|命令速查表

Git 常用命令大全

转 Git 常用命令大全

Git 学习深入理解git reset 命令