简体   繁体   English

GIT:如何在两个不同的分支中维护相同的提交历史

[英]GIT : How to maintain same history of commits in two different branches

Using gitbash to merge and commit. 使用gitbash合并和提交。

Let me explain the basic structure first. 首先让我解释一下基本结构。 So we have origin/dev which we pull and start working on. 这样我们就可以提取并开始工作的origin / dev。 After the changes are done we push the changes to origin/dev. 更改完成后,我们将更改推送到origin / dev。

Then using gitbash to merge dev to qa,I do below 然后使用gitbash将dev合并到qa,我在下面做

git checkout qa

# for all recent changes in origin/qa(similar have parallel origing/dev and uat as well.)
git pull

# for checking out changes in dev to my local qa space which will be merged
# to origin/qa by the below commands
git checkout dev -- directorynameToCheckoutCodeFrom

git commit
git push

So this is the process normally followed between any 2 different environment when merging happens. 因此,合并是在任何两个不同环境之间通常遵循的过程。

So my issues is I make 5 commits for 5 issues in DEV all have different commit id. 所以我的问题是我对DEV中的5个问题进行了5次提交,所有提交都有不同的提交ID。 So when i merge from DEV to QA when I commit all five changes in 1, I get 1 commit id and all the changes will be merged in 1. Same happens when merging in UAT. 因此,当我在1中提交所有五个更改时,从DEV合并到QA时,我将获得1个提交ID,并且所有更改都将在1中进行合并。在UAT中进行合并时也会发生同样的情况。

Is there any way we can maintain same history between different environments. 有什么方法可以在不同环境之间维护相同的历史记录。 The real issues comes in QA we might merge 4-5 times in 10 days and in UAT we would like to keep intact and merge only once a month. 真正的问题来自质量检查,我们可能会在10天之内合并4-5次,而在UAT中,我们希望保持完整并每月仅合并一次。 In that case if we commit all the changes from QA to UAT as one commit the history which is different in QA will be lost. 在这种情况下,如果我们将所有从QA更改到UAT的更改作为一次提交提交,则QA中不同的历史记录将丢失。 Any way to tackle this? 有什么办法解决吗?

Gone through some posts online but was unable to understand, what i understood was the only way is making frequent commit as we doing in DEV env. 上网浏览了一些帖子,但无法理解,我了解的唯一方法就是像在DEV env中一样频繁进行提交。 For 1 issue merge in dev>then qa>the uat this is the only way to preserve the same history is my understanding correct. 对于在dev> then qa> uat中合并的1个问题,我认为这是保存相同历史记录的唯一方法。

There is not a history of commits . 没有提交历史 There are only commits; 只有提交; the commits are the history. 提交就是历史。

Each commit is uniquely identified by a hash ID. 每个提交都由哈希ID唯一标识。 That hash ID is the true name of the commit, as it were. 散列ID 真名的承诺,因为它是。 If you have that commit, you have that hash ID. 如果具有提交,则具有哈希ID。 If you have that hash ID, you have that commit. 如果您具有哈希ID,则具有提交。 Read out the big ugly hash ID and see if it's in your database of "all the commits that I have in this repository": ie, see if Git knows it. 读出大的丑陋哈希ID并查看它是否在“我在该存储库中拥有的所有提交”数据库中:即查看Git是否知道。 If so, you have that commit. 如果是这样,则您具有提交。 For instance, b5101f929789889c2e536d915698f58d5c5c6b7a is a valid hash ID: it's a commit in the Git repository for Git. 例如, b5101f929789889c2e536d915698f58d5c5c6b7a是有效的哈希ID:它是Git在Git存储库中的提交。 If you have that hash ID in your Git repository, you have that commit. 如果您在Git存储库中具有该哈希ID,则具有提交。

