压缩线性提交历史时发生冲突

发布于 2024-09-11 19:28:10 字数 1054 浏览 2 评论 0原文

当尝试压缩/修复线性分支时,我仍然需要进行手动合并,这怎么可能?该存储库已从 Subversion 转换。每个冲突要么是“自动挑选失败”,要么是“由于提交消息为空而中止提交”。后者我可以理解,但是 --fixup-empty 或其他东西会很有用。

典型输出:

user@machine:/path (master|REBASE-i)$ git add * && git rebase --continue 
[detached HEAD c536940] fixup!
 Author: John Doe <[email protected]>
 2 files changed, 57 insertions(+), 4 deletions(-)
Automatic cherry-pick failed.  After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'
Could not apply 8854a54... >6d5f180 foo
user@machine:/path (master|REBASE-i)$ git st
# Not currently on any branch.
# Unmerged paths:
#   (use "git reset HEAD <file>..." to unstage)
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#   both modified:      filename.ics
#
no changes added to commit (use "git add" and/or "git commit -a")

How is it possible that when trying to squash/fixup a linear branch I still have to do manual merges? The repo has been converted from Subversion. Every conflict is either "Automatic cherry-pick failed" or "Aborting commit due to empty commit message". The latter I could understand, but a --fixup-empty or something would be useful.

Typical output:

user@machine:/path (master|REBASE-i)$ git add * && git rebase --continue 
[detached HEAD c536940] fixup!
 Author: John Doe <[email protected]>
 2 files changed, 57 insertions(+), 4 deletions(-)
Automatic cherry-pick failed.  After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'
Could not apply 8854a54... >6d5f180 foo
user@machine:/path (master|REBASE-i)$ git st
# Not currently on any branch.
# Unmerged paths:
#   (use "git reset HEAD <file>..." to unstage)
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#   both modified:      filename.ics
#
no changes added to commit (use "git add" and/or "git commit -a")

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

匿名的好友 2024-09-18 19:28:10

这些工作:

git mergetool
git rebase --continue

These work:

git mergetool
git rebase --continue
·深蓝 2024-09-18 19:28:10

这是我的建议,用于实现您拥有某种 --fixup-empty 功能的想法:

git filter-branch --msg-filter "sed 's/^$/Unknown/'"

这会将空提交消息替换为“未知”,并且如果您想在使用 git-svn 转换具有一些空提交消息但无法转换的 Subversion 存储库,因为它失败并显示“由于空提交消息而中止提交”。

Here's my suggestion for achieving your idea of having some kind of --fixup-empty functionality:

git filter-branch --msg-filter "sed 's/^$/Unknown/'"

This replaces empty commit messages with 'Unknown' and is particularly useful if you want to do a rebase after using git-svn to convert a Subversion repository that had some empty commit messages but can't because it fails with "Aborting commit due to empty commit message".

枕头说它不想醒 2024-09-18 19:28:10

我遇到了同样令人费解的问题。我想将更改从一大堆顺序提交转换为单个提交,但是使用带有一系列挤压的交互式变基会令人沮丧地产生合并冲突。这是不久前的事了,我刚才无法在一个最小的例子中重现它。无论如何,我确实知道如何解决它,所以在这里:

我将提出两种不同的方法来完成帖子所要求的操作 - 一种笨重但“不那么可怕”,因为很清楚你要做什么正在做的,第二个是优雅的,仅使用 git 命令,但需要您更加相信自己对 git 的理解。

设置

假设您有一个提交 ID idA 作为初始状态,将提交 ID idB 作为最终状态,中间还有一堆其他提交。事实上,您想要压缩所有中间提交,这表明您不再关心如何从初始状态到最终状态 - 您只想要一个使您从 idA 点移动的提交指向idB

假设 idB 对应于当前的 HEAD 并说它也是您的 master 分支。我们将创建一个名为 squashed 的新分支,它具有与 master 相同的工作树内容,但在提交 ID idA.

解决方案 1

  1. git checkout master(您可能已经在 master 上)
  2. 使用 bash 或文件资源管理器将整个工作树复制到其他位置。只需确保忽略 .git 目录——这是大多数操作系统上的默认行为。因此,需要明确的是,打开存储库文件夹,选择全部,复制并将其粘贴到桌面上或任何您想要的位置的新文件夹中。
  3. git checkout -b scraped idA 创建一个名为 squashed 的新分支,并将 idA 作为其最新提交,并将其设为当前分支。
  4. master 的内容(您在步骤 2 中放在其他位置的内容)粘贴回 repo 文件夹中。如果您的文件资源管理器询问,请告诉它替换所有已更改的文件。
  5. 在存储库的顶层,git add .,然后git commit。您的新提交将包含从 idAidB 的所有更改。

解决方案 2

git branch squashed idA # make a new branch called `squash` with `idA` as its latest commit 
git checkout master # master is "idB" in this case.
git symbolic-ref HEAD refs/heads/squashed
git commit

瞧。当您使用symbolic-ref将HEAD移动到squash分支的尖端时,工作树状态和新HEAD位置之间的差异集将被计算并暂存。

我想回应一些人表示这是“危险”、“低级”git hack 的担忧。我将尽力解释它的工作原理,让您感到轻松自在。您的 .git 文件夹是所有版本化文件内容所在的位置。其中两个文件夹是 objectsrefsrefs 文件夹包含告诉 git 您的分支名称以及它们对应的提交的文件。例如,如果您打开 .git/refs/heads/master,您将看到 master 最新提交的提交 ID。 objects 文件夹包含一堆文件,这些文件位于具有两个字符的十六进制名称的子文件夹中。对象可以是多种不同的东西,包括补丁、提交和整个文件。在 .git 文件夹的顶层还有一个 index 文件。 这是一篇关于索引文件内容的精彩文章。对于当前的讨论,您需要了解的是,索引文件告诉您哪个对象(在 objects 文件夹中)对应于当前分支和提交上每个文件的最新提交版本。

