简体   繁体   English

如何在创建的提交上将分支合并到 master

[英]how to merge branch into master on the created commit

I have this senario that i created a branch A from master where the master head was on 1234 commit id我有这个场景,我从 master 创建了一个分支 A,其中 master 负责人在1234提交 id

and now some developers pushed more branchs and commits into master and now the master head is on 789 commit id.现在一些开发人员将更多的分支和提交推送到 master 中,现在 master 负责人在789提交 id 上。

Is it possible to merging the branch A into master on the commit 1234 ?是否可以在提交1234时将分支 A 合并到 master 中?

Don't try to push this analogy too far (because it will break down) but that's a bit like asking if you can get your package onto the cargo ship in China now that the ship is in port in LA.不要试图把这个比喻推得太远(因为它会崩溃),但这有点像问你是否可以将你的 package 放在中国的货船上,因为船在洛杉矶的港口。 You can, but you'll have to get the ship back to China first somehow.你可以,但你得先把船弄回中国。

Instead of the above klunky analogy, let's look at how Git really works.让我们看看 Git 是如何工作的,而不是上面的笨拙的类比。 Git is all about commits, and—as you've correctly described here—these commits are numbered . Git 都是关于提交的,而且——正如你在这里正确描述的——这些提交被编号 The number aren't simple, sequential counting numbers, though—commit #52 is not followed by commit #53 for instance—but instead they're big, ugly, random-looking hash IDs.不过,这个数字并不是简单的连续计数数字——例如,提交 #52 后面没有提交 #53——而是它们是大的、丑陋的、看起来随机的 hash ID。 So I like to represent them with single uppercase letters instead:所以我喜欢用单个大写字母来表示它们:

...--G--H   <-- master

Here, the name master points to commit H .在这里,名称master指向提交H H is the latest commit that is on master . Hmaster上的最新提交 Commit H itself reaches backwards to earlier commit G , which reaches backwards to yet another earlier commit, and so on.提交H本身向后到达较早的提交G ,后者向后到达另一个较早的提交,依此类推。

In your case, you created a new name also pointing to commit H and then made some new commits:在您的情况下,您创建了一个新名称也指向提交H ,然后进行了一些新提交:

          I--J   <-- your-branch
         /
...--G--H   <-- master

Unfortunately for you, since then, someone else has added more commits to master , like this:不幸的是,从那时起,其他人master添加了更多提交,如下所示:

          I--J   <-- your-branch
         /
...--G--H
         \
          K--L   <-- master

You're now asking about using git merge .您现在询问是否使用git merge

Merging is about combining work合并是关于合并工作

When we do use git merge , we do this because we'd like to combine work .当我们确实使用git merge时,我们这样做是因为我们想合并工作 That is, you started with commit H —some particular set of files, stored forever in a commit with some big ugly hash ID.也就是说,你从提交H开始——一些特定的文件集,永远存储在一个带有一些丑陋的大 hash ID 的提交中。 You changed a file or two and made a new commit, which got some other big ugly random-looking ID that I'm calling I , and maybe changed yet another file or two (or the same file or two) and made another commit J .您更改了一个或两个文件并进行了新的提交,它得到了一些我称之为I的其他大而丑陋的随机 ID,并且可能更改了另一个或两个文件(或相同的文件或两个)并进行了另一个提交J . So between H and J , you did some work, resulting in some changes to some files.所以在HJ之间,你做了一些工作,导致一些文件发生了一些变化。

Git can show you what you did (summarized into one big all-at-once set of changes) by comparing the snapshot in commit H —the set of files frozen for all time from which you started—to the snapshot in your latest commit, I . Git 可以通过将提交H中的快照(从您开始的所有时间冻结的文件集)与您最近提交中的快照进行比较,向您展示您所做的(总结为一组大的一次性更改), I This will have instructions like: in file main.py , delete line 47, and add some lines after line 300 .这将有如下指令:在文件main.py中,删除第 47 行,并在第 300 行之后添加一些行

Git can also show you what other people did by comparing what's in the snapshot in H —note that this snapshot is shared on both branches —with what's in the latest snapshot on master , in commit K . Git 还可以通过比较H中的快照中的内容(注意此快照在两个分支上共享)与提交Kmaster上的最新快照中的内容,向您展示其他人做了什么。 This may have instructions like change line 7 in README.md , along with instructions like add a line at line 100 in main.py .这可能包含更改 README.md 中的第 7 行README.md类的指令,以及诸如main.py中的第 100 行添加一行的指令

What git merge can do for you—or anyone—is to combine these changes . git merge可以为您或任何人做的就是结合这些更改 That is, if they touched README.md and you didn't, Git will keep their changes.也就是说,如果他们接触README.md而你没有接触,Git 将保留他们的更改。 If you touched Documentation/info.md and they didn't, Git will keep your changes.如果您触摸了Documentation/info.md而他们没有,Git 将保留您的更改。 If you and they both modified main.py , Git will try to keep both sets of changes to that one file.如果您和他们都修改main.py , Git 将尝试保留对该文件的两组更改

