Git推送的效果
git push是否完全替换远程回购中的目标分支?我见过的所有撤销git推送的解决方案都涉及在本地更改分支,然后重新执行git推送。你不能像撤消合并那样撤消git推送吗?要撤消它,你有两个选项:提交“撤消”代码或替换撤消代码 要替换它,您需要重新设置基址,并在启用force标志的情况下按下 提交选项会向其他所有人显示您已经改变了主意,这更安全,因为如果人们已经使用了拉叉操作,那么这样他们至少可以参考表单的来源,子问题“git push是否完全替换远程回购中的目标分支?”是有问题的。用“是”或“否”来回答充其量只是误导。(与老的律师笑话问题“你停止打你的妻子了吗?”相比:如果你说“是”,这意味着你曾经打过她,如果你说“不”,这意味着你现在仍然打她![作为一名计算机程序员/逻辑学家,我会说正确的答案可以是“不”,但人们并不总是这样想。] 大致上,Git推送的效果,git,git-push,Git,Git Push,git push是否完全替换远程回购中的目标分支?我见过的所有撤销git推送的解决方案都涉及在本地更改分支,然后重新执行git推送。你不能像撤消合并那样撤消git推送吗?要撤消它,你有两个选项:提交“撤消”代码或替换撤消代码 要替换它,您需要重新设置基址,并在启用force标志的情况下按下 提交选项会向其他所有人显示您已经改变了主意,这更安全,因为如果人们已经使用了拉叉操作,那么这样他们至少可以参考表单的来源,子问题“git push是否完全替换远程回购中的目标分支?”是有问题的。用“是”或“否
gitpush
所做的事情与gitfetch
所做的事情是相同的,但方向相反。在智能协议(如ssh)上,有一个完整的协商序列,在这个序列中,“您的端”和“他们的端”交换关于他们有哪些对象以及他们没有但想要哪些对象的信息。在apush
的情况下,你告诉另一端:“我有一个分支标签,B,它指的是最终提交C9,我能把它推给你吗?”他可能会说“好吧,等一下C9的父级是什么”,你说“C8”,他说“我也没有,C8的父级是什么”,你说“C6和C7的合并”,他说“哦,我都知道了,给我C8和C9。”所以你的团队会制作一个“包”(特别是一个“瘦包”-细节不是那么重要,但这就是你看到的消息,“计算对象”和“压缩对象”等等)保存这些提交以及它们引用的树和文件
一旦send将对象发送到其端,其端将把推送的分支标签移动到新的分支提示。如果新的分支提示标记的分支与远程上的任何分支都没有共同的历史记录[请参见下面的注意),那么实际上答案将是“是的,它完全替换了目标分支”“。也就是说,它发送一组全新的提交对象,分支标签B位于新的(不同的)提交树上
但是,更常见的情况是,您的终端有一个分支提示标签,就像上面描述的提交C9一样,因此您只需提交两个新的提交(C8和C9),在它们的终端,它们将分支标签从C7移动到C9。在这种情况下,答案是“不,它不会替换目标分支,它只是增加它并向前移动标签。”
不过,还有一个更复杂但实际上相当典型的场景。也许“他们的终点”在提交C7之后有一个您没有的提交C10。同时,您还有要发送的C8和C9提交。这是一种“非快进”操作。通常设置--bare
回购以拒绝此类推送:他们会看到您要求添加C8和C9,然后将分支标签从C10移动到C9。这将留下一个“孤立”提交(C10),即没有标签的提交。如果您--force
推送(并且被允许推送),git将继续进行推送。[这里是前面提到的注意事项:推送一个不相关的分支也需要--force
,原因几乎相同:git检测到一些提交将变得不被引用,并让您说“我真的想这么做”。]
与Git中的所有内容一样,以前的提交“仍然存在”,只是不再被引用。当然,推送几乎总是转到一个--裸回购。由于没有.git/logs
目录中充满了reflogs,所以使用--bare
repo会变得有点棘手,因此现在很难找到像C10这样丢失的提交,或者整个丢失的提交链。(您或本讨论中的“他们”需要git fsck--unreachable
找到无法访问的提交对象来恢复它们。)
现在,如果你做了一个推送,你不应该让我们假设你打算推送提交C8,而不是C9,那么有可能说服远程更改他的标签版本。假设您已经更新了master
,并且remote.origin.url
类似于ssh://barehost/dir/with/repo.git
。假设您可以ssh barehost
使用--bare
repo登录主机:
edithost$ ssh barehost
barehost$ cd /dir/with/repo.git
barehost$ git log --oneline --decorate -2 master
5e01371 (HEAD, master) commit-C9's one line description
309b36c commit-C8's one line desc.
barehost$
现在,您可以重新标记master
以指向提交C8:
barehost$ git branch -f master master^
poof,--bare
repo.git
copy现在有一个以commit C8结尾的分支
如果您只是在edithost
上创建自己的分支并强制推送它,则会得到相同的效果,不过:
edithost$ git branch rewinder master^
edithost$ git push -f origin rewinder:master
第二个git推送
将连接到barehost
,交换过程如下:
edithost (to barehost): branch label for you, master should go to 309b36c
barehost (to edithost): I got 309b36c but are you sure? force push?
edithost: yep, he's sure...
barehost: OK, I set master to 309b36c!
A0 -- A1 -- A2 <-- origin/master, old_master
C0 -- ... -- C5 -- C6 <-- master
\
C7 <-- feep
(事实上,原力标志在最初的交换中是正确的,但我认为这样想象更有趣/更难忘)。这真的很快;不需要“薄包”的对象
执行以下任一操作时出现的缺陷-ssh
绑定到barehost
,并使用git分支手动重置,或者强制推送一个复卷机:master
标签对是一样的:如果有人叫他Pat,在你不小心推送它和你“撤销”它之间,Pat进来并从barehost
抓取了commit C9,那么Pat可能会感到困惑。他现在有C9,据他所知,应该在C8之后。根据接下来发生的情况,他可能会尝试使用它,也可能会将它推回到ba
$ git clone ssh://[redacted]/repo.git
$ cd repo
$ git branch -m master old_master
$ git config --remove-section branch.old_master
$ git checkout --orphan master
Switched to a new branch 'master'
$ git rm somefile # or "git rm -rf ."
rm 'somefile'
$ echo '*.o' > .gitignore
$ git add .gitignore && git commit -m C0
[master (root-commit) f09f66f] C0
1 file changed, 1 insertion(+)
create mode 100644 .gitignore
A0 -- A1 -- A2 <-- origin/master, old_master
C0 <-- master
$ git config branch.master.remote origin
$ git config branch.master.merge refs/heads/master
A0 -- A1 -- A2 <-- origin/master, old_master
C0 -- ... -- C5 -- C6 <-- master
\
C7 <-- feep
$ git push origin feep:feep
Counting objects: 25, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (21/21), done.
Writing objects: 100% (25/25), 2.27 KiB | 0 bytes/s, done.
Total 25 (delta 6), reused 0 (delta 0)
To ssh://[redacted]/repo.git
* [new branch] feep -> feep
$ git push -f origin master:master
Total 0 (delta 0), reused 0 (delta 0)
To ssh://[redacted]/repo.git
+ dce5ffc...ae235f9 master -> master (forced update)
A0 -- A1 -- A2 <-- old_master
C0 -- ... -- C5 -- C6 <-- origin/master, master
\
C7 <-- origin/feep, feep
A0 -- A1 -- A2 [no label]
C0 -- ... -- C5 -- C6 <-- master
\
C7 <-- feep
$ git rev-parse old_master
dce5ffcfcee7c5c66275189c75ea68219e8f26f5
$ cd ..
$ git clone ssh://[redacted]/repo.git altrepo
Cloning into 'altrepo'...
remote: Counting objects: 25, done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 25 (delta 6), reused 0 (delta 0)
Receiving objects: 100% (25/25), done.
Resolving deltas: 100% (6/6), done.
Checking connectivity... done
$ cd altrepo
$ git branch
* master
$ git log --oneline -1
ae235f9 C6
$ echo 'Some important documentation blah blah blah' > doc.txt
$ git add doc.txt && git commit -m C10
[master a22a608] C10
1 file changed, 1 insertion(+)
create mode 100644 doc.txt
$ git push origin master:master
Counting objects: 4, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 368 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ssh://[redacted]/repo.git
ae235f9..a22a608 master -> master
$
A0 -- A1 -- A2 [no label]
C0 -- ... -- C5 -- C6 -- C10 <-- master
\
C7 <-- feep
$ cd ../repo
$ git checkout master
$ git merge --no-ff feep -m C8
Merge made by the 'recursive' strategy.
silly.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
$ echo experiment > experimental.file
$ git add experimental.file && git commit -m C9
A0 -- A1 -- A2 <-- old_master
C0 -- ... -- C5 -- C6 _ <-- origin/master
\ \_ C8 -- C9 <-- master
\ /
C7 <-- origin/feep, feep
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 3 commits.
# (use "git push" to publish your local commits)
#
nothing to commit, working directory clean
$ git push origin master:master
To ssh://[redacted]/repo.git
! [rejected] master -> master (fetch first)
error: failed to push some refs to 'ssh://[redacted]/repo.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first merge the remote changes (e.g.,
hint: 'git pull') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
$ git fetch
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From ssh://[redacted]/repo
ae235f9..a22a608 master -> origin/master
$ git status
# On branch master
# Your branch and 'origin/master' have diverged,
# and have 3 and 1 different commit each, respectively.
# (use "git pull" to merge the remote branch into yours)
#
nothing to commit, working directory clean
$ git log --oneline --decorate --graph --all
* 525b6fa (HEAD, master) C9
* 1933a04 C8
|\
| * c4c3302 (origin/feep, feep) C7
|/
| * a22a608 (origin/master, origin/HEAD) C10
|/
* ae235f9 C6
* e026711 C5
* f16f7cc C4
* 00d0a69 C3
* 45acc03 C2
* 07a1145 C1
* f09f66f C0
* dce5ffc (old_master) A2
* ad84d73 A1
* cee030f initial commit
A0 -- A1 -- A2 <-- old_master
C10 <-- origin/master
/
C0 -- ... -- C5 -- C6 _
\ \_ C8 -- C9 <-- master
\ /
C7 <-- origin/feep, feep
$ git push -f origin master:master
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 529 bytes | 0 bytes/s, done.
Total 4 (delta 0), reused 0 (delta 0)
To ssh://[redacted]/repo.git
+ a22a608...525b6fa master -> master (forced update)
A0 -- A1 -- A2 [no label]
C10 [no label]
/
C0 -- ... -- C5 -- C6 _
\ \_ C8 -- C9 <-- master
\ /
C7 <-- feep
$ git branch rewinder master^
$ git push -f origin rewinder:master
Total 0 (delta 0), reused 0 (delta 0)
To ssh://[redacted]/repo.git
+ 525b6fa...1933a04 rewinder -> master (forced update)
barehost$ git log --oneline --graph
* 1933a04 C8
|\
| * c4c3302 C7
|/
* ae235f9 C6
* e026711 C5
* f16f7cc C4
* 00d0a69 C3
* 45acc03 C2
* 07a1145 C1
* f09f66f C0
A0 -- A1 -- A2 [no label]
C10 [no label]
/
C0 -- ... -- C5 -- C6 _
\ \_ C8 <-- master
\ / \_ C9 [no label]
C7 <-- feep
barehost$ barehost$ git branch resurrected_master dce5ffc
barehost$ git branch recovered_c10 a22a608
barehost$ git branch recovered_c9 525b6fa
barehost$ git log --oneline --decorate --graph --all
* 525b6fa (recovered_c9) C9
* 1933a04 (HEAD, master) C8
|\
| * c4c3302 (feep) C7
|/
| * a22a608 (recovered_c10) C10
|/
* ae235f9 C6
* e026711 C5
* f16f7cc C4
* 00d0a69 C3
* 45acc03 C2
* 07a1145 C1
* f09f66f C0
* dce5ffc (resurrected_master) A2
* ad84d73 A1
* cee030f initial commit
$ git fetch
From ssh://[redacted]/repo
* [new branch] recovered_c10 -> origin/recovered_c10
* [new branch] recovered_c9 -> origin/recovered_c9
* [new branch] resurrected_master -> origin/resurrected_master
$ git log --oneline --decorate --graph --all
* 525b6fa (HEAD, origin/recovered_c9, master) C9
* 1933a04 (origin/master, origin/HEAD, rewinder) C8
|\
| * c4c3302 (origin/feep, feep) C7
|/
| * a22a608 (origin/recovered_c10) C10
|/
* ae235f9 C6
* e026711 C5
* f16f7cc C4
* 00d0a69 C3
* 45acc03 C2
* 07a1145 C1
* f09f66f C0
* dce5ffc (origin/resurrected_master, old_master) A2
* ad84d73 A1
* cee030f initial commit