在此背景下,解决方案的作用如下:symbolic-ref命令只是“突然”告诉 git 您位于 squash 分支而不是 master 分支不接触工作树。这意味着您的文件都对应于您了解和喜爱的 master 状态,但是 git 将此视为带有一堆未提交的 idA更改(即,index 文件指定所有文件的当前签出版本是 idA 的版本,这是衡量工作树更改的参考)。事实上,正是这些变化让您声明idB。这也正是解决方案 1 所做的。在这种情况下,由于 symbolic-ref 的实现方式,所有上述更改都已上演(即 git add-ed),因此您所要做的就是是git commit。这没有什么“危险”,因为它不会改变 mastergit 正在跟踪的任何 objects。您刚刚在 refs 文件夹中创建了一个名为 squashed 的新文件,并在 objects 文件夹中创建了一个与该新组合提交相对应的新文件。您的 master 就在您离开的地方,您只是有一个名为 squashed 的新分支,其内容相同,但提交历史记录较短。如果您不确定发生了什么,请放心,您可以git checkout master,它会在您离开的地方。

如果您想采用 squashed 作为新的 master,您可以继续,

git branch -m master old-master
git branch -m squashed master

然后您将拥有旧分支,其中所有多余的提交都保存为 old -mastermaster 正是您想要的。

希望这有帮助!

I ran into this same puzzling question. I wanted to turn the changes from a whole bunch of sequential commits into a single commit, but using interactive rebasing with a sequence of squashes was frustratingly giving merge conflicts. This was a while back, and I was not able to reproduce it in a minimal example just now. In any case, I do know how to solve it, so here you go:

I'll present two different ways of doing what the post asked -- one that is clunky but "less scary," because it is clear exactly what you're doing, and a second that is elegant and uses only git commands, but requires you to trust your understanding of git a bit more.

Setup

Let's suppose you have a commit ID idA as the initial state and commit ID idB as the final state, with a bunch of other commits in between. The fact that you want to squash all the in-between commits says that you no longer care how you got from the initial state to the final state -- you just want a commit that moves you from point idA to point idB.

Let's suppose that idB corresponds to the current HEAD and say that it's also your master branch. We're going to make a new branch called squashed that has the same working tree contents as master, but has just one single commit after commit ID idA.

Solution 1

  1. git checkout master (you might already be on master)
  2. Use bash or your file explorer to copy the whole working tree to somewhere else. Just make sure you leave out the .git directory -- which is the default behavior on most operating systems. So, to be clear -- open the repo folder, select all, copy, and paste it to some new folder, on your desktop or wherever you want.
  3. git checkout -b squashed idA to make a new branch called squashed with idA as its latest commit, and make it your current branch.
  4. Paste the contents of the master (that you put somewhere else in Step 2) back into the repo folder. If your file explorer asks, tell it to replace all the files that have changed.
  5. In the top level of your repo, git add . and then git commit. Your new commit will have all the changes that take you from idA to idB.

Solution 2

git branch squashed idA # make a new branch called `squash` with `idA` as its latest commit 
git checkout master # master is "idB" in this case.
git symbolic-ref HEAD refs/heads/squashed
git commit

And voilà. When you use symbolic-ref to move the HEAD to the tip of the squash branch, the set of diffs between the working tree state and the new HEAD location are computed and staged.

I want to respond to the concern that some have expressed that this is a "dangerous", "low-level" git hack. I will try to explain just enough about how it works that you can feel at ease. Your .git folder is where all the versioned file contents live. Two of the folders in it are objects, and refs. The refs folder contains files that tell git the names of your branches and what commits they correspond to. So if you open up .git/refs/heads/master, for example, you'll see the commit ID of the latest commit to master. The objects folder contains a bunch of files that are in subfolders with two-character hex names. The objects can be several different things including patches, commits, and whole files. Also in the top level of the .git folder is an index file. This is a great post on the contents of the index file. What you need to know for the present discussion is that the index file tells you what object (in the objects folder) corresponds to the latest committed version of each file on the current branch and commit.

Against that background, here is what the solution does: the symbolic-ref command simply tells git "suddenly" that you're on the squash branch instead of the master branch without touching the working tree. That means that your files all correspond to the state of master you know and love, but git is seeing this as idA with a bunch of uncommitted changes (that is, the index file specifies that the current checked out version of all the files are those of idA, and this is the reference against which working tree changes are measured). The exact changes, in fact, that get you to state idB. This is exactly what Solution 1 does as well. In this case, because of the way symbolic-ref is implemented, all of the said changes are already staged (i.e. git add-ed), so all you have to do is git commit. There is nothing "dangerous" about this, because it doesn't change master or any of the objects that git is keeping track of. You've just created a new file in the refs folder called squashed, and one new file in the objects folder corresponding to that new combined commit. Your master is right there where you left it, you just have a new branch called squashed with the same contents but a shorter commit history. If you aren't sure what's going on, rest assured you can git checkout master and it will be right there where you left it.

If you want to adopt squashed as your new master, you can go ahead and

git branch -m master old-master
git branch -m squashed master

And then you'll have the old branch with all the superfluous commits saved as old-master, and master will be just what you wanted.

Hope this helps!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文