oynix

于无声处听惊雷,于无色处见繁花

Git撤销文件修改

日常开发中,经常会遇到需要将做了一些修改的文件恢复原状的需求,在这里,尽量用简洁的描述来总结一些 Git 中不同状态下的文件如何撤销修改。

1. Git 分区

主要有 4 个分区:

  • 工作区:即工作目录
  • 暂存区:用于存放临时改动,通过git add命令即可将工作区的改动添加至暂存区
  • 仓库区:用于存放提交,通过git commit命令即可将暂存区的内容提交至仓库区
  • 远程仓库:用于托管本地仓库

2. 撤销新添加的文件(未被追踪的文件)

对于新增的文件,在仓库区中并没有记录,对于这样未被追踪的文件,需要使用clean命令,但clean命令只能清理工作区的文件,如果已经通过git add命令添加到了暂存区的文件,则需要先将文件退回到工作区,然后再clean

1
2
3
4
5
6
# 针对在工作区的文件,直接clean
git clean -df

# 针对在暂存区的文件,先退回再clean
git restore --staged file.name
git clean -df

3. 撤销修改的文件(已被追踪的文件)

对于已经存在于仓库区的文件,即为已被追踪的文件,如果想要撤销这种文件的改动,需要使用restore命令,同上面,如果文件已经添加到暂存区,那么也需要先退回到工作区,然后再撤销。

1
2
3
4
5
6
# 针对在工作区的文件,直接restore
git restore file.name

# 针对在暂存区的文件,先退回再restore
git restore --staged file.name
git restore file.name

4. 撤销提交中的某个文件(已提交到仓库区的文件)

某次提交了多个文件,但是想将其中的某一个文件的修改撤回。这种情况,流程是这样的,首先需要确定文件需要回退到哪次提交的状态,比如,想撤回提交ID为C1中对文件的修改,那么也就是说,文件需要回到C1上一次的提交。然后,Git会列出目标状态之后的所有改动,只要放弃这些改动,那么文件便回到了目标状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看与该文件相关的所有历史提交,用哪个都可以
git log file.name
git log --oneline file.name
git reflog file.name

# 将文件的追踪状态回到某个提交
git reset commitId file.name

# 此时,该文件在commitId之后的改动都在工作区,只要放弃这些修改,就会回撤到commitId状态
git restore file.name

# 提交即可,可单独提交,也可以追加到上次提交
git commit -am 'reset file.name'
git commit --amend --allow-empty

当执行完git reset id file.name之后,这个文件既存在于工作区,也存在于暂存区,工作区是最新的状态,暂存区是目标状态。这个时候,不管是通过git add命令将工作区的文件添加到暂存区,还是通过git restore --staged将暂存区的文件退回到工作区,最终都会使得这个文件变成最新状态,那么git reset命令的效果就会消失,就像是无事发生。

只有通过git restore file.name放弃工作区的最新状态,将暂存区的目标状态提交至仓库区,才可达到目的。

附加1: git reset 和 git revert

git reset还可用来将分支的指针HEAD指向之前的某个提交,对于这个提交之后的所有改动,有3种处理方式:放到工作区、放到暂存区和直接丢弃。

1
2
3
4
5
6
7
8
9
# 放到工作区mixed(默认方式),两种写法是等同的
git reset commitId
git reset --mixed commitId

# 放到暂存区
git reset --soft commitId

# 直接丢弃
git reset --hard commitId

git revert用来反向修改某个提交的所有改动,然后和当前HEAD所在的提交进行合并,所以,反向修改后的内容,和最新状态的内容可能会出现冲突,这个时候需要处理冲突,然后再提交

1
git revert commitId

当使用git revert恢复文件时,不用,也不能指定方式,对于目标状态之后的所有改动,只能手动处理。

附加2: git clean

这个命令只能清理工作区中未被追踪过的文件,必须同时满足这两个条件。同时,这个命令无法指定文件,是针对所有符合这两个条件的文件无差别攻击,所以,一般操作是将不需要被清理的文件先添加至暂存区避难。

  • 删除 untracked files

    1
    git clean -f
  • 删除 untracked directories

    1
    git clean -d
  • 删除 gitignore 中的文件

    1
    git clean -x
  • 显示将要被删除的文件(强烈建议)

    1
    git clean -n

以上参数可按需搭配使用,一般常用删除文件和目录,gitignore的轻易不需要删除(如果在参数加了n,那么此次操作并不会真的执行删除,只是列出会被删除的文件):

1
2
3
4
# 列出
git clean -nfd
# 删除
git clean -fd

附加3: 文件名

所有指定文件名的地方,都支持模糊匹配,举个例子:

  • 放弃工作区所有扩展名为txt文件的修改
    1
    2
    # **表示任何文件夹,*.txt表示任何以.txt结尾的文件
    git restore **/*.txt
------------- (完) -------------
  • 本文作者: oynix
  • 本文链接: https://oynix.com/2023/02/f69d9a8a88f7/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

欢迎关注我的其它发布渠道