首页 > 其他分享 >git 暂存和取消暂存

git 暂存和取消暂存

时间:2023-08-26 14:45:30浏览次数:32  
标签:reset 文件 HEAD git 取消 提交 暂存

git add

文件暂存

在使用Git进行版本控制时,我们经常需要使用git add 命令将文件添加到暂存区(stage)以便提交更改。

但有时候我们可能会错误地将文件添加到暂存区,或者改变了对文件的修改意图,这时候我们需要取消暂存并将文件从暂存区移除。下面将详细介绍如何在Git中取消暂存文件的方法。

查看暂存文件状态

在取消暂存文件之前,首先我们需要了解哪些文件已经被暂存。可以使用以下命令查看暂存文件的状态:

git status

执行以上命令后,Git会显示已暂存和未暂存的文件列表,以及其他相关的状态信息。

git restore

取消暂存文件

如果只需要取消暂存单个文件,可以使用以下命令:

git restore --staged <文件名>

替换<文件名>为要取消暂存的文件名。执行该命令后,Git将会将文件从暂存区移除,但保留对文件的修改。

如果需要取消暂存多个文件,可以使用以下命令:

git restore --staged <文件1> <文件2> ...

替换<文件1> <文件2> ...为要取消暂存的文件列表,用空格分隔每个文件名。

如果需要一次性取消所有暂存文件,可以使用以下命令:

git restore --staged .

执行该命令后,Git将会将所有暂存文件移除,但保留对文件的修改。

取消暂存的同时撤销修改

有时候我们希望取消暂存的同时也撤销对文件的修改,将文件恢复到上一次提交的状态。可以使用以下命令:

git restore <文件名>

替换<文件名>为要取消暂存和撤销修改的文件名。执行该命令后,Git将会将文件从暂存区移除,并且撤销对文件的修改

如果希望一次性撤销所有暂存文件的修改,并将它们恢复到上一次提交的状态,可以使用以下命令:

git restore .

执行该命令后,Git将会将所有暂存文件移除,并且撤销对这些文件的修改。

确认取消暂存结果

取消暂存文件后,可以再次使用git status命令确认文件的状态是否已正确更新。取消暂存的文件应该不再显示在暂存区中,且状态应该被修改为"未暂存的更改"。

git reset

下面讨论一下reset和checkout 。 

三棵tree

理解reset和checkout 的最简方法,就是以 Git 的思维框架(将其作为内容管理器)来管理三棵不同的tree。 “tree” 在我们这里的实际意思是 “文件的集合”,而不是指特定的数据结构。 (在某些情况下索引看起来并不像一棵tree,不过我们现在的目的是用简单的方式思考它。)

Git 作为一个系统,是以它的一般操作来管理并操纵这三棵树的:

用途

HEAD

上一次提交的快照,下一次提交的父结点

Index

预期的下一次提交的快照

Working Directory

沙盒

HEAD

HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交。 这表示 HEAD 将是下一次提交的父结点。 通常,理解 HEAD 的最简方式,就是将它看做 你的上一次提交 的快照。

其实,查看快照的样子很容易。 下例就显示了 HEAD 快照实际的目录列表,以及其中每个文件的 SHA-1 校验和:

$ git cat-file -p HEAD
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
author Scott Chacon  1301511835 -0700
committer Scott Chacon  1301511835 -0700

initial commit

$ git ls-tree -r HEAD
100644 blob a906cb2a4a904a152...   README
100644 blob 8f94139338f9404f2...   Rakefile
040000 tree 99f1a6d12cb4b6f19...   lib

cat-file 与ls-tree是底层命令,它们一般用于底层工作,在日常工作中并不使用。不过它们能帮助我们了解到底发生了什么。

索引

索引是你的 预期的下一次提交。 我们也会将这个概念引用为 Git 的 “暂存区域”,这就是当你运行git commit时 Git 看起来的样子。

Git 将上一次检出到工作目录中的所有文件填充到索引区,它们看起来就像最初被检出时的样子。 之后你会将其中一些文件替换为新版本,接着通过git commit将它们转换为树来用作新的提交。

