我在分支a
上。我想在分支b
上进行提交,以便克隆分支b
的人具有与我现在相同的工作目录。
存储当前更改无效,因为在某些情况下这会导致冲突。
我正在寻找相当于创建工作目录的临时副本,调用git checkout -f b
,删除所有文件,将临时目录复制到项目目录并进行提交的等效操作。
答案 0 :(得分:1)
基本上任何提交 都是“工作目录的临时副本”。
例如,当您git show <commit>
时,快照所指的就是您所指的“之间的变化”。
为什么直接回答您的问题有点困难。让我们尝试以下两种可能的路径:
具有分支b
的历史记录重写
如果您此时想在分支b
上进行一次提交,以反映分支a
上的确切状态,为什么不直接将分支b指向正确位置。像这样:
# get the uncommited changes on the branch
git commit -am "Useful message"
# point b where a is now
git branch -f b a
# instead of forcing the branch we could merge with a strategy ours
# but it's hard to tell what you need from your description alone
# reset a to its previous state
git reset --hard HEAD^
初始状态:
C1---C2 <<< a <<< HEAD # with a dirty tree, uncommited changes
? <<< b # (you didn't mention where b was pointing)
之后的结果
C1---C2 <<< a <<< HEAD
\
C3 <<< b # where your last (previously uncommited) changes are
在重写分支的历史记录时,应仔细考虑它,并可能排除是否共享了该分支。尽管如此,它仍然可以满足您的要求:“克隆分支b的人与我现在拥有的工作目录相同。”
没有分支b
的重写历史记录
为避免重写分支b
的历史记录,一种替代方法是使用一种策略将a合并为b,该策略将从a
的角度考虑所有内容,而不仅仅是冲突的部分。它会像这样:
# we work on a copy of branch a
git checkout -b a-copy a
# get the uncommited changes on the branch
git commit -am "Useful message"
# proceed with the merge, taking nothing from b
git merge -s ours b
# we now reflect the merge on b, and this is a fast-forward
git checkout b
git merge a-copy
# no further need for the working copy of a
git branch -D a-copy
a
不需要重置,因为它在任何时候都没有移动。
第一次提交后:
C1---C2 <<< a
\
C3 <<< a-copy <<< HEAD
o <<< b (anywhere in the tree)
第一次合并后:
C1---C2 <<< a
\
C3 (same state as a, plus the previously uncommited changes)
\
C4 <<< a-copy <<< HEAD (the merge taking only C3 side)
/
o <<< b (anywhere in the tree)
结束状态:
C1---C2 <<< a
\
C3 (same state as a, plus the previously uncommited changes)
\
C4 <<< b <<< HEAD
/
o
答案 1 :(得分:1)
实际上,git stash
确实保存当前的工作树。问题是保存它的 way 并不直接适合您的需求。还有一个次要问题,可能是一个非常棘手的问题,但反而可能很小。请参阅下面的警告。但是最后,您 也许可以使用git stash
做您想要的事情。
首先,请记住,Git根本不从您的工作树(您的工作目录)中进行提交,并且提交是所有文件的完整快照。它们是根据 index 而不是工作树创建的快照。新提交中的文件是现在索引为 的文件。
(还要记住,索引是Git的其他部分称为暂存区或有时称为 cache 的东西。它保存了每个文件的一个副本,该副本将进入 next 提交。该副本最初是从 current 提交获取的副本,除非Checkout another branch when there are uncommitted changes on the current branch中提到的某些极端情况。)
如果您的工作树与索引不同,并且您想对您的工作树进行快照,则需要git add
每个文件,然后覆盖索引中的 副本。可以提交。当然,这会破坏您在索引中 所做的任何仔细的分阶段。
但这就是git stash
真正使两次提交的原因:
(实际上存在第三个可选提交,用于保存未跟踪或未跟踪加忽略的文件。如果存在该提交,则它是工作树提交的第三个父级。但是,通常它不存在。)
已经进行了这两次提交,git stash
更新了refs/stash
以记住工作树提交w
的哈希ID。该提交会记住索引提交i
的哈希ID以及当前提交的哈希ID:
...--o--o--T <-- your-branch (HEAD)
\ |\
\ i-w <-- refs/stash
\
o--A <-- b
然后git stash
运行git reset --hard
,以便您的索引和工作树返回到匹配的提交T
。正如其他分支A
所指出的,我突出了另一个提交b
。
1 从技术上讲,git stash
使用第二个辅助/临时索引进行提交w
,以防万一出错。在这种情况下,它可以放弃临时索引。不过,使索引提交i
非常容易,因为管道命令git write-tree
完成了所有工作。
请记住这里的警告:git stash
实际上只是对索引中已经的所有文件执行git add
。任何未跟踪的文件(包括所有未跟踪和忽略的文件)根本不在提交w
中。他们只是坐在您的工作树上。确实是即使,如果您做了git checkout A
来提交A
,其中有些文件将被复制到您的索引中。 (当然,在这种情况下,您通常会抱怨Git首先需要覆盖一些未跟踪的文件。)
无论如何,除了这一重大警告外,隐藏提交w
在其快照中具有您刚刚要提交的快照{{1 }}。
现在,此快照已存在,您可以告诉Git进行新的提交A
,该提交以B
作为父节点,并以A
的 tree 作为其快照。这需要一个Git管道命令:
w
也就是说,我们使用名称git commit-tree -p refs/heads/b refs/stash^{tree}
(分支refs/heads/b
,指向提交b
)来告诉Git parent 哈希ID应该是什么我们的新承诺。我们使用A
来告诉Git新提交的 tree (快照)应该是什么。 Git读取标准输入以收集日志消息-如果愿意,可以添加refs/stash^{tree}
或-m <message>
来提供消息,或向标准输入发送一条消息:
-F <file>
结果是:
echo some message | git commit-tree -p refs/heads/b refs/stash^{tree}
其中新提交...--o--o--T <-- your-branch (HEAD)
\ |\
\ i-w <-- refs/stash
\
o--A <-- b
\
B
与隐藏提交B
具有相同的快照。
w
命令打印新提交的哈希ID。您需要抓住它(也许将其捕获到一个shell变量中),然后很可能设置一些名称(例如git commit-tree
)来记住此提交。例如:
refs/heads/b
给予:
hash=$(git commit-tree -p refs/heads/b refs/stash^{tree})
git update-ref -m "add stashed work-tree commit" refs/heads/b $hash
也就是说,新提交...--o--o--T <-- your-branch (HEAD)
\ |\
\ i-w <-- refs/stash
\
o--A--B <-- b
现在是现有分支b
的技巧。 b
中的快照是B
中的快照;它们是自动共享的。 w
中的 log消息与您给B
的内容相同。 git commit-tree
的哈希ID 现在存储在B
中,并且b
的父代是B
,因此此新提交在分支上A
,随心所欲。
现在所有 操作都已完成,您将想要恢复b
删除的索引和工作树,但首先将它们保存在这两个提交中。为此,请使用git stash
。 git stash pop --index
很重要:它将当前索引与--index
进行比较,并使用差异来还原索引。 2 然后将当前工作树与{{1} },并使用差异从i
恢复您的工作树。然后,其中的w
部分将丢弃w
个提交,如果还有其他隐藏的提交,则使pop
记住正确的提交。
因此,忽略所有可能发生错误的地方并进行所有适当的错误检查,以下命令序列可能会根据您的实际情况执行所需的操作你想要吗?
i-w
这是完全未经测试的(并且有一些错误的失败模式,特别是refs/stash
表示没有任何可保存的内容,并且拒绝执行任何操作)。
2 这是直接将git stash push # and make sure it does something
hash=$(echo automatic commit of work-tree |
git commit-tree -p refs/heads/b refs/stash^{tree})
git update-ref -m "add stashed work-tree commit" refs/heads/b $hash
git stash pop --index
读入索引的一种低效方式,但它实现了相同的目标。 git stash push
步骤也是如此。
答案 2 :(得分:1)
git reset --soft(如果您的朋友)。如果希望B之后的修订成为您在工作树上的方式(到目前为止尚未提交),可以执行以下操作:
git checkout --detach # disconnect from A
git reset --soft b # set the branch pointer to whatever revision B is pointing to.... Your working tree will be unaffected
git add . # add everything to index
git commit -m "revision after B that made it look the way I had it where I was working"
# if you like everything, move b branch to this revision and push
git branch -f b
git checkout b
git push some-remote b
那应该做。