有没有办法让我把阶段性的变化藏起来?我遇到问题的情况是,我在给定的时间内处理了几个bug,并且有几个未分阶段的更改。我希望能够单独运行这些文件,创建我的.patch文件,并将它们保存起来,直到代码被批准。这样,当它被批准时,我可以隐藏我的整个(当前)会话,弹出错误并推送代码。

我做错了吗?我是否误解了git如何以其他方式简化我的过程?


当前回答

如果你没有一个更新的git,它有——staging选项,下面是如何直接做到这一点。

git stash命令只是一个复杂的shell脚本,用于操作树对象和提交等。我们可以手动完成它所做的事情。

概述

隐藏堆栈记录特殊的提交。我们将从阶段性更改中创建一个提交,然后手动将其转移到存储中。然后,取消提交。

设置

我有一个项目,其中Makefile有两个更改。一个是有舞台的,一个是没有舞台的:

$ git diff --cached
diff --git a/Makefile b/Makefile
index 4ca6058f..c8c7480a 100644
--- a/Makefile
+++ b/Makefile
@@ -605,7 +605,7 @@ conftest2: conftest1.c conftest2.c
        $(V)if echo | $(CC) -dM -E - | grep -s __ANDROID__ >  /dev/null 2>&1 ; then \
          echo yes ; \
        fi
-
+# FOO
 .PHONY: conftest.clean
 conftest.clean:
        $(V)rm -f conftest$(EXE) conftest.[co] \

$ git diff
diff --git a/Makefile b/Makefile
index c8c7480a..270c313d 100644
--- a/Makefile
+++ b/Makefile
@@ -611,3 +611,4 @@ conftest2: conftest1.c conftest2.c
        $(V)rm -f conftest$(EXE) conftest.[co] \
        conftest2$(EXE) conftest[12].[oc] \
        conftest.err
+# BAR

添加# FOO行是阶段性的;# BAR的添加是无阶段的。

步骤1:创建树对象。

首先,我们从当前索引(包含分段项)创建一个树对象。

$ git write-tree
0d9651ad74328e747a053a9434d9867c8cd79d41 <-- output

步骤2:创建两次提交。

首先,从树中创建一个提交,它有一个父结点,即当前分支HEAD:

$ git commit-tree -p HEAD -m 'add # FOO' 0d9651ad74328e747a053a9434d9867c8cd79d41
baa34222e781078d82cefed519ff105715c7f665 <-- output

然后,从树中创建另一个提交,它有两个父节点:HEAD和baa34222…我们刚刚做出的承诺:

$ git commit-tree -p HEAD -p baa34222e781078d82cefed519ff105715c7f665 -m 'add # FOO' 0d9651ad74328e747a053a9434d9867c8cd79d41
2c96b028e475a05d84f472da7f2a70ac53d0ac90 <-- output

这个双亲2c96b02…将是我们安装到隐藏的提交。

注意,git commit-tree不是git commit。这是一个低级别的命令。这些提交不会对当前分支做任何事情;我们只是在Git的存储中分配对象,并没有对我们所在的分支做任何事情,也没有改变索引或工作树。

步骤3。

接下来,我们将这个提交写到.git/refs/stash中。你可能需要备份这个文件。

$ echo 2c96b028e475a05d84f472da7f2a70ac53d0ac90 > .git/refs/stash

步骤4。

我们将同样的提交钩子到.git/logs/refs/stash文件中。在编辑之前,文件中的最后一行看起来是这样的:

b1819d98ab24720796315b9497236172d1fb1f5f 3b2ecc6604d77c9df4fe72efd1fbd384b2c43f76 Au Thor <author@example.com> 1654892876 -0700 On master: elim-aliases

我们手动添加这条伪行:

3b2ecc6604d77c9df4fe72efd1fbd384b2c43f76 2c96b028e475a05d84f472da7f2a70ac53d0ac90 Au Thor <author@example.com> 1654892876 -0700 On master: add # FOO

您可能还想备份此文件。然而,如果出了问题,事情很容易恢复。

注意左边哈希3b2ecc…在这个新行中与前一行中的右哈希相同。这是之前的存储提交,与我们在这里所做的无关,必须重复将这一行链接到存储堆栈中。在它的右边,我们有我们的散列2c96b028e4....然后剩下的人都假装离开了。在时区-0700后面有一个硬选项卡,而不是空格。我只是复制粘贴了一下。