$ git ls-files -s
100644 a906cb2a4a904a152e80877d4088654daad0c859 0    README
100644 8f94139338f9404f26296befa88755fc2598c289 0    Rakefile
100644 47c6340d6459e05787f644c2447d2595f5d3a54b 0    lib/simplegit.rb

再说一次,我们在这里又用到了ls-files这个幕后的命令,它会显示出索引当前的样子。

确切来说,索引并非技术上的树结构,它其实是以扁平的清单实现的。不过对我们而言,把它当做树就够了。

工作目录

最后,你就有了自己的工作目录。 另外两棵树以一种高效但并不直观的方式,将它们的内容存储在.git文件夹中。 工作目录会将它们解包为实际的文件以便编辑。 你可以把工作目录当做 沙盒。在你将修改提交到暂存区并记录到历史之前,可以随意更改。

$ tree
.
├── README
├── Rakefile
└── lib
    └── simplegit.rb

1 directory, 3 files

工作流程

Git 主要的目的是通过操纵这三棵树来以更加连续的状态记录项目的快照。

reset workflow

让我们来可视化这个过程:假设我们进入到一个新目录,其中有一个文件。 我们称其为该文件的 v1 版本,将它标记为蓝色。 现在运行 git init,这会创建一个 Git 仓库,其中的 HEAD 引用指向未创建的分支( 还不存在)master。

reset ex1

此时,只有工作目录有内容。

现在我们想要提交这个文件,所以用git add来获取工作目录中的内容,并将其复制到索引中。

reset ex2

接着运行git commit,它首先会移除索引中的内容并将它保存为一个永久的快照,然后创建一个指向该快照的提交对象,最后更新master来指向本次提交。

reset ex3

此时如果我们运行git status,会发现没有任何改动,因为现在三棵树完全相同。

现在我们想要对文件进行修改然后提交它。 我们将会经历同样的过程;首先在工作目录中修改文件。 我们称其为该文件的 v2 版本,并将它标记为红色。

reset ex4

如果现在运行git status,我们会看到文件显示在 “Changes not staged for commit,” 下面并被标记为红色,因为该条目在索引与工作目录之间存在不同。 接着我们运行git add来将它暂存到索引中。

reset ex5

此时,由于索引和 HEAD 不同,若运行git status的话就会看到 “Changes to be committed” 下的该文件变为绿色 ——也就是说,现在预期的下一次提交与上一次提交不同。 最后,我们运行git commit来完成提交。

reset ex6

现在运行git status会没有输出,因为三棵树又变得相同了。

切换分支或克隆的过程也类似。 当检出一个分支时,它会修改 HEAD 指向新的分支引用,将 索引 填充为该次提交的快照,然后将 索引 的内容复制到 工作目录 中。

reset的作用

在以下情景中观察reset命令会更有意义。

为了演示这些例子,假设我们再次修改了file.txt文件并第三次提交它。 现在的历史看起来是这样的:

reset start

让我们跟着reset看看它都做了什么。 它以一种简单可预见的方式直接操纵这三棵树。 它做了三个基本操作。

第 1 步:移动 HEAD

reset 做的第一件事是移动 HEAD 的指向。 这与改变 HEAD 自身不同( checkout所做的);reset移动 HEAD 指向的分支。 这意味着如果 HEAD 设置为 master分支(例如,你正在master分支上),运行git reset 9e5e6a4将会使master指向 9e5e6a4.

reset soft

无论你调用了何种形式的带有一个提交的reset,它首先都会尝试这样做。 使用reset --soft,它将仅仅停在那儿。

现在看一眼上图,理解一下发生的事情:它本质上是撤销了上一次git commit命令。 当你在运行git commit时,Git 会创建一个新的提交,并移动 HEAD 所指向的分支来使其指向该提交。 当你将它reset回HEAD~(HEAD 的父结点)时,其实就是把该分支移动回原来的位置,而不会改变索引和工作目录。 现在你可以更新索引并再次运行git commit来完成git commit --amend所要做的事情了(见 修改最后一次提交)。

