Git rebase方法来避免重复提交 [英] Git rebase approach to avoid duplicate commits

查看:116
本文介绍了Git rebase方法来避免重复提交的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我尝试从master到开发分支重新建立基础时,我面临一个奇怪的问题.master和dev分支中正在进行一些工作.

There is strange issue I am facing when I am trying to do the rebase into my development branch from master. There is some work in progress in the master as well as in dev branches.

为了从大师那里获得更改,我在下面做了

In order to get the changes from master I do below

git checkout dev
git rebase master

这很好.但是-

  1. dev 分支提交在执行时正在复制git checkout开发git添加git commit -m'一些变化'git推git rebase master在开发人员分支的git项目历史记录中重复执行commit 一些更改.它还会创建循环.
  2. 如果在推送dev分支更改之前执行 git rebase master ,则它运行良好,并且我可以在项目中看到线性历史记录.
  1. The dev branch commits are duplicating when I do git checkout dev git add . git commit -m 'some change' git push git rebase master the commitsome change is repeating in git project history for dev branch. It also creates the loop .
  2. If I do git rebase master before pushing the dev branch changes then it works well and I can see linear history in project.

我不确定为什么会这样.在我看来,这是奇怪的行为.如果有人可以解释,请提供帮助.

I am not sure why it is like that. This seems to me the strange behaviour. Please help if anyone can explain this.

