git 丢弃临时区域中的特定更改

git discard specifically changes in the staging area

在某些情况下,我想丢弃当前在暂存区中的所有文件,并且只丢弃当前在暂存区中的文件。本质上我想要的东西就像 进行提交然后立即丢弃刚刚进行的提交 .

现在在实践中我主要使用 git stash 来达到这个目的,请记住我实际上并不想要我藏起来的东西。

但是,stashing 和丢弃并不是一回事,stash 似乎捕获了所有被跟踪的文件,而不仅仅是当前在暂存区中的文件,如下面的脚本所示.

有没有办法完全丢弃(更改为)当前在暂存区中的文件?

#!/bin/bash

mkdir -p ./a

echo foo > ./a/foo
echo bar > ./a/bar
(
    # add initial commit
    cd ./a
    git init
    git add foo
    git add bar
    git commit --message='initial commit'

    # change first line of foo and bar
    ed ./foo <<'EOF'
s/$/ aaaaaa/
wq
EOF
    ed ./bar <<'EOF'
s/$/ aaaaaaa/
wq
EOF

    git add ./foo
    git stash save
    git status
)

当运行时,此脚本产生以下输出

$ bash gitrepo.sh
Initialized empty Git repository in ~/git/a/a/.git/
[master (root-commit) a8f0104] initial commit
 2 files changed, 2 insertions(+)
 create mode 100644 bar
 create mode 100644 foo
4
11
4
12
Saved working directory and index state WIP on master: a8f0104 initial commit
HEAD is now at a8f0104 initial commit

我也尝试过用 git clean -fgit reset 代替 git stash save

的基本相同的脚本

git clean -f 似乎既没有放弃对 foo 的更改,也没有放弃对 bar.

的更改
[master (root-commit) 0c10b47] initial commit
 2 files changed, 2 insertions(+)
 create mode 100644 bar
 create mode 100644 foo
4
11
4
12
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   foo

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   bar

git reset 在这种特定情况下似乎只是从临时区域中删除 foo

[master (root-commit) 65fce7b] initial commit
 2 files changed, 2 insertions(+)
 create mode 100644 bar
 create mode 100644 foo
4
11
4
12
Unstaged changes after reset:
M       bar
M       foo

目前我拥有的最佳解决方案是以下脚本,在我测试过的少数情况下它具有正确的行为。但是,它通过创建、提交然后删除一个由 UUID 命名的新分支来完成此行为。我更喜欢一种放弃临时区域中不那么迂回的更改的方法。

#!/bin/bash

branch="$(python -c 'import uuid; print(str(uuid.uuid4()))')"
git checkout -b "$branch"
git commit -m "commit for $branch"
git checkout -
git branch -D "$branch"

当使用 gitdiscard(上面的脚本)时,测试脚本产生了正确的结果,foo 的更改被丢弃,但对 bar 的未暂存修改仍然存在。

[master (root-commit) e4e81d9] initial commit
 2 files changed, 2 insertions(+)
 create mode 100644 bar
 create mode 100644 foo
4
11
4
12
M       bar
M       foo
Switched to a new branch 'abebe893-e579-4ecc-9422-6c99051d67f6'
[abebe893-e579-4ecc-9422-6c99051d67f6 05916d5] commit for abebe893-e579-4ecc-9422-6c99051d67f6
 1 file changed, 1 insertion(+), 1 deletion(-)
M       bar
Switched to branch 'master'
Deleted branch abebe893-e579-4ecc-9422-6c99051d67f6 (was 05916d5).

类似但不重复的问题的非详尽列表:

This question 不是重复,因为它指的是丢弃未跟踪的文件,而不是暂存区中的更改。

This question不是重复,因为它指的是直接清除整个工作,而不是丢弃暂存区中的更改。

This question 包括未跟踪的文件和未跟踪的目录,但不包括放弃对暂存区中文件的更改。

您可以考虑 --keep-index 标志 git stash

事实上,它确实与您的要求相反:它存储索引中的所有内容

您可以利用这一点,只存储未暂存的更改,然后重置索引和工作树,最后重新应用存储。

(我不确定您希望如何处理未跟踪的文件,但也有可能要记住存储标志 --include-untracked。如果需要,请在第一个存储中使用它将它们包括在最终结果中)

git stash --keep-index [--include-untracked]
git reset --hard
git stash apply

首先,请记住当 git status 说:

nothing to commit, working tree clean

这并不意味着暂存区。事实上,暂存区完全满了:每个文件暂存区匹配当前(或HEAD)中的每个文件犯罪。同时,暂存区中的每个文件 匹配工作树中的每个跟踪文件。

Is there a way to completely discard (changes to) exactly the files that are currently in the staging area?

考虑 git resetgit checkout 和 - 在 Git 2.23(我还没有使用)git restore:

  • git reset 命令有一种模式,它从当前提交复制一个文件,即 HEAD,到索引/暂存区,而不触及全部工作树:

    git重置--<em>文件</em>

    仅当文件名 file 类似于分支名称或 git reset 选项时才需要 --,例如,如果您有一个名为 master 的文件,并且您想让暂存版本与提交的版本匹配,git reset master 是错误的,但 git reset -- master 是正确的。

  • git reset HEAD 完全按照您的要求执行:它将 HEAD 提交中的每个文件复制到暂存区,因此不会暂存任何更改。

  • 如果您还想更新文件的工作树副本,git checkout 的众多模式之一正是这样做的:

    git 结帐 HEAD -- <em>file</em>

    和以前一样,仅当文件名类似于 git checkout 选项时才需要 --。例如,如果您想放弃对名为 --patch 的文件进行的暂存 and/or 未暂存更改,则 git checkout HEAD --patch 是错误的,但 git checkout HEAD -- --patch 是正确的。 (由于您必须在此处指定 HEAD,因此 git checkout HEAD master 会抛出对名为 master 的文件的暂存和未暂存更改。但是,养成只使用的习惯更安全每次 --。)

  • 和以前一样,您可以检出当前提交中的每个每个文件,但是这次它需要使用检出一个folder(目录)名称导致 Git 递归地检查该文件夹的内容,因此:

    git checkout HEAD -- .

    可以解决问题,尽管您需要 . 来引用工作树的正确级别。

在 Git 2.23 中,新的 git restore 命令使您能够根据需要覆盖索引、工作树或两个副本。

记住,Git 始终为每个文件保留 三个 个活动副本。 提交的 HEAD copy 无法更改——它在提交中并且提交无法更改——但可以使用 git resetgit checkout HEAD 访问它。索引副本——也称为暂存副本——可以随时通过以下方式覆盖:

  • HEAD 复制:现在没有任何更改,或者
  • 从工作树复制:现在工作树中的任何内容都已上演。

工作树副本是一个普通的普通文件,所以你可以做任何你想做的事;但各种形式的 git checkout 将根据您的要求覆盖它。