第 2 步:更新索引(--mixed)

注意,如果你现在运行git status的话,就会看到新的 HEAD 和以绿色标出的它和索引之间的区别。

接下来,reset 会用 HEAD 指向的当前快照的内容来更新索引。

reset mixed

如果指定--mixed选项,reset 将会在这时停止。 这也是默认行为,所以如果没有指定任何选项(在本例中只是git reset HEAD~),这就是命令将会停止的地方。

现在再看一眼上图,理解一下发生的事情:它依然会撤销一上次提交,但还会 取消暂存 所有的东西。 于是,我们回滚到了所有git add和git commit的命令执行之前。

第 3 步:更新工作目录(--hard)

reset 要做的的第三件事情就是让工作目录看起来像索引。 如果使用--hard选项,它将会继续这一步。

reset hard

现在让我们回想一下刚才发生的事情。 你撤销了最后的提交、git add和git commit命令以及工作目录中的所有工作。

必须注意, 标记--hard是reset命令唯一的危险用法,它也是 Git 会真正地销毁数据的仅有的几个操作之一。 其他任何形式的reset调用都可以轻松撤消,但是--hard选项不能,因为它强制覆盖了工作目录中的文件。 在这种特殊情况下,我们的 Git 数据库中的一个提交内还留有该文件的 v3 版本,我们可以通过reflog来找回它。但是若该文件还未提交,Git 仍会覆盖它从而导致无法恢复。

回顾

reset 命令会以特定的顺序重写这三棵树,在你指定以下选项时停止:

  1. 移动 HEAD 分支的指向 (若指定了 --soft,则到此停止)

  2. 使索引看起来像 HEAD (若未指定 --hard,则到此停止)

  3. 使工作目录看起来像索引

通过路径来重置

前面讲述了reset基本形式的行为,不过你还可以给它提供一个作用路径。 若指定了一个路径,reset将会跳过第 1 步,并且将它的作用范围限定为指定的文件或文件集合。 这样做自然有它的道理,因为 HEAD 只是一个指针,你无法让它同时指向两个提交中各自的一部分。 不过索引和工作目录 可以部分更新,所以重置会继续进行第 2、3 步。

现在,假如我们运行git reset file.txt(这其实是git reset --mixed HEAD file.txt的简写形式,因为你既没有指定一个提交的 SHA-1 或分支,也没有指定--soft或--hard ),它会:

  1. 移动 HEAD 分支的指向 (已跳过)

  2. 让索引看起来像 HEAD (到此处停止)

所以它本质上只是将file.txt从 HEAD 复制到索引中。

reset path1

它还有 取消暂存文件 的实际效果。 如果我们查看该命令的示意图,然后再想想git add所做的事,就会发现它们正好相反。

reset path2

这就是为什么git status命令的输出会建议运行此命令来取消暂存一个文件。 (查看 取消暂存的文件 来了解更多。)

我们可以不让 Git 从 HEAD 拉取数据,而是通过具体指定一个提交来拉取该文件的对应版本。 我们只需运行类似于git reset eb43bf file.txt的命令即可。

reset path3

它其实做了同样的事情,也就是把工作目录中的文件恢复到 v1 版本,运行git add添加它,然后再将它恢复到 v3 版本(只是不用真的过一遍这些步骤)。 如果我们现在运行git commit,它就会记录一条“将该文件恢复到 v1 版本”的更改,尽管我们并未在工作目录中真正地再次拥有它。

还有一点同git add一样,就是reset命令也可以接受一个--patch选项来一块一块地取消暂存的内容。 这样你就可以根据选择来取消暂存或恢复内容了。

压缩

我们来看看如何利用这种新的功能来做一些有趣的事情 - 压缩提交。

假设你的一系列提交信息中有 “oops.”、“WIP” 和 “forgot this file”, 聪明的你就能使用reset来轻松快速地将它们压缩成单个提交,也显出你的聪明。 (压缩提交 展示了另一种方式,不过在本例中用reset更简单。)