People don't normally type in, or use, these hash IDs at all. 人们通常根本不会键入或使用这些哈希ID。 Git uses them, but Git is a computer program, not a human. Git使用它们,但是Git是计算机程序,而不是人。 Humans don't do well with these things—I have to cut and paste the above hash ID or I'll get it wrong—so humans use a different way to get started. 人们在这些事情上做的不好-我必须剪切并粘贴上面的哈希ID否则我会弄错的-所以人类使用了不同的入门方法。 Humans use branch names . 人类使用分支名称 But many different Git repositories all have master and this master doesn't always (or ever!) mean that big ugly hash ID I typed in above. 但是许多不同的Git存储库都拥有master而这个master并不总是(或从来没有!)意味着我在上面键入的丑陋的哈希ID大。 So a name like master is specific to one particular Git repository, while hash IDs are not. 因此,像master这样的名称特定于一个特定的Git存储库,而哈希ID不是。

Now, every commit stores some stuff. 现在,每个提交都存储一些东西。 What a commit stores includes a snapshot of all the files that go with that commit, so that you can get it back out later. 什么提交商店包括所有承诺去文件的快照,这样就可以得到它后来退了出去。 It also includes the name and email address of the person who made that commit, so that you can tell who to praise or blame. 它还包括提出承诺的人的姓名和电子邮件地址,以便您可以说出要赞扬或责备的人。 😀 It includes a log message: why the person who made the commit says they made that commit. 😀其中包括一条日志消息: 为什么进行提交的人说他们进行了提交。 But—here's the first tricky part—almost every commit also includes at least one hash ID , which is the commit that comes before this particular commit. 但是-这是第一个棘手的部分-几乎每个提交还包括至少一个哈希ID ,这是此特定提交之前的提交。

So, if you have b5101f929789889c2e536d915698f58d5c5c6b7a , then what you have is this: 因此,如果您有b5101f929789889c2e536d915698f58d5c5c6b7a ,那么您所拥有的是:

$ git cat-file -p b5101f929789889c2e536d915698f58d5c5c6b7a | sed 's/@/ /'
tree 3f109f9d1abd310a06dc7409176a4380f16aa5f2
parent a562a119833b7202d5c9b9069d1abb40c1f9b59a
author Junio C Hamano <gitster pobox.com> 1548795295 -0800
committer Junio C Hamano <gitster pobox.com> 1548795295 -0800

Fourth batch after 2.20

Signed-off-by: Junio C Hamano <gitster pobox.com>

(The tree line represents the saved snapshot that goes with this commit. You can ignore this here.) The parent line gives the hash ID of the commit that comes before b5101f929789889c2e536d915698f58d5c5c6b7a . tree线表示此提交附带的已保存快照。您可以在此处忽略。) parent行给出b5101f929789889c2e536d915698f58d5c5c6b7a 之前的提交的哈希ID。

If you have b5101f929789889c2e536d915698f58d5c5c6b7a you almost certainly also have a562a119833b7202d5c9b9069d1abb40c1f9b59a . 如果您有b5101f929789889c2e536d915698f58d5c5c6b7a ,则几乎可以肯定也有a562a119833b7202d5c9b9069d1abb40c1f9b59a The history for the later commit is the earlier commit. 稍后提交的历史记录先前提交的历史。

If we replace each of these big ugly hash IDs with a single uppercase letter, 1 we can draw this sort of history a lot more easily: 如果我们用一个大写字母1替换这些大的丑陋哈希ID,则可以更轻松地绘制这种历史记录:

... <-F <-G <-H

where H is the last commit in a long chain of commits. 其中H是一长串提交中的最后一个提交。 Since H holds G 's hash ID, we don't need to write down G 's big ugly hash ID, we can just write down H 's hash. 由于H持有G的哈希ID,因此我们无需写下G的大丑陋哈希ID,我们只需写下H的哈希。 We use that to have Git find G 's ID, inside H itself. 我们用它来让Git在H本身内找到G的ID。 If we want F , we use H to find G to find F 's ID, which lets Git retrieve F . 如果需要F ,则使用H来找到G来找到F的ID,这使Git可以检索F