第5步。

我们验证我们已经将commit添加到存储堆栈:

$ git stash list | head -3
stash@{0}: On master: add # FOO
stash@{1}: On master: elim-aliases
stash@{2}: On master: compiler-safe-eval

and:

$ git stash show -p
diff --git a/Makefile b/Makefile
index 4ca6058f..c8c7480a 100644
--- a/Makefile
+++ b/Makefile
@@ -605,7 +605,7 @@ conftest2: conftest1.c conftest2.c
        $(V)if echo | $(CC) -dM -E - | grep -s __ANDROID__ >  /dev/null 2>&1 ; then \
          echo yes ; \
        fi
-
+# FOO
 .PHONY: conftest.clean
 conftest.clean:
        $(V)rm -f conftest$(EXE) conftest.[co] \

它在那里;Git stash认为我们的提交是一个“类仓库”的提交,并接受它。

总结

我们手动获取一个分阶段更改的索引,并生成一个树对象。 然后,我们将树对象转换为常规提交对象,然后再进行一个双亲提交。双父对象作为类存储提交是可以接受的。 最后,我们通过编辑一对文件手动将这个提交修补到存储堆栈中。

附录

我们没有执行任何操作索引或工作树的不安全命令。但是,我们已经不安全地操作了git存储堆栈。如果出现问题,以下是如何修复它(而不是从备份文件恢复):

删除我们添加到.git/logs/refs/stash的伪行,这样这仍然是最后一行: b1819d98ab24720796315b9497236172d1fb1f5f 3b2ecc6604d77c9df4fe72efd1fbd384b2c43f76 Au Thor <author@example.com> 1654892876 -0700 On master: elime -aliases 取右边的散列3b2ecc6604d77c9df4fe72efd1fbd384b2c43f76并将其植入到.git/refs/stash文件中: $ echo 3b2ecc6604d77c9df4fe72efd1fbd384b2c43f76 > .git/refs/stash

以前的收藏现在恢复了。

其他回答

另一种方法是用你不想被存储的文件创建一个临时提交,然后存储剩余的文件,轻轻地删除上次提交,保持文件完整:

git add *files that you don't want to be stashed*
git commit -m "temp"
git stash --include-untracked
git reset --soft HEAD~1

这样你只需要触摸你想要触摸的文件。

注意,“——include-untracked”在这里还用于保存新文件(这可能是您真正想要的)。

铊;博士;总stash-staged

创建别名后:

git config --global alias.stash-staged '!bash -c "git stash -- \$(git diff --staged --name-only)"'

这里git diff返回的是——暂存文件的列表——只有名字 然后我们将这个列表作为pathspec传递给git stash命令。

从男人的赃物:

git stash [--] [<pathspec>...]

<pathspec>...
   The new stash entry records the modified states only for the files
   that match the pathspec. The index entries and working tree
   files are then rolled back to the state in HEAD only for these
   files, too, leaving files that do not match the pathspec intact.

要修剪意外的更改,特别是删除多个文件,请执行以下操作:

git add <stuff to keep> && git stash --keep-index && git stash drop

换句话说,把垃圾藏起来,把它们一起扔掉。

在git版本2.17.1中测试

为什么不提交对某个错误的更改,并根据该提交及其前身创建一个补丁呢?

# hackhackhack, fix two unrelated bugs
git add -p                   # add hunks of first bug
git commit -m 'fix bug #123' # create commit #1
git add -p                   # add hunks of second bug
git commit -m 'fix bug #321' # create commit #2

然后,创建适当的补丁,使用git format-patch:

git format-patch HEAD^^

这将创建两个文件:0001-fix-bug-123。补丁和0002-fix-bug-321.patch

或者您可以为每个错误创建单独的分支,这样您就可以单独合并或重新建立错误修复,甚至在它们不起作用时删除它们。

在这个场景中,我更喜欢为每个问题创建新的分支。我使用了一个前缀temp/,所以我知道我可以稍后删除这些分支。

git checkout -b temp/bug1

准备修复bug1的文件并提交它们。

git checkout -b temp/bug2

然后,您可以根据需要从相应的分支中选择提交,并提交一个拉取请求。