假设你有一个项目,第一次提交中有一个文件,第二次提交增加了一个新的文件并修改了第一个文件,第三次提交再次修改了第一个文件。 由于第二次提交是一个未完成的工作,因此你想要压缩它。

reset squash r1

那么可以运行git reset --soft HEAD~2来将 HEAD 分支移动到一个旧一点的提交上(即你想要保留的第一个提交):

reset squash r2

然后只需再次运行git commit:

reset squash r3

现在你可以查看可到达的历史,即将会推送的历史,现在看起来有个 v1 版file-a.txt的提交,接着第二个提交将file-a.txt修改成了 v3 版并增加了file-b.txt。 包含 v2 版本的文件已经不在历史中了。

checkout

和reset一样, checkout也操纵三棵树,不过它有一点不同,这取决于你是否传给该命令一个文件路径。

不带路径

运行git checkout [branch]与运行git reset --hard [branch]非常相似,它会更新所有三棵树使其看起来像[branch],不过有两点重要的区别。

首先不同于reset --hard,checkout对工作目录是安全的,它会通过检查来确保不会将已更改的文件吹走。 其实它还更聪明一些。它会在工作目录中先试着简单合并一下,这样所有还未修改过的文件都会被更新。 而reset --hard则会不做检查就全面地替换所有东西。

第二个重要的区别是如何更新 HEAD。reset会移动 HEAD 分支的指向,而checkout只会移动 HEAD 自身来指向另一个分支。

例如,假设我们有master和develop分支,它们分别指向不同的提交;我们现在在develop上(所以 HEAD 指向它)。 如果我们运行git reset master,那么develop自身现在会和master指向同一个提交。 而如果我们运行it checkout master的话, 不会移动develop,HEAD 自身会移动。 现在 HEAD 将会指向master 。

所以,虽然在这两种情况下我们都移动 HEAD 使其指向了提交 A,但做法是非常不同的。reset会移动 HEAD 分支的指向,而checkout则移动 HEAD 自身。

reset checkout
带路径

运行checkout的另一种方式就是指定一个文件路径,这会像 一样不会移动 HEAD。 它就像reset那样用该次提交中的那个文件来更新索引,但是它也会覆盖工作目录中对应的文件。 它就像是 git reset [branch] file(如果reset允许你这样运行的话)- 这样对工作目录并不安全,它也不会移动 HEAD。

此外,checkout同git reset和git add一样, 也接受一个--patch选项,允许你根据选择一块一块地恢复文件内容。

总结