But we still have to write down that last hash ID. 但是我们仍然必须写下最后一个哈希ID。 This is where branch names come in. Branch names like master act as our way of saving the hash ID of the last commit. 这是分支名称的来源。分支名称(例如master充当我们保存上一次提交的哈希ID的方式。

To make a new commit, we have Git save the hash ID of H in our new commit. 为了进行新的提交,我们让Git 新的提交中保存H的哈希ID。 We have Git save a snapshot and our name and email address and all the rest of that as well—"the rest" includes a time stamp, the precise second when we had Git do all this. 我们让Git保存了快照以及我们的姓名和电子邮件地址,以及所有其余的内容-“其余”包括时间戳记,这是我们让Git完成所有操作的精确秒数。 Now Git computes the actual hash ID of all of this data, including the time stamp. 现在,Git计算所有这些数据的实际哈希ID,包括时间戳。 The commit is now saved in our database of all commits, and Git has given us a new hash ID I : 现在,提交已保存到我们所有提交的数据库中,并且Git为我们提供了新的哈希ID I

...--F--G--H   <-- master
            \
             I

We have Git automatically write I 's hash ID into our name master : 我们让Git 自动I的哈希ID写入我们的名称master

...--F--G--H--I   <-- master

and we've added new history, which retains all the existing history. 并且我们添加了新的历史记录,保留了所有现有的历史记录。


1 Of course, if we only used one uppercase letter like this, we'd run out of the ability to create commits, anywhere in the world, after creating just 26 commits. 1当然,如果我们只使用一个这样的大写字母,那么在仅创建26个提交之后,我们将无法在世界上任何地方创建提交。 That's why Git's hash IDs are so big. 这就是Git的哈希ID很大的原因。 They hold 160 bits so the number of possible commits or other objects is 2 160 or 1,461,501,637,330,902,918,203,684,832,716,283,019,655,932,542,976. 它们持有160位,因此可能提交或其他对象的数量为2 160或1,461,501,637,330,902,918,203,684,832,716,283,019,655,932,542,976。 As it turns out, this isn't really enough, and Git will probably move to a larger hash that can hold 79,228,162,514,264,337,593,543,950,336 times as many objects. 事实证明,这还远远不够,Git可能会移动到更大的哈希中,该哈希可以容纳79,228,162,514,264,337,593,543,950,336倍的对象。 While the first number is big enough to enumerate all the atoms in the universe, there are specific attacks that are troublesome, so a 256-bit hash is a good idea. 尽管第一个数字足以枚举宇宙中的所有原子,但存在一些特定的攻击比较麻烦,因此256位哈希是一个好主意。 See How does the newly found SHA-1 collision affect Git? 请参阅新发现的SHA-1碰撞如何影响Git?


This tells you how to have the same history 这告诉你如何拥有相同的历史

History is the commits. 历史就是承诺。 To have the same history in two branches, you need both branch names to point to the same commit: 要在两个分支中具有相同的历史记录,您需要两个分支名称都指向同一提交:

...--F--G--H--I   <-- master, dev

Now the history in master is: Starting at I , show I , then move back to H and show H , then move back to G ... Likewise, the history in dev is: Starting at I , show I , then move back to H ... 现在master的历史记录是: I开始,显示I ,然后返回到H并显示H ,然后返回到G ...同样, dev的历史记录是: I开始,显示I ,然后返回到H ...

Of course, that's not quite what you want. 当然,这不是您想要的。 What you want is to have history that diverges , then converges again . 您想要的是让历史发生分歧 ,然后再次趋同 That's what branches are really about: 那才是真正的分支:

...--F--G--H   <-- master
            \
             I   <-- dev

Here the history in dev starts (ends?) at I , then goes back to H , and then G , and so on. 在这里, dev的历史记录从I开始(结束?),然后回到H ,然后回到G ,依此类推。 The history in master starts (ends?) at H , goes back to G , and so on. master的历史记录从H开始(结束?),回到G ,依此类推。 As we add more commits, we add more history, and if we do it like this: 随着我们添加更多的提交,我们也添加了更多的历史记录,如果我们这样做的话:

             K--L   <-- master
            /
...--F--G--H
            \
             I--J   <-- dev

then the history of the two branches diverges . 那么这两个分支发散的历史。 Now master starts at L and works backwards, while dev starts at J and works backwards. 现在masterL开始并向后工作,而devJ开始并向后工作。 There are two commits on dev that are not on master , and two commits that are on master that are not on dev , and then everything from H on back is on both branches. dev上有两个不在 master提交,而master上有两个不在 dev上的提交,那么从H后面的所有东西都在两个分支上。

This divergence—the commits that are not on some branch—is where the lines of work diverge. 这种分歧( 不在某个分支上的提交)是工作线分歧之处。 The branch names still only remember one commit each , specifically the tip or last commit of each line of development. 分支名称仍然只记住每个提交一个提交 ,特别是每行开发的技巧最后一个提交。 Git will start at this commit, by the saved hash ID, and use that commit's saved parent hash ID to walk backwards, one commit at a time. Git将以保存的哈希ID从该提交开始,并使用该提交的已保存的哈希ID向后移动,一次提交一次。 Where the lines rejoin, the history rejoins. 线路重新合并的地方,历史重新合并。 That's all there is in a repository, except for the next section. 除了下一部分之外,这就是存储库中的所有内容。

Merges combine history (and snapshots) 合并合并历史记录(和快照)

What you can do now is make a merge commit . 您现在可以做的是合并提交 The main way to make a merge commit is using the git merge command. 进行合并提交的主要方法是使用git merge命令。 This has two parts: 这包括两个部分:

  • combining work , where Git figures out what has changed in each line of development; 结合工作 ,Git找出每个开发领域中发生了什么变化; and
  • making a merge commit , which is a commit with exactly one special feature. 进行合并提交 ,这是具有一项特殊功能的提交。

To make a merge, you start by picking one branch tip. 要进行合并,请先选择一个分支提示。 You run git checkout master or git checkout dev here. 您可以在此处运行git checkout mastergit checkout dev Whichever one you pick, that's the commit you have out now, and Git attaches the special name HEAD to that branch name to remember which one you picked: 无论您选择哪个,这就是您现在要提交的提交,并且Git将特殊名称HEAD附加到该分支名称以记住您选择了哪个:

             K--L   <-- master (HEAD)
            /
...--F--G--H
            \
             I--J   <-- dev

Now you run git merge and give it an identifier to choose the commit to merge . 现在,您运行git merge并为其指定一个标识符以选择要合并的提交。 If you're on master = L , you'll want to use dev = J as the commit to merge: 如果您使用master = L ,则需要使用dev = J作为合并的提交:

git merge dev         # or git merge --no-ff dev

Git will now walk the graph as usual to find the best shared commit—the best commit that's on both branches, to use as a starting point for this merge. Git现在将像往常一样遍历该图,以找到最佳的共享提交(这是两个分支上的最佳提交),以用作此合并的起点。 Here, that's commit H , where the two branches first diverge. 在这里,这是提交H ,两个分支首先分开。

Now Git will compare the snapshot saved with commit H —the merge base—to the one in your current commit L . 现在,Git将使用提交H合并基础)保存的快照与当前提交L的快照进行比较。 Whatever is different , you must have changed on master . 不管有什么不同 ,您都必须对master进行更改。 Git puts those changes into one list: Git将这些更改放在一个列表中:

git diff --find-renames <hash-of-H> <hash-of-L>   # what we changed

Git repeats this but with their commit J : Git的重复这一点,但他们承诺J

git diff --find-renames <hash-of-H> <hash-of-J>   # what they changed

Now Git combines the two sets of changes . 现在,Git 结合了两组更改 Whatever we changed, we want to keep changed. 无论我们进行了什么更改,我们都希望保持更改。 Whatever they changed, we want to use those changes too. 无论他们进行了什么更改,我们也都希望使用这些更改。 If they changed README.md and we did not, we'll take their change. 如果他们更改了README.md而我们没有更改,我们将接受他们的更改。 If we changed a file and they didn't, we'll take our change. 如果我们更改了文件但没有更改,我们将接受更改。 If we both changed the same file, Git will try to combine those changes. 如果我们都更改了同一文件,Git将尝试合并这些更改。 If Git succeeds, we have a combined change for that file. 如果Git成功,我们将对该文件进行合并更改。

In any case, Git now takes all of the combined changes and applies them to the snapshot in H . 无论如何,Git现在都会获取所有组合的更改,并将它们应用于H的快照。 If there were no conflicts, Git automatically makes a new commit from the result. 如果没有冲突,Git会自动从结果中进行新的提交。 If there were conflicts, Git still applies the combined changes to H , but leaves us with the messy result, and we have to fix it up and do the final commit ourselves; 如果冲突,Git会仍然适用合并更改H ,但给我们留下了凌乱的结果,我们必须解决它并做最后的承诺; but let's assume there were no conflicts. 但我们假设没有冲突。

