首页 > 解决方案 > git 提交分阶段更改的子集(按文件名)

问题描述

我有以下情况:

$ git status --porcelain
 M A
M  B
A  C
D  D
AM E
MM F
MM G

我只想提交F和的分阶段更改G。我不想为Fand提交未暂存的更改G,我也不想为A->提交任何更改E

不起作用的事情:

# commits all staged and F, G unstaged
git commit F G

# same as above
git commit -o F G

# commits all staged + unstaged F, G (basically same as above)
git commit -i F G

# interactively add more to the index, keeps all already staged
git commit -p

# as above but limits interactive choices to F, G unstaged changes
git commit -p F G

# almost, but stash pop results in merge conflicts for F, G
git stash -k
git commit F G
git stash pop

更新:我知道这可以通过额外的直接文件副本来完成。但我想要一些不那么手动的东西。

标签: git

解决方案


您在这里所做的大多数事情都是相当手动的,包括所有git stash方法,实际上只是从当前索引和当前工作树进行两次提交,然后使用git reset --hard或等效将所有内容恢复到它在HEAD. 来自的两个提交git stash不在分支上,但可以稍后使用git stash apply. 不过,这不是我会在这里做的。考虑首先手动执行此操作,然后(如果您经常执行此操作)将手动过程转换为脚本(参见下面的示例)。

一个关键是要注意只有一个可分辨索引(“the”索引),但您可以拥有其他索引文件。您可以拥有任意数量的临时索引文件。将环境变量设置为GIT_INDEX_FILE不存在的文件名,例如:

export GIT_INDEX_FILE=$(realpath $(git rev-parse --git-dir))/index$$

(这取决于你的 shell 的 PID 创建一个唯一的文件名)或者:

tf=$(mktemp); rm -f $tf; export GIT_INDEX_FILE="$tf"

您现在在这个临时索引中没有任何内容。从当前提交填充它,使其与当前提交匹配:

git read-tree HEAD

所有文件现在都重新设置为匹配HEAD. 现在是困难的部分:

I want to commit only the staged changes for F and G

这些是存储库中的 blob 和/或(主)索引中的模式更改。我们必须取消设置 GIT_INDEX_FILE 才能读取它们。我们可以在创建临时索引之前执行此操作,但现在执行此操作,我们可以使用取消设置环境变量的子 shell:

(unset GIT_INDEX_FILE && git ls-file --staged F G) | git update-index --index-info

现在,我们的临时索引中有文件FG临时索引,为提交而暂存,我们可以在git commit没有选项的情况下运行,或者使用-m或类似的选项从这个临时索引提交。

做出新的提交后,您只需要取消设置GIT_INDEX_FILE(尽管要清理,您还应该删除临时索引,rm -f $GIT_INDEX_FILE)。如果您脚本中执行所有这些操作,退出脚本(删除任何临时文件后)就足够了。

一个骨架脚本,未经测试,但考虑到可能的极端情况,如下所示:

#! /bin/sh

case $# in
0) echo "usage: git onlycommit file1 ... fileN"; exit 1;;
esac

# exit (and clean up) on error
set -e
tf=$(mktemp)
rm -f "$tf"
trap "rm -f \"$tf\"" 0 1 2 3 15

# if GIT_INDEX_FILE is already set, make our reset use that value;
# otherwise, make our reset just unset it
if [ "${GIT_INDEX_FILE+true}" = true ]; then
    reset_index="export GIT_INDEX_FILE=\"${GIT_INDEX_FILE}"\"
else
    reset_index="unset GIT_INDEX_FILE"
fi
export GIT_INDEX_FILE="$tf"

git read-tree HEAD
(eval $reset_index; git ls-files --staged "$@") | git update-index --index-info
git commit

这应该被测试,并且可能也很有趣,例如,它可能应该首先测试所有参数文件是否标记为“暂存提交”,即当前索引条目与HEAD. 它可能还应该确保您没有处于冲突合并的中间,或者至少,所选文件不是。

如果您调用它git-onlycommit并将其放入您的文件中,$PATH您应该可以将其作为git onlycommit F G.


推荐阅读