请拉我的回购摆脱我所做的工作吗?

时间:2017-02-18 13:20:25

标签: git github

如果标题不清楚,则为方案。

今天当我开始工作时,我忘了git pull

今天我写了大约200-300行,当我去git push时,它说我的本地回购已经过时了,所以我需要git pull

但是git pull会覆盖我已编写的代码吗,因为它们在同一个文件中?

2 个答案:

答案 0 :(得分:4)

它不会丢弃您的更改。相反,它会显示您更改并存在冲突的文件列表。如果发生冲突,您可以合并更改并提交代码。

答案 1 :(得分:4)

不 - 但无论如何都不要使用git pull。从来没有,或者至少还没有:直到你熟悉git pull为你运行的两个命令。

这两个命令是git fetch,它总是安全的,另一个命令。但为什么我要说"另一个命令"而不是说哪个命令?这是因为第二个命令git pull为您运行取决于某些事情。

应该依赖的内容是您使用git fetch获得的内容,这是第一步。只是,它没有。这取决于您在之前告诉git pull运行的内容,您知道git fetch的内容。

所以:你应该首先运行git fetch,然后运行第二个命令。第二个命令可能是 git rebase,但让我们慢一点。

Git就是提交

你今天做了很多工作,你是通过以下方式完成的:

  • 编辑工作树中的文件(其中文件的格式正常,以便您可以使用它们,因此名称为工作树);
  • 使用git add:这会将文件复制到Git的索引(也称为"暂存区"),替换索引中的内容之前的那个文件;和
  • 运行git commit:这会将索引中的任何内容转换为提交。

这些提交位于您的存储库中。您的存储库是您的:它对您来说是私有的。无论你做什么都是你的,用老Outer Limits TV show, you control the horizontal, you control the vertical的话说。每个提交都由一个大丑陋的哈希ID标识:8a3fc17...或其他任何内容。

但是,在那里有另一个存储库,即您在某个URL上调用origin的存储库。 (插入不祥的音乐:-))

进行新提交

您自己的存储库中的每个提交都存储了一些历史记录(历史记录提交,换句话说)。每个提交都说"在我之前的提交是<一些很丑的Git哈希ID>"。这使得提交链:

A <- B <- C    <-- master

master这样的分支名称只是标识您的最新提交,在本例中为C。该提交C&#34;指回&#34;到早期的提交B,它指向A。 (在此图中,整个存储库只有三次提交。A是第一个,因此它不能&#34;指向早期的提交,所以它没有。)

当您添加新的提交D时,Git会将新的提交点恢复为之前的提示,并使新的提交成为新的分支提示:

A <- B <- C <- D   <-- master

如果你做了几次提交,他们都像这样链接在一起:

A--B--C--D--E--F--G   <-- master

(内部向后箭头太烦人且占用空间而烦恼;请记住,在Git中,一切都像这样倒退。)

每次提交,一旦这样保存,都是永久性的,大多是永久的,永远不变。 (它实际上可以更改,因为它的大丑陋哈希ID是通过对其内容进行加密校验和来计算的。如果你改变任何东西,甚至一个位,那么新的内容得到一个新的,不同的校验和,所以它们是一个新的不同的提交。)但它只在你的存储库中。最终,您需要发布 - 或推送 - 您的新提交,以便其他人可以看到它们。 (或者,您可以通过其他方式分发它们,但我们只是担心在这里推送。)

请注意,它是找到这些提示提交的分支名称。 Git否则不知道从哪里开始。一旦Git有提示提交,它就会使用向后指针来查找其他提交。

git fetch为您提供新发布的提交

所以,让我们说你有:

...--E--F--G   <-- master

在您的存储库中,与origin上最新发布的提交保持同步。但随后有人在H上的存储库中发布了一些新的提交origin。您可能希望运行git fetch

这让你的Git在网络电话上调用他们的Git。他们谈了一下,发现他们的Git有新的提交H。你的Git将它加载到你的存储库中。新提交H&#34;指回&#34;到现有提交G,并且在他们的主服务器上。

为了避免扰乱 master,您的Git会保存H,但会使用名称origin/master

...--E--F--G     <-- master
            \
             H   <-- origin/master

这就是git fetch所做的事情:它会调用其他一些Git并找出新内容,下载所有新内容,并使用这些所谓的远程跟踪分支名称< / em>(origin/master)记住它刚刚得到的东西。

git push就像git fetch,在另一个方向 - 但不完全是

发布提交,您将使用git push。这就像获取一样,但是当你的Git在origin上调用另一个Git时,你就会向他们提交你的提交。然后你要求他们设置他们的分支 - 例如,不是特殊的will/master,而只是简单master - 指向你的新分支提示。

如果您是唯一一个正在工作的人,那很好。但也许另一个名叫鲍勃的人也在做工作。 (再次插入不祥的音乐。)

git pull运行第二个命令

现在,如果您尚未进行任何新的提交,则可能希望master合并新的提交H。有两个标准命令可以执行此操作,此时它们都执行相同的操作,因为您尚未进行任何新的提交。