Git now makes a new commit with one special feature. Git现在使用一项特殊功能进行新提交。 Instead of just remembering our previous commit L , Git has this merge commit remember two previous commits, L and J : Git 不仅仅是记住我们之前的提交L ,还拥有合并提交,还记得两个先前的提交LJ

             K--L   <-- master (HEAD)
            /    \
...--F--G--H      M
            \    /
             I--J   <-- dev

Then, as always, Git updates our current branch to remember the new commit's hash ID: 然后,与往常一样,Git更新我们的当前分支以记住新提交的哈希ID:

             K--L
            /    \
...--F--G--H      M   <-- master (HEAD)
            \    /
             I--J   <-- dev

Note that if we do the merge by running git checkout dev; git merge master 请注意,如果我们通过运行git checkout dev; git merge master进行合并git checkout dev; git merge master git checkout dev; git merge master , Git would do the same two diffs and get the same merge commit M (well, as long as we did it at the exact same second so that the time stamps match up). git checkout dev; git merge master ,Git将执行相同的两个比较并获得相同的合并提交M (好吧,只要我们在完全相同的第二秒执行它,以便时间戳匹配)。 But then Git would write the hash ID of M into dev rather than into master . 但是随后Git会将M的哈希ID写入dev而不是master

In any case, if we now ask about the history of master , Git will start at M . 无论如何,如果现在我们询问master的历史,Git将从M开始。 Then it will walk back to both L and J and show both of them. 然后,它会走回两个 L J ,并显示他们两个 (It has to pick one to show first, and git log has a lot of flags to help you choose which one to show first.) Then it will walk back from whichever one it picked first, so that it now has to show both K and J , or both L and I . (它必须选择一个首先显示,而git log有很多标志来帮助您选择首先显示哪个 。)然后它将从它首先选择的那个后退,因此现在必须显示两个 KJ两者 LI Then it will walk back from whichever one it picked to show. 然后,它将从它选择显示的那一个走回去。

In most cases Git shows all the children before any of the parents, ie, eventually, it will have shown all four of I , J , K , and L and have only H to show. 在大多数情况下,Git会在所有父母之前显示所有孩子,也就是说,最终,它将显示IJKL全部四个,并且只显示H So from here, Git will show H , then G , and so on—there's now just one chain to walk back, one commit at a time. 因此,从这里开始,Git将显示H ,然后显示G ,依此类推-现在只有一条链可以向后走,一次提交一次。 But be aware that when you traverse back from a merge, you run into the which commit to show next problem. 但是请注意,当您从合并中遍历时,会遇到which提交以显示下一个问题。

git merge does not always make a merge commit git merge并不总是进行合并提交

Suppose you have this history: 假设您有以下历史记录:

...--F--G--H   <-- master
            \
             I--J   <-- dev

That is, there's no divergence , dev is merely strictly ahead of master . 也就是说,没有分歧dev 完全领先于 master You do git checkout master to select commit H : 您进行git checkout master选择提交H

...--F--G--H   <-- master (HEAD)
            \
             I--J   <-- dev

and then git merge dev to combine the work you've done since the merge base with the work they did since the merge base. 然后git merge dev将您自合并基础以来所做的工作与他们自合并基础以来所做的工作相结合。

The merge base is the best shared commit. 合并基础是最佳共享提交。 That is, we start at H and keep going back as needed, and also start at dev and keep going back as needed, until we reach a common starting point. 也就是说,我们从H开始并根据需要继续前进,也从dev开始并根据需要继续前进,直到达到一个共同的起点。 So from J we go back to I and to H , and from H we just sit quietly at H until J goes back here. 因此,从J我们回到IH ,并从H我们只是静静的坐在 H直到J回到这里。

