如果标题不清楚,则为方案。
今天当我开始工作时,我忘了git pull
。
今天我写了大约200-300行,当我去git push
时,它说我的本地回购已经过时了,所以我需要git pull
。
但是git pull
会覆盖我已编写的代码吗,因为它们在同一个文件中?
答案 0 :(得分:4)
它不会丢弃您的更改。相反,它会显示您更改并存在冲突的文件列表。如果发生冲突,您可以合并更改并提交代码。
答案 1 :(得分:4)
不 - 但无论如何都不要使用git pull
。从来没有,或者至少还没有:直到你熟悉git pull
为你运行的两个命令。
这两个命令是git fetch
,它总是安全的,另一个命令。但为什么我要说"另一个命令"而不是说哪个命令?这是因为第二个命令git pull
为您运行取决于某些事情。
应该依赖的内容是您使用git fetch
获得的内容,这是第一步。只是,它没有。这取决于您在之前告诉git pull
运行的内容,您知道git fetch
的内容。
所以:你应该首先运行git fetch
,然后运行第二个命令。第二个命令可能是 git rebase
,但让我们慢一点。
你今天做了很多工作,你是通过以下方式完成的:
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 merge
和git rebase
。 git 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
尚未移动。
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 merge
或git 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
成功。