If Git can combine all these changes on its own, Git will make a single new commit on its own.如果 Git可以自己组合所有这些更改,则 Git 将自己进行一个新的提交。 This new commit will have, as its snapshot, all the files from the snapshot in H —the common starting point—with the combined changes applied, so that Git keeps your changes and adds theirs, or—if you prefer to look at it this way—keeps their changes and adds yours.这个新的提交将包含来自H中快照的所有文件(通用起点)作为其快照,并应用组合更改,以便 Git 保留您的更改并添加他们的更改,或者 - 如果您更喜欢查看它方式 - 保留他们的更改并添加您的。 The result is the same either way, and it looks like this:结果都是一样的,看起来像这样:

          I--J
         /    \
...--G--H      M
         \    /
          K--L

What I've left off here is which branch holds the new commit M .我在这里留下的是哪个分支拥有新的提交M There are two namesmaster and your-branch —and Git will only update one of these two names so that it points to new commit M .有两个名称——master 和your-branch master并且 Git 只会更新这两个名称之一,以便它指向新的提交M The other name will continue to point to either commit J , or to commit L (whichever one it pointed-to before).另一个名称将继续指向提交J或提交L (无论它之前指向哪个)。

I've also left off another detail, which sometimes does matter: since commit M has two backwards links, or parents , pointing to commits J and L both, one of these two comes first.我还省略了另一个细节,这有时确实很重要:因为提交M两个反向链接,或者parents ,指向提交JL ,所以这两个中的一个首先出现。 The other one is second, although for Git, that's just "not first".另一个是第二个,尽管对于 Git,这只是“不是第一”。 The --first-parent flag, later, can sometimes be useful to pick out the "first" parent.稍后, --first-parent标志有时对于挑选“第一个”父级很有用。 The snapshot in M is the same, no matter how you do the merge, but the choice of first-parent depends on which branch you git switch to, and which name you give to git merge :无论您如何进行合并, M中的快照都是相同的,但是第一父级的选择取决于您git switch到哪个分支,以及您给git merge的名称:

git switch master
git merge your-branch

gives us:给我们:

          I--J   <-- your-branch
         /    \₂
...--G--H      M   <-- master
         \    /¹
          K--L

That is, master is the name that moves to point to M , and the first parent is now L .也就是说, master是指向M名称,而第一个父级现在是L

This is usually what most people want to do—unless, that is, they want to use git rebase .这通常是大多数人想要做的——除非他们想使用git rebase

Is it possible to merging [my branch] into master on the [shared] commit?是否可以在 [shared] 提交时将 [my branch] 合并到 master 中?

Not exactly, because no matter what you do, master needs to point to some commit other than H in the end.不完全是,因为无论你做什么, master最终都需要指向除H之外的其他提交。

You can "eject" commits KL , like this:可以“弹出”提交KL ,如下所示:

          I--J   <-- your-branch
         /
...--G--H   <-- master
         \
          K--L   [abandoned]

The problem with doing this is that commits K and L are now lost .这样做的问题是提交KL现在丢失了 They're not gone —they are still there in the repository—but Git finds commits by starting with branch names.它们并没有消失——它们仍然在存储库中——但是 Git 通过以分支名称开头来查找提交。 The name master used to find commit L .用于查找提交L的名称master From L , Git could work backwards to K .L , Git 可以向后工作到K

Git is unable to work forwards . Git无法继续工作。 Git always works backwards . Git 总是向后工作。 So once you move master back two steps, to point to H , Git can't find K any more.因此,一旦您将master向后移动两步,指向H , Git 就再也找不到K了。 Even if Git could find K , that would not help to find L .即使 Git可以找到K ,也无助于找到L So these two commits are now "abandoned".所以这两个提交现在被“放弃”了。 This throws away their work .丢掉了他们的工作 That's not what merging is for: merging is useful to keep someone else's work while adding yours , or, equivalently, to keep your work while adding someone else's .这不是合并的目的:合并有助于在添加您的工作时保留其他人的工作,或者等效地,在添加其他人的同时保留您的工作

Rebasing变基

Without going into a lot of detail, the idea behind rebasing is that we wish to copy some set of commits to some new-and-improved commits.无需赘述,变基背后的想法是我们希望将一些提交复制到一些新的和改进的提交中。 In this particular case, you might wish to take your IJ commits, which you think are pretty good, and "improve" them by making two new commits—which for no as-yet obvious reason, we'll call I' (I-prime) and J' —that are a whole lot like I and J .在这种特殊情况下,您可能希望接受您认为非常好的IJ提交,并通过进行两次的提交来“改进”它们——由于目前还没有明显的原因,我们将其称为I' (I- prime) 和J'很像IJ

