使用 gitbash 进行合并和提交。
让我先解释一下基本结构。所以我们有 origin/dev ,我们拉出来并开始工作。更改完成后,我们将更改推送到 origin/dev。
然后使用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
Run Code Online (Sandbox Code Playgroud)
因此,这是合并发生时任何 2 个不同环境之间通常遵循的过程。
所以我的问题是我对 DEV 中的 5 个问题进行了 5 次提交,所有提交都具有不同的提交 ID。因此,当我在 1 中提交所有五个更改时从 DEV 合并到 QA 时,我得到 1 个提交 ID,所有更改将合并为 1。在合并 UAT 时也会发生同样的情况。
有什么办法可以在不同环境之间保持相同的历史记录。真正的问题出现在 QA 中,我们可能会在 10 天内合并 4-5 次,而在 UAT 中,我们希望保持完整并且每月只合并一次。在这种情况下,如果我们将所有从 QA 到 UAT 的更改作为一次提交提交,那么 QA 中不同的历史记录将丢失。有什么办法可以解决这个问题?
在网上浏览了一些帖子但无法理解,我所理解的唯一方法是像我们在 DEV env 中所做的那样频繁提交。对于 dev>then qa> the uat 中的 1 个问题合并,这是保留相同历史记录的唯一方法,我的理解是正确的。
没有提交的历史记录。只有提交;提交是历史。
每个提交都由哈希 ID 唯一标识。该哈希 ID是提交的真实名称,就像它一样。如果你有那个提交,你就有那个哈希 ID。如果你有那个哈希 ID,你就有那个提交。读出丑陋的大哈希 ID,看看它是否在“我在这个存储库中的所有提交”的数据库中:即,看看 Git 是否知道它。如果是这样,你有那个承诺。例如,b5101f929789889c2e536d915698f58d5c5c6b7a是一个有效的哈希 ID:它是 Git 存储库中 Git 的提交。如果您的 Git 存储库中有该哈希 ID,则您拥有该提交。
人们通常根本不会输入或使用这些哈希 ID。 Git使用它们,但 Git 是一个计算机程序,而不是人类。人类在这些事情上做得不好——我必须剪切并粘贴上面的哈希 ID,否则我会弄错——所以人类使用不同的方式开始。人类使用分支名称。但是许多不同的 Git 存储库都有master,这master并不总是(或永远!)意味着我在上面输入的大而丑陋的哈希 ID。所以像这样的名称master特定于一个特定的 Git 存储库,而哈希 ID 则不是。
现在,每次提交都会存储一些东西。提交存储的内容包括与该提交相关的所有文件的快照,以便您稍后将其取回。它还包括做出该承诺的人的姓名和电子邮件地址,以便您知道该赞扬或指责谁。它包括一条日志消息:为什么进行提交的人说他们进行了该提交。但是——这是第一个棘手的部分——几乎每个提交还包括至少一个哈希 ID,这是在这个特定提交之前的提交。
所以,如果你有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>
Run Code Online (Sandbox Code Playgroud)
(该tree行表示与此提交一起保存的快照。您可以在此处忽略它。)该parent行给出了之前 提交的哈希 ID b5101f929789889c2e536d915698f58d5c5c6b7a。
如果你有b5101f929789889c2e536d915698f58d5c5c6b7a你几乎肯定也有a562a119833b7202d5c9b9069d1abb40c1f9b59a。较晚提交的历史记录是较早的提交。
如果我们用一个大写字母1替换这些丑陋的大哈希 ID,我们可以更轻松地绘制这种历史记录:
... <-F <-G <-H
Run Code Online (Sandbox Code Playgroud)
一长串提交中H的最后一次提交在哪里。由于HholdG的hash ID,我们不需要记下G大的丑陋的hash ID,直接记下Hh的hash即可。我们使用它来让 Git findG的 ID 在H其内部。如果需要F,我们可以使用HfindG来查找F的 ID,这让 Git 检索F.
但是我们仍然必须写下最后一个哈希 ID。这就是分支名称的用武之地。分支名称就像master我们保存最后一次提交的哈希 ID 的方式一样。
为了进行新的提交,我们让 GitH 在我们的新提交中保存哈希 ID 。我们的Git保存快照和我们的姓名和电子邮件地址和所有其余的为良好“休息”包括时间戳,精确的第二当我们有Git来完成这一切。现在 Git 计算所有这些数据的实际哈希 ID,包括时间戳。提交现在保存在我们所有提交的数据库中,Git 给了我们一个新的哈希 ID I:
...--F--G--H <-- master
\
I
Run Code Online (Sandbox Code Playgroud)
我们让 Git自动将I哈希 ID 写入我们的名称master:
...--F--G--H--I <-- master
Run Code Online (Sandbox Code Playgroud)
并且我们添加了新的历史记录,它保留了所有现有的历史记录。
1当然,如果我们像这样只使用一个大写字母,那么在仅创建 26 个提交之后,我们将无法在世界任何地方创建提交。这就是为什么 Git 的哈希 ID 如此之大的原因。它们拥有 160 位,因此可能的提交或其他对象的数量是 2 160或 1,461,501,637,330,902,918,203,684,832,716,283,019,655,932,542,976。事实证明,这还不够,Git 可能会转向更大的哈希,它可以容纳 79,228,162,514,264,337,593,543,950,336 倍的对象。虽然第一个数字足够大,可以枚举宇宙中的所有原子,但有一些特定的攻击很麻烦,因此 256 位哈希是一个好主意。请参阅新发现的 SHA-1 冲突如何影响 Git?
历史就是提交。要在两个分支中具有相同的历史记录,您需要两个分支名称都指向同一个提交:
...--F--G--H--I <-- master, dev
Run Code Online (Sandbox Code Playgroud)
现在历史记录master是:从 开始I,显示I,然后移回H并显示H,然后移回G... 同样,历史记录dev是:开始I,显示I,然后移回H...
当然,这并不完全是您想要的。你想要的是有分歧的历史,然后又会合。这就是分支的真正含义:
...--F--G--H <-- master
\
I <-- dev
Run Code Online (Sandbox Code Playgroud)
这里的历史dev开始(结束?)在I,然后回到H,然后G,依此类推。中的历史从master开始(结束?)H,回到G,依此类推。随着我们添加更多提交,我们添加更多历史记录,如果我们这样做:
K--L <-- master
/
...--F--G--H
\
I--J <-- dev
Run Code Online (Sandbox Code Playgroud)
那么这两个分支的历史发散。现在master从 at 开始L并向后工作,而dev从 at 开始J并向后工作。有两个提交dev那些不上master,而且两次提交是对master那些不上dev,然后一切从H上回是两个分支。
这种分歧——不在某个分支上的提交——是工作路线分歧的地方。分支名称仍然只记住每个提交,特别是每行开发的提示或最后一次提交。Git 将从这次提交开始,通过保存的哈希 ID,并使用该提交保存的父哈希 ID 向后走,一次一个提交。线条重新连接的地方,历史重新连接。这就是存储库中的全部内容,除了下一部分。
您现在可以做的是进行合并提交。进行合并提交的主要方法是使用该git merge命令。这有两个部分:
要进行合并,您首先要选择一个分支提示。你跑git checkout master或git checkout dev在这里。无论您选择哪一个,这就是您现在的提交,Git 会将特殊名称附加HEAD到该分支名称以记住您选择了哪一个:
K--L <-- master (HEAD)
/
...--F--G--H
\
I--J <-- dev
Run Code Online (Sandbox Code Playgroud)
现在你运行git merge并给它一个标识符来选择要合并的提交。如果你在master= 上L,你会想要使用dev=J作为要合并的提交:
git merge dev # or git merge --no-ff dev
Run Code Online (Sandbox Code Playgroud)
Git 现在将像往常一样遍历图表以找到最佳共享提交 -两个分支上的最佳提交,用作此合并的起点。在这里,那是 commit H,两个分支首先分歧的地方。
现在 Git 会将通过 commit 保存的快照(H合并基础)与当前 commit 中的快照进行比较L。不管有什么不同,你一定已经改变了master。Git 将这些更改放在一个列表中:
git diff --find-renames <hash-of-H> <hash-of-L> # what we changed
Run Code Online (Sandbox Code Playgroud)
Git 重复这一点,但他们的提交J:
git diff --find-renames <hash-of-H> <hash-of-J> # what they changed
Run Code Online (Sandbox Code Playgroud)
现在 Git结合了两组变化。无论我们改变了什么,我们都想保持改变。无论它们发生了什么变化,我们也想使用这些变化。如果他们改变了README.md而我们没有改变,我们将接受他们的改变。如果我们更改了文件而他们没有更改,我们将进行更改。如果我们都更改了同一个文件,Git 会尝试合并这些更改。如果 Git 成功,我们对该文件进行了合并更改。
在任何情况下,Git 现在都会获取所有合并的更改并将它们应用到H. 如果没有冲突,Git 会自动根据结果进行新的提交。如果出现了冲突,Git会仍然适用的组合变化H,但给我们留下了凌乱的结果,我们必须解决它并做最后的承诺; 但让我们假设没有冲突。
Git 现在使用一项特殊功能进行新提交。而不是只记住我们之前的承诺L,Git有此合并提交记得2个以前提交,L并J:
K--L <-- master (HEAD)
/ \
...--F--G--H M
\ /
I--J <-- dev
Run Code Online (Sandbox Code Playgroud)
然后,一如既往,Git 更新我们当前的分支以记住新提交的哈希 ID:
K--L
/ \
...--F--G--H M <-- master (HEAD)
\ /
I--J <-- dev
Run Code Online (Sandbox Code Playgroud)
请注意,如果我们通过运行进行合并git checkout dev; git merge master,Git 将执行相同的两个差异并获得相同的合并提交M(好吧,只要我们在完全相同的秒内进行合并以便时间戳匹配)。但是随后 Git 会将散列 ID 写入Mintodev而不是 into master。
无论如何,如果我们现在询问 的历史master,Git 将从M. 然后,它会走回两 L 和 J,并显示两个人。(它选择一个首次证明,并git log有大量的标志,以帮助你选择哪一个首先显示。)然后,它会从一个什么方向先挑走回来,所以它现在必须证明这两个 K和J,或这两个 L和I。然后它将从它选择显示的任何一个走回来。
在大多数情况下的Git显示所有的孩子之前的任何父母,也就是,最终,它会显示所有四个I,J,K,和L和只H显示。所以从这里开始,Git 将显示H,然后G,等等——现在只有一个链可以返回,一次提交一个。但是请注意,当您从合并中返回时,您会遇到which commit 以显示下一个问题。
git merge 并不总是进行合并提交假设你有这样的历史:
...--F--G--H <-- master
\
I--J <-- dev
Run Code Online (Sandbox Code Playgroud)
也就是说,没有分歧,dev只是严格领先 master。你做git checkout master选择 commit H:
...--F--G--H <-- master (HEAD)
\
I--J <-- dev
Run Code Online (Sandbox Code Playgroud)
然后git merge dev将您自合并基础以来所做的工作与他们自合并基础以来所做的工作结合起来。
合并基础是最好的共享提交。也就是说,我们H根据需要开始并继续返回,并且也根据需要开始dev并继续返回,直到我们到达一个共同的起点。所以从J我们来I来回回H,从H我们只是静静地坐在那里, H直到J回到这里。
合并的基础上,换句话说,是在当前提交。如果 Git 运行:
git diff --find-renames <hash-of-H> <hash-of-H>
Run Code Online (Sandbox Code Playgroud)
不会有任何变化。将没有更改(从H到Hvia master)与一些更改(从H到Jvia dev)结合起来,然后将这些更改应用于H, 的行为将是J. Git 说:嗯,这太容易了,它没有进行新的提交,而是将名称master 向前移动,与通常的向后方向相反。(事实上,Git 确实倒退了——从J到I到H——为了弄清楚这一点。它只记得它是从 开始的J。)所以默认情况下你得到的是这样的:
...--F--G--H
\
I--J <-- dev, master (HEAD)
Run Code Online (Sandbox Code Playgroud)
当 Git 能够像这样master向前滑动标签时,它将该操作称为fast-forward。当您对git merge自身进行此操作时,Git 将其称为快进合并,但这根本不是真正的合并。Git 真正做的是检查commitJ并master指向J.
在很多情况下,这是可以的!现在的历史是:对于master,开始J并返回。对于dev,开始J然后走回去。 如果这就是你所需要和关心的,那很好。但是如果你想要一个真正的合并提交——例如,你可以在以后区分master和dev分开——你可以告诉 Git:即使你可以做一个快进而不是合并,无论如何都要做一个真正的合并。 Git会继续和比较H对H,然后比较H到J,并结合变化,并作出新的承诺:
...--F--G--H------K <-- master (HEAD)
\ /
I--J <-- dev
Run Code Online (Sandbox Code Playgroud)
现在你得到了一个真正的合并提交K,需要有两个父项作为合并提交。第一个父级H和往常一样,第二个是J,就像通常的合并提交一样。的历史master,现在包括历史dev,但仍从不同的历史dev,因为历史dev不包括承诺K。
请注意,如果您现在切换回dev并进行更多提交,结果如下所示:
...--F--G--H------K <-- master
\ /
I--J--L--M--N <-- dev (HEAD)
Run Code Online (Sandbox Code Playgroud)
现在,您可以git checkout master和git merge dev试。这你就不会需要时间--no-ff,因为有一个承诺是对master那不上dev,即K,当然也有在提交dev that are not on主, namelyL-MN . The *merge base* this time is shared commitĴ (not^ h —^ h is also shared, butJ`是更好)。因此,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?
Run Code Online (Sandbox Code Playgroud)
什么没有,我们从改变J到K?(这是给你的练习,读者。)
假设 Git 能够自行组合更改,此合并操作将成功,产生:
...--F--G--H------K--------O <-- master (HEAD)
\ / /
I--J--L--M--N <-- dev
Run Code Online (Sandbox Code Playgroud)
其中新的合并提交O将J-vs-K更改与J-vs-N更改结合在一起。历史master将开始O,将包括N和M和L和K和J和I和H等。历史dev将开始N,包括M和L和J(不K!),并I和H等。Git 总是向后工作,从孩子到父母。合并让/使 Git 同时沿两条线向后工作(但一次向您显示一个,根据您提供给 的参数以某种顺序显示git log)。
| 归档时间: |
|
| 查看次数: |
1474 次 |
| 最近记录: |