我从git克隆了一个Android项目,并从master
创建了一个名为devBranch
的本地分支。
我在devBranch
中做了很多更改,我也远程推送了它。
现在,一旦我完成了devBranch
中的所有更改,我就将这些更改合并到主本地分支中。
现在我想将本地主站中的这些新更改推送到远程主站。
什么是合适的方法?我应该使用推送还是合并,为什么?
将git push
与git merge
进行比较不是很明智:
-
git push
命令是关于将一些提交发送到负责其他仓库的其他 Git,以便其他仓库获得新的提交,然后要求其他 Git 调整该其他仓库中的某些分支和/或其他名称。 -
git merge
命令很复杂,但当以典型方式之一使用时,是在您自己的存储库的当前分支中进行新提交。 此新提交通常是具有两个父级的合并提交,而不是具有一个父级的更常见的普通提交。
现在,在某些情况下,git merge
不会进行新的提交,而是执行快进操作。 快进操作是对分支名称的更改,以便存储在该名称中的新提交哈希 ID 标识其哈希 ID 以前存储在该名称中的提交的后代。 (这是一个非常多的行话,需要一点图论才能理解。 有关一个简单的示例,请参见下文。
git push
命令还可用于不发送任何新提交,但仍请求其他 Git 存储库对其分支名称之一进行更改。 此更改可以是快进操作,无论您的 Git 是否发送任何新提交。 实际上,如果分支名称更改是非快进操作,则git push
命令需要--force
或--force-with-lease
选项。
由于git merge
和git push
都可以进行这种快进行动,因此至少有一种情况两者具有一定的可比性。 但是,这种特殊情况的主要区别在于,git merge
在您的存储库中执行快进,而git push
在其他Git 存储库中执行快进。
可以说,底线很简单:
- 如果需要进行合并提交,则必须在本地执行此操作。
git push
命令无法执行此操作。 - 如果您不需要进行合并提交,请问问自己谁的分支名称需要哪种更新。 若要更新自己的分支名称,请在本地工作。 要更新其他一些 Git 存储库,请使用
git push
。
请记住,存储库共享提交(通过哈希 ID),但有自己的私有分支名称。 你的名字不是别人的名字,也不需要以同样的方式使用——但人类就是人类,我们倾向于以同样的方式使用相同的名字,因为如果我叫你弗雷德,而你的同事叫你皮奥特,而你的名字实际上是拉胡尔,这太令人困惑了。
绘制快进的含义
为了理解 Git 的快进概念,让我们从分支名称包含提交哈希 ID 的事实开始,并且提交也包含提交哈希 ID。 每当某物持有提交哈希 ID 时,我们都会说该某物(无论它是什么)都指向该提交。 因此,提交指向另一个提交,分支名称指向提交。
任何给定提交的实际名称都是一些丑陋的大哈希ID,例如faefdd61ec7c7f6f3c8c9907891465ac9a2a1475
。 这些太烦人了,无法输入,而且对于人类来说太棘手了,所以让我们使用大写字母绘制我们自己的 Git 提交图片段来代替每个提交。 在这里,我们连续三次提交,最后一个(最新的)提交H
:
... <-F <-G <-H
由于H
是最新的提交,我们将有一个分支名称指向它:
... <-F <-G <-H <--master
同时,提交H
本身实际上存储了早期提交G
的原始(和真实)哈希 ID。 所以H
指向G
. 提交G
反过来,将更早提交F
的哈希 ID 存储在其中:G
指向F
。 这就是我们在这里绘制的内容。
任何提交的任何部分,一旦完成,就永远无法更改。 这意味着我们实际上不必将提交之间的内部向后箭头绘制为箭头:我们可以将它们绘制为连接线。 当我们开始添加更多提交时,这很有用H
,使用另一个名称来查找它们:
...--F--G--H <-- master
I--J <-- dev
在这里,分支名称dev
指向提交J
,向后指向提交I
,向后指向提交H
,依此类推。 同时,名称master
仍然指向现有的提交H
。
我们现在可以要求 Git "将名称向前滑动master
"。 如果我们只向前推进一次提交,master
最终会指向I
,如下所示:
...--F--G--H--I <-- master
J <-- dev
请注意提交是如何没有改变的:我们只是将提交I
高了一行,因为没有其他好方法可以在 StackOverflow 上绘制它们。 名称已更改,或者更准确地说,存储在名称中的值master
更改,因此master
现在指向I
。
如果我们一直向前滑动master
,我们会得到:
...--F--G--H
I--J <-- dev, master
现在根本没有理由继续纠结:
...--H--I--J <-- dev, master
够。
这个操作——移动主节点"前进"一个或多个提交——就是 Git 所说的快进,因为即使master
现在指向I
或J
,我们可以通过从我们现在通过该名称找到的任何提交开始,向后工作到master
之前的位置。它在提交H
,J
指向I
指向H
。 因此,提交H
保留在名为master
的分支上。
但是,如果我们从这个开始呢:
...--F--G <-- master
然后添加一个新名称dev
也指向现有的提交G
:
...--F--G <-- dev, master
然后在master
上进行一个新的提交,以便它指向这个新的提交H
,但随后签出分支dev
并在dev
上进行一个新提交,以便dev
指向新的提交I
? 我们现在将有:
H <-- master
/
...--F--G
I <-- dev
从master
(定位提交H
),我们可以回到提交G
,然后提交F
,依此类推。 从dev
(提交I
)开始,我们可以回到G
和F
等等。 但是这些连接提交的箭头完全是向后箭头。 我们不能从G
"前进"到H
. Git 的连接只指向后方。 我们需要master
名称,以便在返回之前找到提交H
。
如果我们强制 Git 将名称master
移动到指向提交I
,我们最终会得到:
H ???
/
...--F--G
I <-- dev, master
提交仍然存在,但我们无法找到它的哈希 ID。 提交H
丢失:它仍然存在(一段时间),但除非我们迫使 Git 合理快速地恢复它,否则 Git 最终会完全删除它,因为它是不必要的。1这种强制名称动议,其中提交H
丢失,是一种非快进。
如果我们想加入提交H
和I
,并保持在两者中完成的工作,我们需要使用git merge
(或类似的东西)。
1在普通存储库中,Git 会留下痕迹,让我们至少再恢复 30 天。 在服务器上的裸存储库中,恢复时间要短得多。
适当的方法是执行git push
为什么?因为您有一个远程存储库,并且希望将当前工作状态保存到该远程存储库。遥控器只是帮助您保存不同的提交和当前工作状态。 这有两个好的目的:1. 您可以使用相同的遥控器与其他团队成员协作,2.即使没有本地计算机,您也可以在任何地方访问存储库。
git merge
创建合并的两个分支的统一分支。如果您执行 git 合并,则远程上的文件将与本地计算机上的当前工作文件合并。git merge
变得更加复杂,主要处理本地计算机上的文件
Udacity 有一个免费的关于 git 和 git hub 基础知识的好课程