The merge base, in other words, is the current commit . 换句话说,合并基础 当前commit If Git ran: 如果Git跑了:

git diff --find-renames <hash-of-H> <hash-of-H>

there would be no changes . 不会有变化 The act of combining no changes (from H to H via master ) with some changes (from H to J via dev ), then applying those changes to H , is just going to be whatever is in J . 不进行任何更改 (从HmasterH )与某些更改 (通过devHJ )相结合,然后将这些更改应用于H ,将等于J任何值。 Git says: well, that was too easy and instead of making a new commit, it just moves the name master forwards , in the opposite of the usual backwards direction. Git说: 太好了,这太容易了 ,而不是进行提交,它只是名称master 向前 移动 ,而与通常向后的方向相反。 (In fact, Git really did work backwards—from J to I to H —in order to figure this out. It just remembers that it started from J .) So what you get here, by default, is this: (实际上,Git确实做了从JIH反向工作,以便弄清楚。它只记得它是从J开始的。)因此,默认情况下,您得到的是:

...--F--G--H
            \
             I--J   <-- dev, master (HEAD)

When Git is able to slide a label like master forward like this, it calls that operation a fast-forward . 当Git能够像这样向前滑动诸如master类的标签时,它将该操作称为快速前进 When you do this with git merge itself, Git calls it a fast-forward merge , but it's not really a merge at all. 当您使用git merge本身进行此操作时,Git称其为快进合并 ,但这根本不是真正的合并。 What Git really did was to check out commit J , and make master point to J . Git真正做的是检出提交J ,并master J