下面的速查表列出了命令对树的影响。 “HEAD” 一列中的 “REF” 表示该命令移动了 HEAD 指向的分支引用,而`‘HEAD’' 则表示只移动了 HEAD 自身。 特别注意 WD Safe? 一列 - 如果它标记为 NO,那么运行该命令之前请考虑一下。

 HEADIndexWorkdirWD Safe?

Commit Level

       

reset --soft [commit]

REF

NO

NO

YES

reset [commit]

REF

YES

NO

YES

reset --hard [commit]

REF

YES

YES

NO

checkout [commit]

HEAD

YES

YES

YES

File Level

       

reset (commit) [file]

NO

YES

NO

YES

checkout (commit) [file]

NO

YES

YES

NO

在Git中,取消暂存文件是一个常见的操作,用于纠正错误的暂存或更改修改意图。通过使用git restore命令,我们可以轻松地取消暂存单个或多个文件,甚至可以撤销对文件的修改。以下是本文介绍的主要命令和用法:

  • git status:查看暂存文件的状态。
  • git restore --staged <文件名>:取消暂存单个文件。
  • git restore --staged <文件1> <文件2> ...:取消暂存多个文件。
  • git restore --staged .:取消所有暂存文件。
  • git restore <文件名>:取消暂存并撤销对文件的修改。
  • git restore .:撤销所有暂存文件的修改。

 

标签:reset,文件,HEAD,git,取消,提交,暂存
From: https://www.cnblogs.com/lanlancky/p/17658783.html

相关文章

  • github中文社区
    GitHub是世界上最大的代码托管平台,超7千万开发者正在使用。GitHub中文社区是一个致力于分享和传播GitHub上优质开源项目的中文社区平台。 原来的githubs.cn打不开了,换地址了https://github-zh.comGitHub中文社区|GitHub中文网(github-zh.com)......
  • 【面试宝典】Git 版本控制常用命令
    一.Git概述Git是目前世界上最先进的分布式版本控制工具,主要用于管理开发过程中的源代码文件(Java类、xml文件、html页面等),包括:代码回溯:Git在管理文件过程中会记录日志,方便回退到历史版本版本切换:Git存在分支的概念,一个项目可以有多个分支(版本),可以任意切换多人协作:Git支......
  • Typora+PicGo 上传图片至GitHub仓库
    提前准备好Github账号、PicGo、Typora创建Github账号地址:https://github.com/下载PicGo地址:https://github.com/Molunerfinn/PicGo/releases/下载Typora地址:https://typora.yjjxx.cn/index.html1.创建Github仓库 点击new新建仓库输入Repositoryname(PicGo中要用到),选择Public......
  • Git的一些基本操作1
    1.检查文件的状态可以使用gitstatus命令查看文件处于什么状态,例如:在状态报告中可以看到新建的index.html文件出现在Untrackedfiles(未跟踪的文件)下面。未跟踪的文件意味着Git在之前的快照(提交)中没有这些文件;Git不会自动将之纳入跟踪范围,除非明确地告诉它“我需要......
  • Git的基本操作
    1.获取Git仓库的两种方式①将尚未进行版本控制的本地目录转换为Git仓库②从其它服务器克隆一个已存在的Git仓库以上两种方式都能够在自己的电脑上得到一个可用的Git仓库 2.在现有目录中初始化仓库如果自己有一个尚未进行版本控制的项目目录,想要用Git来控制......
  • 关于git的一些基础知识记录
    一.配置用户信息安装完Git之后,要做的第一件事就是设置自己的用户名和邮件地址。因为通过Git对项目进行版本管理的时候,Git需要使用这些基本信息,来记录是谁对项目进行了操作1.空白处鼠标右键,选择GitBashHere输入以下命令:  2.Git的全局配置文件通过gitconfig......
  • 解决:git SSL certificate problem: unable to get local issuer certificate
    今天遇到了gitupdate失败。记录一下错误:gitSSLcertificateproblem:unabletogetlocalissuercertificate这个问题是由于没有配置信任的服务器HTTPS验证。默认,cURL被设为不信任任何CAs,就是说,它不信任任何服务器验证。找到任意一个文件夹。右键选择>GitBashHere(......
  • git_使用git worktree命令使不同分支的代码文件可以同步运行
    情景再现:我本地代码正在开发后台系统的过程中,前台开发的同事时不时地会来找我要IP地址,使用正在开发的后台管理系统来进行一些数据的增删改查.这个时候直接提供正在开发的版本的开发服务器地址是不行的,因为随着代码的编写时不时的报个bug是家常便饭,对于使用者来说非常......
  • git 用法 记录 处理分支 合并分支 解决分支冲突
      gitstatus  查看分支状态gitadd.提交到暂存区gitcommit-m'优化'提交到准备pushgitpull 拉取最新分支gitpush 提交到git上 gitbranch 查看所有分支gitcheckout 分支名字  切换到指定分支gitpullorigin'分支名字'  拉取分支名字最......
  • IDEA + github 初次使用踩坑
    大学毕业至今没怎么用过git,作为计算机专业的学生多少汗颜,工作用的gitblit,在IDEA上也只会pullcommitpush三个按键. 由于最近上班摸鱼时间太多,打算多用用git,也多在github活跃一些. 建立本地仓库,commit然后push自然不多提了,网上的攻略很多.简单记录......