我还写了其他有关跟踪的问题,但我认为我对我不了解的内容的解释不充分。这是一个简单的示例,以显示我的问题:
git clone
在计算机上克隆了此项目。my_server_branch
。因此,在这一步中,我的计算机对my_server_branch
一无所知。
我已阅读,我应该在计算机上使用以下命令创建一个“跟踪分支”:
git branch --track my_server_branch origin/my_server_branch
我还没有运行此命令。我只需键入(在我的计算机的master分支上):
git pull
这是我在控制台中看到的内容:
* [new branch] my_server_branch -> origin/my_server_branch
因此,当我启动拉动时,git检测到有一个新的分支。因此,我的问题又是:如果gits检测到并执行了这些操作,跟踪分支有什么好处?
肯定有一个优势,因为如果不存在,该命令将不存在...但我看不到这一优势。
如果我输入git branch --track
命令,这就是我得到的:
error: the requested upstream branch 'origin/my_server_branch' does not exist
答案 0 :(得分:2)
这里有很多概念,您需要分开思考。然后还有一个术语问题。
这些是概念:
master
; origin/master
; 前两个名称(分支名称和远程跟踪名称)密切相关,并且与诸如v2.1
之类的标记名称一起被归为一个概念,Git称之为 reference 。
术语上的问题是,有时某些分支(例如master
之类的名称)被称为 tracking 。诸如origin/master
之类的其他名称称为远程跟踪分支,看起来好像是同一回事。不是!这就是为什么我将后者称为远程跟踪名称以避免使用 branch 这个词的原因,并且为什么我建议您代替动词 tracking 将诸如master
之类的分支名称考虑为具有上游或不具有上游。这回避了棘手的单词 track (当应用于工作树中的文件时,它还有另一个含义)。
让我们继续执行您的操作:
这里是一个简单的例子,以显示我的问题:
我已经创建了一个gitlab项目
我已使用
git clone
命令在计算机上克隆了该项目
此时,您有两个单独的存储库。 GitLab服务器计算机上有一台计算机,而您自己的计算机上有一台计算机。我们将GitLab称为“他们的”,而将计算机上的一个称为“您的”,尽管从某种意义上说,它们都是您的。您的存储库与它们的存储库非常相似,但不完全相同:它是一个克隆,并且可以将其标识为副本而不是原始副本。
稍后,我们将回到您计算机上的存储库。接下来的几个步骤都发生在其存储库中。
- 然后,我在gitlab Web界面中创建了一个新分支(使用'+'按钮):
my_server_branch
好,因此,此时,其 Git存储库中有一个您不知道的分支。
- 我已经将单个文件放入此分支(也使用gitlab Web界面)
从技术上讲,您不能将文件放入这样的存储库中。您要做的是添加一个新的 commit ,其中新的提交包含文件。
这很重要,因为分支名称的工作方式是记住分支中 last 提交的哈希ID。添加新提交时,存储在分支名称中的哈希ID会更改以记住新提交。新提交会记住先前的最后提交。
如果我们用单个大写字母代表实际的提交哈希ID来绘制它们,我们将得到一个类似这样的图片,它是一个简单的三提交存储库,它只有一个master
分支:
A <-B <-C <-- master
在这里,名称 master
会记住提交C
的实际哈希ID。该提交本身会记住提交B
的实际哈希ID,而它会记住提交A
的哈希ID。因此,Git仅需要具有名称master
记住提交C
的ID:其余的则是通过查看提交本身来找到的。
(我们说master
指向 C
,C
指向B
,B
指向{{ 1}}。由于A
是存储库中有史以来的第一个提交,因此它毫无意义:这就是告诉我们和Git我们可以停下来休息的地方。没有以前的历史可以检查。 em>是历史记录,历史记录依次为 C,B,A 。)
要向存储库添加新分支,我们通常会在存储库中选择一些现有提交并检出它,使其成为当前提交,然后添加指向同一提交的新名称:
A
Git在进行新提交时需要知道哪个名称进行更新,因此Git会将特殊名称A--B--C <-- master, my_server_branch (HEAD)
(大写为此类)附加到分支名称。如果使用的是本地计算机(而不是Web界面),则将创建一个文件,使用HEAD
进行添加,然后运行git add
进行新的提交。如果我们使用的是Web界面,则GitLab在其存储库中几乎会做同样的事情,它只是隐藏在其Web界面后面。他们最终以:
git commit
尽管他们可能会以这样的方式进行操作:无论如何他们仍然将A--B--C <-- master
\
D <-- my_server_branch (HEAD)
附加到HEAD
上。例如,这就是GitHub无需移动master
的方式。无论如何,由于它不是他们的{em> {em> HEAD
,所以现在并不是那么重要。
现在是时候回顾您自己的存储库了。当您运行时:
HEAD
您让计算机创建了一个新的空Git存储库,其中包含 no 提交, no 分支,除了空的存储库外壳,基本上什么也没有。然后,通过从他们的 Git中抓取 all 提交,将Git放入计算机中。因此,如果他们有三个简单的提交:
git clone <url>
您的Git获得了这三个提交:
A--B--C <-- master
(内部向后指向的箭头太烦人了,无法绘制,但仍然存在:A--B--C
指向C
,B
指向B
)
所有哈希ID都匹配:宇宙中的每个Git都会同意提交A
中的内容,这将使其哈希ID C
成为哈希ID。因此,您的Git及其Git只需查看这些哈希ID即可知道哪个Git具有哪些提交。但是您的 Git仍然没有任何分支。
您的Git询问他们的Git他们所有的分支和标记名称是什么,他们说:我的C
拥有提交master
的哈希ID。现在,您的Git创建而不是C
,而是创建master
,指向提交origin/master
:
C
没有提交,也没有分支,因此您的Git复制完成。您的Git现在执行A--B--C <-- origin/master
的最后一步,即运行:
git clone
您可以让Git使用其他名称,如果不使用,您的Git会询问他们的 Git使用哪个名称;但这是通常的常见情况:您的Git尝试检出您的git checkout master
。
您没有master
。 但是,此结帐还是成功的。它成功的原因是他们的 Git有一个master
,而您的 Git将其复制到了您的 master
。因此,您的Git不仅没有通过结账,还对自己说:嗯,没有origin/master
,但是有master
...看起来很像origin/master
,我打赌您的意思是我应该使用master
make master
。因此,您的Git会这样做:
origin/master
创建您自己的git checkout --track master origin/master
,并将其上游设置为master
。所以现在你有了这个:
origin/master
您的A--B--C <-- master (HEAD), origin/master
分支现在存在,并且以master
作为上游分支。
令人困惑的方式是,您的origin/master
分支(1)现在正在从远程(6)跟踪(2)您的远程跟踪分支(3,4,5)master
{ {1}}。这里,在第(1)和(5)点,两个单词或短语都使用单词 branch ,但两者的含义有所不同。在(2)和(4),我们有 tracking 字样,两者的含义有所不同。在(3)和(6),我们有一个单词 remote ,它们的含义有所不同。您可以看到为什么我不喜欢这些单词,并且更喜欢将其称为分支名称 origin/master
,将其与上游 origin
一起使用, master
是与远程 origin/master
相关联的远程跟踪名称。我仍然必须两次使用 remote 这个词,但是“ remote-tracking”至少是连字符的。
origin/master
的一种正确和好方法在其 Git中创建了origin
,并在其中添加了提交my_server_branch
,现在可以执行以下命令:
my_server_branch
在计算机上使用自己的Git。 (如果要明确,可以使用D
。)这使您的Git调用其Git并再次询问它的分支名称列表。这次他们说:我有git fetch
,正在提交git fetch origin
。我有master
,正在提交C
。您的Git说:啊,我已经有了my_server_branch
了,所以没问题。不过,还是给我D
。他们这样做了,现在您的Git和他们的Git之间的对话完成了。现在,您的Git将您的C
更新为指向D
,这根本没有改变,然后创建您的origin/master
,指向新的提交C
。所以现在您有了:
origin/my_server_branch
您现在可以运行D
。和以前一样,您目前没有A--B--C <-- master (HEAD), origin/master
\
D <-- origin/my_server_branch
,但是您的Git不会失败,而是会说:我还没有{{1} }。但是我确实有git checkout my_server_branch
。我将创建 my_server_branch
,指向提交my_server_branch
。我将origin/my_server_branch
的上游设置为my_server_branch
。然后,我将进行您要求的结帐。结果是:
D
my_server_branch
您唯一需要origin/my_server_branch
的时候就是A--B--C <-- master, origin/master
\
D <-- my_server_branch (HEAD), origin/my_server_branch
不能为您做正确的事情。在两种情况下会发生这种情况:
假设您有多个远程,例如,如果您有git checkout --track
加上第二个远程git checkout --track
,可以从Fred的存储库中获取内容。进一步假设您在分支机构git checkout
上复制了自己的origin
,并且Fred将 Fred的 fred
复制到了{{1} }。如果您尝试origin/hello
,您的Git将会找到两个候选对象-hello
和hello
-并且不知道使用哪个候选对象。因此,您现在可以改为运行:
fred/hello
如果您真的想使用Fred的话,或者:
git checkout hello
如果您真的想使用原产地。
或者,如果由于某些奇怪的原因,您拥有fred/hello
,但是在您的存储库中,您希望将其称为origin/hello
。使用git checkout --track fred/hello
将使您git checkout --track origin/hello
;当然,使用origin/my_server_branch
会尝试找到bob_server_branch
。所以在这里,您需要长格式:
git checkout my_server_branch
my_server_branch
git checkout bob_server_branch
命令的缩写为:
origin/bob_server_branch
;然后,只要成功git checkout --track bob_server_branch origin/my_server_branch
。由于git pull
会(当使用正确的选项运行时)会创建和/或更新来自git pull
的{{1}}远程跟踪名称分支,是git fetch
的前半部分为您创造了git merge
。
第二个命令git fetch
,或者,如果您告诉它使用origin/*
,则origin
–接受第一个命令带来的提交命令并使用它们进行合并或变基。
由于许多原因,我不喜欢git pull
命令,其中一些纯粹是历史原因(origin/my_server_branch
有时会以一些罕见但并非鲜为人知的方式销毁您的本地作品案例,而我至少经历过一次)。不过,最实际的反对意见非常简单:在您看到git merge
所获取的内容之前,如何知道您是否要运行git rebase
,git rebase
或其他所有内容?< / em>因此,我宁愿避免使用git pull
:首先运行git pull
,然后 可以运行git fetch
或git merge
,或者执行某些操作其他完全。该做什么取决于我从git rebase
看到的内容(当然,还有我正在使用此特定存储库的内容)。
偶尔会有一些例外,尤其是对于我使用只读存储库的情况而言-我只想要它们的最新提交及其历史记录,因此git pull
可能 很好,只要因为它们表现良好-或在我控制两端的地方,例如git fetch
存储库确实位于GitHub上,我知道我在其中放置的内容。但是,即使是后一种情况,我也倾向于避免使用git merge
,因为有时我会忘记放入哪个存储库中的内容。我先使用git rebase
进行检查。
答案 1 :(得分:1)
我不确定是否100%理解您的要求,但是跟踪远程分支的好处是您可以推入和拉出它。
在本地签出分支:
git checkout my_server_branch
进行更改并提交。
现在git push
会将您的更改推送到远程origin/my_server_branch
分支。
答案 2 :(得分:1)
git pull
运行git fetch
和git merge
。很高兴知道
使用此命令的第一部分(git fetch
),您从远程获取了所有分支。如您所读here,此命令将提取所有refs
。
默认情况下,当您从远程签出新分支时,git会将其创建为新的上游分支。基本上,您要查询的命令是在获取所有分支时隐式为您运行的。