这两个命令是git mergegit rebasegit pull命令默认运行git merge作为其第二个命令 - 但事实上,大多数人应该运行git rebase

现在,它没有任何区别。你的Git会看到:

...--E--F--G     <-- master
            \
             H   <-- origin/master

在您的存储库中,并会说:&#34;啊,我需要做的就是向下滑动名称master&#34;:

...--E--F--G
            \
             H   <-- master, origin/master

(Git称之为快进。)现在我们也可以理顺扭结,只需要一条线性链到H

当你和其他人都提交时会发生什么?

让我们现在安全地保存H

...--F--G--H   <-- master, origin/master

现在让我们假设您忘记更新自己的存储库 - 甚至不要忘记,但Bob设法提交并在您工作时推送它。您在存储库中进行了新的提交J(我们正在跳过I为Bob保留它):

             J   <-- master
            /
...--F--G--H     <-- origin/master

请注意,您的存储库中的origin/master尚未移动。

与此同时,Bob拥有他的自己的存储库,他也从origin复制(克隆)了。他也有这个F-G-H序列。 Bob进行新的提交I并运行git push origin。 Bob的Git将Bob的I提交到origin并要求他们将其添加到master,在其存储库中,origin上:

...--F--G--H--I   <-- master    (on origin)

现在运行git push。你的Git调用了起源的Git,然后说#34;采取这个闪亮的新提交J&#34; -it确实 - 然后要求原作者的Git制作J原点是master

这一次,他们来源 - 说不。

如果他们说是的话,看看他们会发生什么:

...--F--G--H--J   <-- master
            \
             I    [lost! nobody points to I anymore]

这就是为什么他们说&#34; no&#34;。

合并和变基:这里第二个命令很重要

运行git push并拒绝了您的请求后,现在您必须采取行动。

您的提交已J,但origin有一个提交,而不是Bob&#39; I。您必须git fetch,这会让您提交I

             J   <-- master
            /
...--F--G--H
            \
             I   <-- origin/master

现在只在你的存储库中(来源和Bob没有你的J但是,原来可能,因为你给了他们,但他们不是记得它了。现在你的工作就是将它们编织在一起。

您可以使用git mergegit rebase执行此操作。 git pull将运行其中一个。默认情况下,它运行git merge

merge命令进行另一个新的提交,称为&#34; merge commit&#34;。合并提交有点特殊:它指向两个提交:

             J
            / \
...--F--G--H   K   <-- master
            \ /
             I     <-- origin/master

合并提交将您的工作(在J中)与Bob(在I中)结合起来。现在你可以再次git push:你的Git会将J K发送给来源,并请原作者的Git设置他们的 master指向K。由于K指向I(以及J),他们现在应该接受推送。

唯一的问题是你添加了这种无用的合并K。现在,K记录了你和鲍勃同时工作的事实,但鲍勃击败了你到git push步骤,你必须弥补这一点。明天,这可能不重要。一年后,它几乎肯定无所适从。

合并的替代方法是 rebase 。 rebase 副本提交让他们降落在新的位置。假设我们可以将您的原始J复制到执行J'所做的事情的新J - 进行相同的更改 - 但在 Bob之后使其成为工作:

             J   [no longer needed]
            /
...--F--G--H   J'   <-- master
            \ /
             I   <-- origin/master

这正是git rebase的作用。如果您有多次提交,它将按顺序复制所有,并在origin/master上最后一次提交后放置每个副本。

一旦你的尚未发布的提交都像这样被复制,你可以再次git push。这次你的Git将J'发送给了原作者的Git。由于J'位于I之后,这一次,他们会接受它。

重新定位通常更好

现在每个人都有J'并且每个人都忘记了旧的J提交,图表看起来像这样 - 对于你(在你的存储库中)和来源(在它中):

...--F--G--H--I--J'  <-- master

并且小刻度标记甚至可以脱落,我们永远不会知道(或记住)rebase步骤发生了。 Bob将运行git fetch并更新自己的master,他也将拥有相同的图表。在视图中没有合并提交K和合并泡泡:​​看起来你刚刚在Bob完成他的工作之后就完成了你的工作。

有时候,特别是对于大功能提交 - 更好地放入真正的合并。 (当然,有了很大的功能,随着时间的推移在侧枝上开发它们会很好,然后保持侧枝,以防在侧枝上容易看到的小毛刺,但很难看看大合并。)但是你可能不知道,在你运行git fetch之前,是否有其他人自己带来了一个大的功能。如果他们这样做,那么变基可能比合并要困难得多 - 在这种情况下你可能想要合并。

在任何情况下,重新定位通常正确的命令。在大多数情况下,唯一真正的缺点是您需要确保仅重新设置未发布的提交(因为当您复制提交时,这些哈希ID在新的提交中会有所不同)副本,Git通过哈希ID识别事物。但是,这在这里是自动的。

所以,你应该运行git fetch - 这将让你&#34; Bob&#34;&#34; commit(s) - 然后运行git rebase,复制今天的Bob提交后的提交。然后,您可以git push成功。