项目历史记录提交 b48659d 是重复的(我最初进行过推送,然后在dev分支中重新建立基础时我做了 37c07a4 的原始提交.

Project History commit b48659d is duplicating(original commit I did 37c07a4) when first did push and then rebase in dev branch.

*   fbadb86 (HEAD -> dev) Merge branch 'dev' of *******
|\  
| * b48659d (origin/dev) sample added in dev branch --> did rebase after push and got this commit again
* | 37c07a4 sample added in dev branch --> commit + push
* | 46c1f40 (origin/master, origin/HEAD, master) master file added in master branch --> commit + push
|/  
* 5baae80 first commit

在进行推送之后,所有提交都会发生这种情况.在提交重复时,这会在重新设置基准方面引起很多问题,即使我自己对先前提交的代码进行了更改,也会造成很多冲突.

And this happens for all commits after doing the push. That is causing lots of issue in rebasing as the commits are repeating and also creating lot off conflicts even with my own code changes from previous commits.

推荐答案

所有提交始终都是只读的.它们也是永久的,但有一些例外(与是否有人可以找到有关).这意味着 git rebase 副本提交.这就是工作!

All commits are read-only, always. They are also permanent, with some exceptions (having to do with whether anyone can find them). This means that git rebase copies commits. That's it's job!

让我们看一下您的 git log --graph --oneline ... 输出,但让我们开始更简单一些:

Let's take a look at your git log --graph --oneline ... output, but let's start a bit simpler:

  * b48659d (dev) ...
* | 46c1f40 (origin/master, origin/HEAD, master) ...
|/
* 5baae80 first commit

请注意缩短的哈希ID,例如 5baae80 b48659d .这些是每个提交的真实名称",因为通常就足够了,所以缩短到仅7个字符. 1 每个提交都记录一个 parent 提交的ID,Git使用这些父提交哈希从最新的分支提示提交开始,沿历史的顺序向后跟随每个提交.现在,分支 dev 的尖端提交为 37c07a4 ... :

Note the shortened hash IDs, such as 5baae80 and b48659d. These are the "true names" of each commit, shortened to just 7 characters since that's usually sufficient.1 Each commit records the ID of a parent commit, and Git uses these parent commit hashes to follow each commit backwards through history, starting from a more-recent branch tip commit. The tip commit of branch dev is now 37c07a4...:

5baae80--46c1f40   <-- master, origin/master
       \
        b48659d   <-- dev

这些提交都无法更改!

现在,您将其 git push 转到 origin . git push 命令在作为 origin 的计算机上调用第二个Git,并移交您没有的提交,每个人都同意根据他们的ID.然后,您的Git要求其Git将其 dev (将是您的 origin/dev )设置为与您的 dev 相同的值: b48659d ... 如果他们同意此请求,您的Git会记住他们现在有他们的 dev -您的 origin/dev -指向 b48659d ,就像我们在此处绘制的那样,所以现在我们有了:

You now git push this to origin. The git push command calls up a second Git, over on the machine acting as origin, and hands over the commits that you have that they don't, which everyone agrees to by their IDs. Then your Git asks their Git to set their dev, which will be your origin/dev, to the same value as your dev: b48659d... If they agree to this request, your Git remembers that they now have their dev—your origin/dev—pointing to b48659d, just as we have drawn here, so now we have this:

5baae80--46c1f40   <-- master, origin/master
       \
        b48659d   <-- dev, origin/dev

此时,您运行 git rebase ,这就是开始出错的地方.

At this point, you run git rebase, and this is where things start to go wrong.

1 Git现在动态地选择缩短的长度,而不是总是使用7,但是默认情况下它以7开头.您可以随时使用更多:全名目前为40个字符.

1Git now dynamically chooses the shortened length, rather than just always using 7, but it starts with 7 by default. You can always use more: the full names are currently 40 characters long.

git rebase 命令不能更改任何现有的提交,因为Git中的没有可以做到.Git旨在使这成为不可能.因此,它甚至不尝试.相反,它会复制提交.

The git rebase command cannot change any existing commit, because nothing in Git can do that. Git is designed to make this impossible. So it does not even try. Instead, it copies commits.

复制提交时,Git需要两条信息:

When copying commits, Git needs two pieces of information:

  • 应复制哪些提交内容?
  • 应将副本放在何处?

Git通过列出从 HEAD 开始并向后工作的提交(从您选择的某个点停止)来获得第一个(要复制的提交)列表.(此列表是向后的,因此必须将其反转.)Git从您提供的参数(例如 master )中获得第二个目标(目标为之后"复制点).

Git gets the first—the list of commits to copy—by listing commits starting from HEAD and working backwards, stopping at some point you choose. (This list is backwards so it has to reverse it.) Git gets the second—the target "copy after" point—from the argument you give it, such as master.

在这种情况下, HEAD 命名为 dev (因为在开始改基之前运行了 git checkout dev ),因此提交要复制以名称 dev 指向的那个结尾:

In this case, HEAD names dev (because you run git checkout dev before you start the rebase), so the commits to copy end with the one to which the name dev points:

5baae80--46c1f40   <-- master, origin/master
       \
        b48659d   <-- dev (HEAD), origin/dev

因此,我们将复制一些在 b48659d 处停止的提交.

We will therefore copy some series of commits stopping at b48659d.

复制它们的地方来自您的参数 master .名称 master 标识提交 46c1f40 ,因此副本将位于 46c1f40 之后.

The place to copy them comes from your argument master. The name master identifies commit 46c1f40, so the copies will go after 46c1f40.

棘手的部分是Git如何计算要复制 not 的提交.它是从 b48659d 开始并向后工作的.您可以想象,Git会为这些提交中的每一个临时涂上绿色.这会将Git带到 5baae80 ,它没有 no 父提交(这是存储库中的第一个提交,因此不能有父提交).这将停止行走,因此这两个提交被涂成绿色.然后,Git从您通过名称指定的提交开始- 46c1f40 -并临时将提交涂成红色. 46c1f40 的父代是 5baae80 ,因此Git将其涂成红色(不要复制),并尝试继续其父代将其涂成红色.没有父母,所以我们完成了临时绘画,只剩下一个绿色提交,即 b48659d .

The tricky part is how Git computes which commits not to copy. It does this by starting from b48659d and working backwards. You can imagine Git coloring each of these commits green temporarily. This takes Git to 5baae80, which has no parent commit (it is the first commit in the repository, so it cannot have a parent). This stops the walk, so that these two commits are painted green. Then Git starts from the commit you specified by name—46c1f40—and painting the commits red temporarily. The parent of 46c1f40 is 5baae80, so Git paints this one red (don't copy), and tries to go on to its parent to paint those red as well. There is no parent, so we're done with the temporary painting, leaving one green commit, b48659d.

这是要复制的提交列表.(它是向后的,但是无论如何它只是一个条目,所以反转它什么也没做.)

This is the list of commits to copy. (It's backwards, but it's only one entry long anyway, so reversing it does nothing.)

现在,Git开始复制过程.每个要复制的提交都被复制,就像通过 git cherry-pick 复制一样(如果您使用 git rebase -i ,它实际上是由Cherry-pick复制的).这使得 new 提交.

Now Git begins the copying process. Each commit to be copied is copied as if by git cherry-pick (if you use git rebase -i, it's literally copied by cherry-pick). This makes new commits.

从现有的 b48659d 进行的新提交为 37c07a4 .让我们画一下:

The new commit made from the existing b48659d is 37c07a4. Let's draw it in:

                 37c07a4
                /
5baae80--46c1f40   <-- master, origin/master
       \
        b48659d   <-- dev, origin/dev

现在,提交的整个列表已被复制, git rebase 会做最后一件事.它将名称 dev 从原始的提交字符串中删除,并将其粘贴到新副本上.结果是:

Now that the entire list of commits has been copied, git rebase does one last thing. It yanks the name dev off the original string of commits, and pastes it onto the new copy. The result is:

                 37c07a4   <-- dev (HEAD)
                /
5baae80--46c1f40   <-- master, origin/master
       \
        b48659d   <-- origin/dev

您可以在问题中引用的 git log --graph 输出中看到此内容.

You can see this in the git log --graph output you quoted in your question.

剩下的一个问题是,这是从哪里来的?

The one remaining question is, where did this come from:

*   fbadb86 (HEAD -> dev) Merge branch 'dev' of *******

该提交存在是因为您运行了 git merge .

That commit exists because you ran git merge.

您可能通过运行 git pull 来运行了 git merge .默认情况下, git pull 的作用是为您运行 git fetch ,然后为您运行 git merge .(我建议Git的初学者绝对不要使用 git pull :它的作用及其执行方式会造成极大的混乱.将其拆分为 git fetch ,迟早要通过第二个Git命令,有时是 git rebase ,有时是 git merge .)

You probably ran git merge by running git pull. What git pull does, by default, is run git fetch for you, and then run git merge for you. (I advise beginners in Git never to use git pull at all: what it does, and the way it does it, is excessively confusing. Split it up into git fetch followed, sooner or later, by a second Git command, sometimes git rebase, sometimes git merge.)

git merge 的作用通常是尝试将两条单独的发展路线"结合起来.给定这样的图形:

What git merge does, in general, is to try to combine two separate "lines of development". Given a graph like this:

                 37c07a4   <-- dev (HEAD)
                /
5baae80--46c1f40
       \
        b48659d   <-- origin/dev

Git或多或少地认为,当您编写 37c07a4 时,有人编写了 b48659d ( origin/dev ).为了组合,Git找到了它们的合并基础",这是这两行再次合并的地方.顺着眼睛看清楚了: 5baae80 .因此,Git进行了一个新的 merge commit ,这是一个有两个父级的提交.让我们来画一下:

Git thinks, more or less, that someone else wrote b48659d (origin/dev) while you wrote 37c07a4. In order to combine them, Git finds their "merge base", which is where these two lines join up again. Following the lines by eye makes it clear: that's 5baae80. So Git makes a new merge commit, which is a commit with two parents. Let's draw that in:

                 37c07a4--fbadb86   <-- dev (HEAD)
                /         /
5baae80--46c1f40         /
       \       __--------
        b48659d   <-- origin/dev

这就是您现在看到的,在您的 git log --graph --oneline ... 输出中(使用 master origin/master 箭头也加入了-我将它们省略了,因为它们很难用这种方式绘制).

This is what you see now, in your git log --graph --oneline ... output (with the master and origin/master arrows also added in—I left them out because they're just too hard to draw this way).

这里的基本错误是在已经有人的提交上运行 git rebase .在这种情况下,另一个位于 origin 的Git已经提交了 b48659d .您无法更改该提交,因此在制作新副本时,您将停止使用您的 b48659d ,但是它们仍然具有它们.最终,您必须通过合并将他们的"和您的副本"合并在一起,以提供这张相当混乱的图片.

The fundamental error here was in running git rebase on commits that someone else already had. In this case, the other Git over at origin already had commit b48659d. You cannot change that commit, so when you make your new copy, you stop using your b48659d, but they still have theirs. Eventually you have to incorporate "theirs" back with "your copy", by merging, giving this rather messy picture.

(此规则有一个例外:如果您可以说服拥有 b48659d 其他人放弃它,并切换其 dev s来使用新副本,您仍然可以重新设置基准.但是是否可以以及如何使用,是另一个问题.

(There is an exception to this rule: if you can convince everyone else who has b48659d to give it up, and switch their devs to use your new copy instead, you can still rebase. But whether you can, and how, is another question.)

这篇关于Git rebase方法来避免重复提交的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