In many cases, this is is OK! 在很多情况下,这还可以! The history is now: For master , start at J and walk back. 现在的历史是: 对于master ,从J开始然后向后走。 For dev , start at J and walk back. 对于dev ,从J开始,然后返回。 If that's all you need and care about, that's fine. 如果这就是您需要和关心的,那很好。 But if you want a real merge commit—so that you can tell master and dev apart later, for instance—you can tell Git: Even if you can do a fast-forward instead of a merge, do a real merge anyway. 但是,如果您要进行真正的合并提交(例如,以后可以区分masterdev ,则可以告诉Git: 即使您可以执行快进而不是合并,也可以进行真正的合并。 Git will go ahead and compare H to H , and then compare H to J , and combine the changes and make a new commit: Git将继续并将HH进行比较,然后将HJ进行比较,并结合更改并进行提交:

...--F--G--H------K   <-- master (HEAD)
            \    /
             I--J   <-- dev

Now you get a real merge commit K , with two parents as required to be a merge commit. 现在,您将获得一个真正的合并提交K ,根据需要有两个父级才能成为合并提交。 The first parent is H as usual, and the second is J , as is usual for a merge commit. 第一个父级通常为H ,第二个父级为J ,与合并提交一样。 The history of master now includes the history of dev , but remains different from the history of dev , because the history of dev doesn't include commit K . master现在包括历史dev ,但仍历史不同的 dev ,因为历史上dev不包括犯K

Note that if you now switch back to dev and make more commits, the result looks like this: 请注意,如果现在切换回dev并进行更多提交,结果将如下所示:

...--F--G--H------K   <-- master
            \    /
             I--J--L--M--N   <-- dev (HEAD)

You can now git checkout master and git merge dev again. 现在,您可以再次git checkout mastergit merge dev This time you won't need --no-ff because there is a commit that's on master that's not on dev , namely K , and of course there are commits on dev that are not on master , namely LMN . The *merge base* this time is shared commit 这次您不需要--no-ff因为在master上的提交不在 dev ,即K ,当然在dev that are not on上的提交dev that are not on master上, namely LMN . The *merge base* this time is shared commit . The *merge base* this time is shared commit J (not H H is also shared, but J` is better ). . The *merge base* this time is shared commit J (not H H is also shared, but J` 更好 )。 So Git will combine changes by doing: 因此,Git将通过以下操作组合更改:

git diff --find-renames <hash-of-J> <hash-of-K>   # what did we change?
git diff --find-renames <hash-of-J> <hash-of-N>   # what did they change?

What did we change from J to K ? 我们得到什么改变从JK (That's an exercise for you, the reader.) (这是您的练习,读者。)

Assuming Git is able to combine the changes on its own, this merge operation will succeed, producing: 假设Git能够自行合并更改,则此合并操作将成功,并产生:

...--F--G--H------K--------O   <-- master (HEAD)
            \    /        /
             I--J--L--M--N   <-- dev

where new merge commit O combines the J -vs- K changes with the J -vs- N changes. 其中新的合并提交OJ -vs- K更改与J -vs- N更改组合在一起。 The history of master will start at O and will include N and M and L and K and J and I and H and so on. master的历史将从O开始,包括NMLKJIH等。 The history of dev will start at N and include M and L and J (not K !) and I and H and so on. dev的历史将从N开始,包括MLJ (不是K !)以及IH等。 Git always works backwards , from child to parent. 从孩子到父母,Git总是向后工作。 Merges let / make Git work backwards along both lines at the same time (but shown to you one at a time, in some order depending on arguments you supply to git log ). 合并让/使一起在同一时间 行Git的工作向后(但一次展现给你一个,在一些顺序根据的论点你提供给git log )。

you can try with 你可以尝试

git checkout qa

git merge dev --no-ff

git push

git merge dev --no-ff git merge dev --no-ff

is mostly use to pull all the dev branch commit to qa with their history. 主要用于将所有dev分支及其历史记录提交到qa。

In the process you describe, you want to 'merge' changes from an individual directory within a repo. 在您描述的过程中,您想从存储库中的单个目录中“合并”更改。 This is contrary to how git works, and that is why you're having trouble keeping a good history. 这与git的工作方式相反,这就是为什么您在保持良好历史方面遇到困难。

It's important to understand that what you're doing is not really a merge[1]. 重要的是要了解您正在做的实际上不是合并[1]。 A merge commit has two (or more) parent commits, and in that way the full history is preserved. 合并提交有两个(或多个)父提交,这样就保留了完整的历史记录。 To be fair, git has a tendency to be "flexible" to the point of inconsistency in how it uses certain terms; 公平地讲,git在使用某些术语时倾向于“灵活”到不一致的程度。 there are operations it calls "merging' that don't result in merge commits. But even with those operations, you merge the entire content - not an individual directory. 有一些它称为“合并”的操作不会导致合并提交,但是即使使用这些操作,您也可以合并整个内容-而不是单个目录。

If you have distinct modules - or, however you might describe them, different content in different directories - that change independently (which certainly applies if you promote them between branches/environments separately), they should be in separate repos. 如果您有不同的模块(或者可能会描述它们,则它们在不同目录中的不同内容)是独立更改的(如果您分别在分支/环境之间进行升级,则肯定适用),它们应该位于单独的存储库中。 I suppose if it helps you could gather them up as submodules of a 'parent' repo, to have the ability to clone from a single url or whatever. 我想如果它能帮助您将它们收集为“父”存储库的子模块,从而能够从单个url或其他任何URL进行克隆。 But beyond that, if this type of separation isn't acceptable for some reason, you may need to consider whether git is the best tool to meet your particular source control requirements. 但是除此之外,如果由于某种原因不能接受这种分隔,您可能需要考虑git是否是满足特定源代码管理要求的最佳工具。


[1] I could also argue semantics about merging due to the fact that if both dev and qa had changes, the changes from qa would be overwritten and lost - which is not typically what is desired in a merge. [1]我还可以争论关于合并的语义,因为如果dev和qa都发生了更改,则来自qa的更改将被覆盖并丢失-这通常不是合并所需要的。 But you would then probably argue that changes always flow from dev to qa, so it's not applicable; 但是您可能会争辩说,更改总是从开发人员流向质量保证人员,因此不适用。 and anyway, git does sometimes describe the clobbering of one branch from another as a merge (ie the "ours merge strategy'). 无论如何,git有时确实将一个分支从另一个分支的破坏描述为合并(即“我们的合并策略”)。

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

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