基于此评论:
好的,所以我检查了 my_branch,我添加了更改并提交它,每当我执行 git push origin master 时,它都会显示所有内容都是最新的。当我从另一台远程计算机上提取它时,我该如何做到这一点,我所做的更改拉动?
确实,您需要的是一个好的教程,但不幸的是,有很多不好的教程,而好的教程却不多。我推荐the Pro Git book。不过,要阅读和消化的内容很多。
目前,让我们回顾一下您目前所掌握的内容,并就他们所做的事情做一些简短的(我希望)说明。让我们首先注意这里至少涉及两个存储库,一个是你的——你的克隆,是你用git clone 创建的——另一个是你在其他机器上以origin 的名字访问的。您所做的几乎所有事情都发生在您的克隆中,除了git push(打包来自您的克隆的提交,将它们传送到远程,并要求远程更改其分支名称之一以指向它们),以及一些git fetch(我们稍后会谈到)。
到目前为止你做了什么
第一:
git checkout my_branch
这告诉 Git 查找名称 my_branch,我们可以在这里假设它是您存储库中的现有分支。
假设这成功了,Git 将HEAD 设置为指向my_branch。名称my_branch 指向某个特定的提交,所以从图表上看,我们有:
...(some commits)<-o<-o <-- HEAD->my_branch
os 代表提交。每个提交都指向它的 parent 提交,my_branch 指向最顶端(最右边)的此类提交。然后,Git 可以通过从 HEAD 开始查找所有提交,看到它指向 my_branch,查看 my_branch 指向的位置(提示提交),然后将每个提交跟踪到其父级。
接下来,您添加/修改一些文件,可能删除一两个文件,并最终提交:
...
git add ...
...
git commit
这使得 new 提交。通过像往常一样读取HEAD,发现它指向my_branch,读取my_branch 以找到它的提交(分支的当前提示),进行新提交的过程,以及然后从索引/暂存区域中的任何内容(您在上面git add-ed 的内容)中进行新的提交。作为最后一步,git 调整 my_branch 以指向 new 提交。
让我们再次绘制该图,其中包含新的提交。让我们将新提交绘制为* 而不仅仅是o,这样我们就可以很容易地看到它是哪一个:
...(some commits)<-o<-o<-* <-- HEAD->my_branch
到目前为止一切顺利,但所有这些都严格保存在您自己的存储库中,所以现在我们来回答您关于 git push 的问题。在这里,事情变得有点复杂。
你运行了这个:
git push origin master
但我们刚刚说过并在上面画了HEAD 指向my_branch 和my_branch 指向这个新的* 提交。 master 在哪里?
好吧,我们之前没有绘制它,而且我没有你的存储库,所以我不得不猜测。但让我猜猜master 指向o,曾经是my_branch 的尖端。为了画这个,我不能再画箭头了(它们不在我在 StackOverflow 上的字符集中),所以我将使用\:
...(some commits)<-o<-o <-- master
\
* <-- HEAD->my_branch
这与之前的绘图相同,除了 (1) 我必须使用两条线和 (2) 我添加了 master 分支。
当您运行 git push origin master 时,您要求您的 Git 调用 origin 的 Git 并向其发送一些提交,即您在 master 上拥有而在 master 上没有的任何提交.但是你和他们在master 上的提交相同,所以你的 git 说“一切都是最新的”并停止。
您在这里拥有的是对 my_branch 的新提交。
(另外,在你的新提交之前的提交是在 both my_branch 和 master,至少在我刚刚制作的绘图中。我没有有你的存储库,所以我只是猜测。但几乎可以肯定 some 提交在两个分支上。与其他版本控制系统相比,这对于 Git 来说是不寻常的。稍后很重要,并且需要记住的一点:提交可以在许多个分支上。现在,这只是一个奇怪的问题。)
你还能做什么:第 1 部分
那么,你可以做些什么来让你的新提交到origin?您可以做的一件事是以my_branch 的名义推送它。如果origin 没有名为my_branch 的分支,则会在那里创建它。如果origin确实有一个my_branch,这将尝试设置他们的my_branch指向你的新提交*——当然,首先交付该提交。无论哪种方式:
git push origin my_branch:my_branch
或:
git push origin -u my_branch:my_branch
将尝试该请求。他们的 Git 可以接受或拒绝请求,并且通常会根据他们是否有一个名为 my_branch 的分支来这样做(如果有,它现在指向的位置)。
注意my_branch:my_branch 中的重复。冒号 : 字符左侧的名称是 your 存储库中的分支名称,右侧的名称是您要询问的名称——远程上名为 origin 的 Git -设置。这两个名字不必相同!如果它们相同,您通常可以只写一次,这就是我们在git push origin master 中看到的。您也可以使用my_branch 来执行此操作。我喜欢包含冒号和目标名称,至少对于新用户来说是这样,因为我认为这会使正在发生的事情更加明显。
您还可以做什么:第 2 部分
您还可以做任何其他事情:
- 使您的新提交出现在您的
master 上(根据您的存储库现在的外观,采用几种方式之一);或
-
复制您的新提交到您添加到
master 的另一个甚至更新的提交;或
- 请他们 (
origin) 接受您在 my_branch 上的新提交并将其添加到他们的 master。
最后一个相当于做第一个,然后做git push origin master。
如何将my_branch 上的新提交提交到(您的)master
有两种方法可以做到这一点,它们可以做非常不同的事情。
git merge:这有两个主要的子模式。它可以进行真正的合并,也可以进行 Git 所说的快进,这实际上根本不是合并。
git reset:这有很多子模式,而且是一个非常锋利的工具,如果使用不当会引起很多悲伤。我们将在这里讨论的模式(但不是实际使用)是要注意它所做的在某种程度上就像git push 要求遥控器做的最后一步。它告诉 Git 从它现在指向的任何提交上剥离一个分支标签,而是让它指向某个 other 提交。
我们先看git reset,具体来说就是“剥离换标签”的操作。让我们再次绘制该图:
...(some commits)<-o<-o <-- master
\
* <-- HEAD->my_branch
首先,我们需要让HEAD 指向master,我们照常使用git checkout:
git checkout master
导致:
...(some commits)<-o<-o <-- HEAD->master
\
* <-- my_branch
现在,假设我们要运行git reset --hard my_branch(我们实际上不这样做,只是看看如果我们运行它会做什么)。这告诉 git “剥离当前分支标签”(读取 READ => 它是 master)“脱离它指向现在的 o 提交,并使其指向与 my_branch 相同的提交”。也就是说,如果我们运行这个命令,我们会得到:
...(some commits)<-o<-o
\
* <-- my_branch, HEAD->master
请注意,标签已移动,但没有其他任何更改——存储库中的提交仍然相同。
git merge
现在让我们回到git merge。不幸的是,git merge 在进行真正的合并时是一个相当复杂的操作。幸运的是,git merge 做的第一件事就是检查它是否是一个非常简单的操作。
假设我们一直绘制的图形是准确的,这个合并是一个简单的操作。特别是,my_branch 指向我们可以通过“滑动标签快进”达到的提交,逆着所有箭头的方向,因此它指向提交*:
...(some commits)<-o<-o
\
* <-- my_branch, HEAD->master
事实证明,这与git reset 所做的相同,但因为git merge 首先检查 看看这是否是一个安全 的事情做,这是我们可能想在这里使用的命令。
让我们也看看它不安全的情况,只是为了完整性。例如,假设我们实际上有这个:
...(some commits)<-o<-o<-o <-- HEAD->master
\
* <-- my_branch
在这种情况下,git merge 将进行真正的合并,而git reset --hard 将在master 上丢弃最终的o 提交。我们不能向前滑动标签,我们必须先将其备份,所以从“master 现在所在的位置”到my_branch 的动作不是快进,@987654423 @ 将进行真正的合并。
git cherry-pick
最后,为了完整起见,让我们看看如何复制提交。
一般来说,要复制一个提交,我们使用git cherry-pick。我们先上到我们要复制的分支到,比如master:
...(some commits)<-o<-o<-o <-- HEAD->master
\
* <-- my_branch
然后运行git cherry-pick my_branch。这会找到my_branch 指向的提交(我们的* 提交)并对其进行复制,将副本添加到当前分支(读取HEAD,参见master,当前分支是master,复制被添加到master)。结果如下所示:
...(some commits)<-o<-o<-o<-x <-- HEAD->master
\
* <-- my_branch
其中x 是* 的副本。
在所有这些情况下,我们现在可以 git push origin master 将原始提交 * 或其新副本 x 推送到 origin,然后要求 origin 将其 master 重置为我们的新提交。
拉取 vs 获取
你提到了“拉”。我建议将其作为两个单独的步骤重新学习,因为git pull 实际上只是这两个步骤:
git fetch
-
git merge(或者,通常使用更好的命令,git rebase)
实际上是git fetch 步骤联系遥控器,在您的情况下为origin,并从中获取新内容。
fetch 操作带来了他们拥有的提交,而您没有(并且不会费心带来他们已经拥有的提交)。然后——这不是git push 的镜像——你的git fetch 复制他们的分支信息,但重命名它。他们的master 变成你的origin/master;如果他们有my_branch,它将成为您的origin/my_branch。
此重命名步骤对于允许您合并(或变基)至关重要。如果 fetch 只是覆盖了你的分支,你将失去你所做的新提交的工作!
获取完成后,您可以将您的工作合并或重新定位到您带来的新提交中。
我会将所有关于变基的描述留给其他帖子(或 Pro Git 书),但会添加最后一点:除了让正在发生的事情更清楚之外,将git fetch 与第二步分开的原因是这让您有机会决定使用哪个步骤。
如果您知道您将使用git rebase,您可以通过运行git pull --rebase 将这两个步骤结合起来。
如果您知道您将运行git merge,您可以通过运行git pull(不带标志)将这两个步骤结合起来。
但是如果你想先看,然后然后决定呢?然后您需要使用两个单独的步骤,并在这两个步骤之间插入“看看,然后决定”部分。如果您以git pull 开头,则不能这样做。
(一旦你使用了一段时间的 Git,你可能会知道在 git fetch 之后将使用哪个命令。实际上,它可能通常是 git rebase,并且你可以将 Git 配置为自动使用 --rebase。但请等到你使用 Git 一段时间。)