使用Git时,我总是保留一个本地master
分支。在对该分支做任何事情之前,我总是会做git pull --ff-only
。
我最近被告知这是一个可怕的想法,我不应该保留一个重复的主分支。我花了几分钟时间研究这个问题,却找不到任何关于这个主题的内容。我不仅想知道哪种方式更受欢迎,还要知道原因。
据我了解,如果我没有本地master
分支,那么我无法在离线时分支。此外,如果我没有任何本地功能分支(如启动新项目或清理完成的任务),该怎么办?没有主人,我会把HEAD放在哪里?
我感谢所有关于这个主题的意见,以帮助我更多地了解最佳实践以及为什么最好遵循这一方式。
编辑:告诉我这个的人解释说我应该只是从origin/master
进行变基和分支,而不是将origin/master
拉到我的本地master
分支。我问这个问题是为了获得关于工作流程和每个工作流程的优缺点的更多反馈。
编辑2:我理解master
只是分支的名称,而分支基本上只是放在提交树上的名称。我的问题涉及工作流程。最佳做法是使用本地master
分支,它始终反映origin/master
分支?或者,我是否应始终从origin/master
分支出来并反对origin/master
,而不要触及我的本地master
分支?
答案 0 :(得分:3)
没有强烈的理由 拥有您自己的名字master
,没有强烈理由 拥有您自己的名字{{1} }。它取决于你想要什么,以及你想要处理什么。
这里要知道的重要一点是,在某个层面上,Git根本不关心分支名称。 (在其他方面,它确实如此,但我们应首先考虑这个级别,而不是它。)
在Git中,分支名称只是一个可移动的标签,就像你粘贴的那些黄色粘滞便笺(或其中的#34;签名"标签形式)之一提交。它是构成实际分支的提交自己。
假设我们从一个只有一个提交的存储库开始。调用提交master
(而不是实际的难以理解和不可发表的哈希ID):
A
现在我们添加一个新的提交A
,其父级为B
:
A
我们说提交A <-B
&#34;指向&#34; B
。换句话说,它记住我以前的提交的A
本身就是B
。
当我们添加新提交A
时,该提交将指回C
:
B
(A <-B <-C
怎么样?它指向哪里?无处:它没有父级!它不能,因为它是第一次提交。技术上{{1} }是一个 root commit 。当你在新的存储库中进行第一次提交时,你会看到Git打印这个短语A
。)
所有这些内部箭头都是一种痛苦。我们知道他们总是倒退:他们必须,因为提交只能记住他们的父母,而不是任何尚未存在的孩子......而且一旦Git提交,它就永远不会在该提交中更改任何。因此,以后无法添加子项列表。因此,Git总是希望向后工作,从最新的提交到最旧的提交。所以,让我们不用箭头画出它们:
A
如果我们想要创建分支,我们只需在某处选择一些提交并将其用作新提交的父级。让我们创建一个新的提交root commit
,其父级为A--B--C
:
D
现在我们有了一个分支!
Git的问题在于它无法快速找到这些提交。这就是分支名称的用武之地。我们创建一个分支名称,如B
,并使其指向底层分支的 tip commit :
A--B--C
\
D
我们需要另一个名称来提交master
;我们暂时将其称为A--B--C <-- master
\
D
:
D
现在,让分支机构名称如develop
特别之处的是我们可以获得&#34; on&#34;他们,使用A--B--C <-- master
\
D <-- develop
:
master
我们需要一种方法来记住我们在&#34; on&#34;上的哪个分支。 Git使用git checkout
:
$ git checkout master
Switched to branch 'master'
$ git checkout develop
Switched to branch 'develop'
当您HEAD
分支时,Git会检出分支的 tip commit ,并使A--B--C <-- master
\
D <-- develop (HEAD)
记住该特定的分支名称。
这是另一个特殊功能:当您进行 new 提交时,其父级是当前(HEAD)提交,然后Git读取git checkout
以查看哪个分支它命名,移动分支名称。让我们使用HEAD
和HEAD
更改一些文件,在E
上进行新的提交develop
:
git add
新提交git commit
指回A--B--C <-- master
\
D--E <-- develop (HEAD)
。名称E
现在指向D
。名称develop
仍然引用E
,但当前提交现在为HEAD
。
我在上面提到Git很难找到没有名字的提交提交。但是这些名称不仅可以轻松找到他们指向的提交。它们还用于保护这些提交。 Git有一个维护命令develop
或垃圾收集器,它可以进行缓慢而痛苦的爬网,以便在存储库中找到每个提交(和其他对象),并检查它们是否有名称。如果没有,E
可以将它们作为垃圾收集起来并将其删除。
因此,名称的存在告诉Git这个提交很重要:Git应该保持它。如果此提交很重要,那么其父提交也很重要。父提交的父级也很重要,所以一直回到根提交。因此,如果git gc
指向git gc
,则Git必须保留master
(然后还有C
和C
)。如果B
指向A
,则Git必须保持develop
(然后E
以及E
和D
。
但分支名称并不是我们唯一的名称。我们还有Git称之为远程跟踪分支的内容,例如B
。 (我们有标签,还有一些特殊的引用,例如A
,Git&#34;注意&#34;等等。这里的通用术语是引用,虽然你不需要记住它。)
当你从某个地方origin/master
存储库时,Git会与另一个Git交谈。其他Git有自己的分支(及其所有其他引用,包括标记和远程跟踪分支)。 你的 Git得到他们的分支列表(和标签;你的Git通常忽略他们的远程跟踪分支在这里)。你的Git然后重命名他们所有的分支。因此,如果我们使用其五次提交克隆此存储库,我们将获得一个如下所示的新副本:
stash
这些git clone
名称是远程跟踪分支(不是常规,普通,本地分支;您无法获得&#34; on&#34;他们)。这些服务和本地分支一样,用于保护提交,并让Git找到它们。你无法 on 他们。 (如果你尝试,你会得到Git称之为&#34;分离的HEAD&#34;而不是。)
然后,您的Git使用其中一个远程跟踪分支名称(通常为A--B--C <-- origin/master
\
D--E <-- origin/develop
)创建至少一个本地分支名称,通常为origin/
,然后将您带到该分支:
master
请注意,任何提交都没有发生任何变化。我们刚刚添加了一个新的标签,origin/master
,指向提交A--B--C <-- master (HEAD), origin/master
\
D--E <-- origin/develop
,就像master
一样。 (然后我们让Git将C
设置为origin/master
,以便记住这是我们&#34; on&#34;的分支。)
(与分支名称一样,标签名称也会被复制。但是,与分支名称不同,标签名称不要将HEAD
推到前面。所以&#39;标签与分支不同的几个方面之一。就像远程跟踪分支机构名称一样,您也无法获得标签,并且标签名称通常不应该移动分支名称的方式。)
master
添加到您的存储库对于原始的origin/
来说,这一切都很好,但最终你可能想要选择其他人添加到你克隆的存储库中的 new 提交。您可以通过运行git fetch
来执行此操作。 (如果您运行git clone
,请注意它只运行git fetch
,然后运行第二个Git命令。所以您仍然使用git pull
。)
git fetch
所做的是返回git fetch
处的其他Git,并从中获取其当前分支名称及其提交哈希ID。由于散列ID在所有这些共享存储库中保证是唯一的,因此您的Git可以判断这些是否是 new 提交。然后你的Git要求他们的Git提交新的提交(以及他们的父母和祖父母等等,根据需要,回到你正在谈论你已经拥有的提交的地步)。让我们看看当我们从他们的 git fetch
引入两个新提交时会发生什么:
origin
此处master
的父提交为 F--G <-- origin/master
/
A--B--C <-- master (HEAD)
\
D--E <-- origin/develop
。我们已经有了F
所以我们的Git并没有把它带进去。但是我们的Git确实带来了C
,这需要他们C
。 和,我们的Git看到他们的 G
现在名称提交F
。所以我们的 Git更新我们的内存 - 我们master
- 也指向G
。
现在我们的origin/master
落后了,我们需要采取措施让它赶上来。或者,我们可以删除它,只要我们将使用作为G
停止。例如,我们可以master
根据HEAD
制作新的本地git checkout -b develop origin/develop
,然后将develop
移到那里:
origin/develop
同样,任何提交都没有发生:这都是名称改组。 (好吧,我们的索引和工作树也会从提交HEAD
中填充。)
我们可以更新我们的 F--G <-- origin/master
/
A--B--C <-- master
\
D--E <-- develop (HEAD), origin/develop
以匹配他们:
E
现在我们可以理顺图中的扭结(在一条大直线上绘制master
)。或者我们可以删除我们的名字 F--G <-- master, origin/master
/
A--B--C
\
D--E <-- develop (HEAD), origin/develop
,而不必再拖动它,同样理顺图纸。
结果没有真正更改:我们只有或者没有指向某个提交的名称A--B--C--F--G
。如果我们拥有它,我们必须决定是否更新它。如果我们没有,我们就不必做出任何决定。这些是您拥有或不拥有master
的理由:这样您就可以记住它的位置,如果您愿意这样做,可以拖动它,或者让您不知道它如果你不想这样做,就不要拖着它。
(如果您做保留名称,您可以告诉我们自您上次向前拖动时进来的内容。如果您不要> 保留名称......好吧,Git有 reflogs ,可以保存以前的引用值,包括远程跟踪分支,所以你几乎可以做同样的事情,除了reflog条目最终到期。 )
答案 1 :(得分:1)
master只是分支的名称......它可以是任何东西。只要您了解当您在&#34; local&#34; master分支,在其他存储库中没有任何其他分支同名的移动,你会没事的。
我记得那天我正在阅读一篇关于如何为linux内核构建模块的教程,并且作者命令人们离开&#34; master&#34;因为是linus工作的分支。只要您明白通过承诺就可以保持在主人之上,linus&#39;分支不随它移动。当然,将你的分支命名为适合你正在做的事情是有道理的,但是你不会因为你选择留在当地主分支机构而让世界变得疯狂。
答案 2 :(得分:0)
我会说不,你不应该。您的Git工作流程很可能会禁止对服务器上的master
分支进行任何提交,因此拥有本地副本毫无意义:您将无法将其推送到相应的master
。但是,您需要不时地将其与远程master
同步:没有专家的额外努力。
每次需要master
时,只需使用origin/master
(假设您的遥控器是origin
)。例如。从master
开始一个新分支:
git checkout -b new_branch origin/master
根据您的分支机构在master
上分支:
git rebase origin/master existing_branch
构建最新的主语言环境(您将出现在detached HEAD state中):
git checkout origin/master && make build