The difference between the new and improved I' , and the original I , is in two parts:新的和改进I'与原来的I之间的区别在于两部分:

  • The new I' will link backwards not to H , but to K .新的I'将不向后链接到H ,而是链接到K
  • The new I' will have, as its snapshot, not the snapshot in H modified with your changes, but rather the snapshot in K modified with your changes.新的I'作为它的快照,不是H中随您的更改而修改的快照,而是K中随您的更改而修改的快照。

The rest of I' will be exactly the same as I , including the commit log message . I'rest将与I完全相同,包括提交日志消息 Let's draw I' now:现在让我们画出I'

          I--J   <-- your-branch
         /
...--G--H
         \
          K--L   <-- master
              \
               I'  <-- temporary-branch

(The underlying Git command that produces I' from I is git cherry-pick . We have to have Git do this on a temporary branch , because Git needs to be able to find all the commits, including both the old ones and the new ones being built. The rebase command hides most of this complexity, but because Git is bad at certain things, sometimes you get to see it all.) (The underlying Git command that produces I' from I is git cherry-pick . We have to have Git do this on a temporary branch , because Git needs to be able to find all the commits, including both the old ones and the new ones正在构建。rebase 命令隐藏了这种复杂性的大部分,但是因为 Git 在某些事情上很糟糕,所以有时你会看到这一切。)

Once Git has copied I to I' , we need to have Git copy J to J' :一旦 Git 复制了II' ,我们需要让 Git 复制JJ'

          I--J   <-- your-branch
         /
...--G--H
         \
          K--L   <-- master
              \
               I'-J'  <-- temporary-branch

Once all the commits that were exclusively on your-branch are copied to the new temporary branch, we then have Git "peel the branch name" your-branch off commit J and paste it onto commit J' —the last copy Git just made—and throw out the temporary name:一旦将your-branch上的所有提交复制到新的临时分支,然后我们将 Git 从提交J中“剥离分支名称” your-branch并将其粘贴到提交J' — 刚刚制作的最后一个副本 Git —并丢弃临时名称:

          I--J   [abandoned]
         /
...--G--H
         \
          K--L   <-- master
              \
               I'-J'  <-- your-branch

Because Git finds commits by branch name, if we look at this repository now, it will seem as though we waited until commit L existed, and only then began working and producing commits IJ .因为 Git 按分支名称查找提交,所以如果我们现在查看这个存储库,看起来好像我们等到提交L存在,然后才开始工作并生成提交IJ We'll see commits I' and J' , not I and J , but unless we memorize the actual hash IDs—and no human ever does that —we won't notice that we're now looking at two different commits.我们将看到提交I'J' ,而不是IJ ,但除非我们记住实际的 hash ID——而且没有人这样做过——否则我们不会注意到我们现在正在查看两个不同的提交。 (Git will know. Git looks at the raw hash IDs. But we will forget, because that's what humans do.) (Git 会知道。Git 查看原始的 hash ID。但我们会忘记,因为这是人类所做的。)

Whether and when to rebase is up to you是否以及何时重新设置取决于您

Because rebase replaces the old commits with new ones (new and supposedly improved), it's not always appropriate.因为 rebase用新的提交替换了旧的提交(新的并且据说是改进的),所以它并不总是合适的。 In particular, if you've already given your I and J commits to someone else , and they are depending on them —including their hash IDs—then saying "throw those away, use these new and improved ones instead" makes them do extra work.特别是,如果你已经将你的IJ提交给了其他人,并且他们依赖于他们——包括他们的 hash ID——然后说“把它们扔掉,改用这些新的和改进的”会让他们做额外的工作. Sometimes that's fine.有时这很好。 (It's OK if they've already agreed to do that, for instance.) Sometimes it's not. (例如,如果他们已经同意这样做,那也没关系。)有时不是。

If you have never given these commits to anyone else , though, they won't ever even know you did this.但是,如果您从未将这些提交给任何其他人,他们甚至不会知道您这样做了。 So for that particular case—"private" commits—it's safe to rebase, as long as you know what you're doing.所以对于那个特殊的情况——“私人”提交——只要你知道你在做什么,rebase 是安全的。

The plus to rebasing is that, in the end, the Git history "looks simpler" and people can think about it with less mental load.变基的好处是,最后,Git 的历史“看起来更简单”,人们可以用更少的精神负担来思考它。 The minus is that it's not what really happened: if you break something during a rebase, it can be hard to debug, and it's a lie.缺点是它不是真正发生的事情:如果你在 rebase 期间破坏了某些东西,它可能很难调试,这是一个谎言。 Is it a little white lie that makes things better, or is it a big ugly lie that causes death and destruction?是一个善意的谎言让事情变得更好,还是一个丑陋的大谎言导致死亡和毁灭? We don't know, and you might not know yet , until you try it out.我们不知道,您可能还不知道,直到您尝试一下。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM