简体   繁体   English

推送到远程失败,因为“当前分支的尖端位于其远程对应部分之后”

[英]Pushing to remote fails because “tip of your current branch is behind its remote counterpart”

我的 git 终端没有意义

My friend is trying to help me learn to resolve conflicts in pull requests.我的朋友正试图帮助我学习解决拉取请求中的冲突。 He created a repository.他创建了一个存储库。 I cloned it, and created a branch called "EditReadMe2".我克隆了它,并创建了一个名为“EditReadMe2”的分支。 I pushed "EditReadMe2" to the repository, and he created a conflict.我将“EditReadMe2”推送到存储库,他造成了冲突。

I did我做了

git checkout master
git pull
git checkout EditReadMe2
git rebase master

It warned me of a conflict which I resolved, but when I tried pushing EditReadMe2 it gave me the error.它警告我解决了冲突,但是当我尝试推送 EditReadMe2 时,它给了我错误。

I entered in the commands again to show my terminal in the attached image, because I don't know how its possible for the branch to be behind when I pull and rebase a second time, and it tells me everything is up to date, but then it still fails.我再次输入命令以在所附图像中显示我的终端,因为我不知道当我第二次拉动和变基时分支如何落后,它告诉我一切都是最新的,但是然后它仍然失败。

Force pushing solved the problem, but I want to know how to do this without using --force .强制推送解决了这个问题,但我想知道如何在不使用--force的情况下做到这一点。

Thanks!谢谢!

The command sequence is incomplete.命令序列不完整。
After git checkout EditReadMe2 you need to perform another git pull .git checkout EditReadMe2之后,您需要执行另一个git pull

git pull updates the working index of the current branch, not of all local branches you have. git pull更新当前分支的工作索引,而不是您拥有的所有本地分支。
When you issue the rebase command, you are rebasing an updated master into your "old" EditReadMe2 branch.当您发出 rebase 命令时,您将更新的master重新定位到您的“旧” EditReadMe2分支中。

Anyway the git rebase is fine to be used in such way.无论如何, git rebase可以以这种方式使用。


An hint:一个提示:
if you are switching to master , git pull ing, switching back to EditReadMe2 only for the purpouse of rebasing, you can use the following sequence and save a couple of commands:如果您要切换到mastergit pull ing,切换回EditReadMe2出于 rebase 的目的,您可以使用以下序列并保存几个命令:

Assuming you are in EditReadMe2假设你在EditReadMe2

git pull
git rebase origin/master

Rebasing will typically produce this result—this need to use --force —because rebasing replaces some existing commit(s) with new-and-improved 1 commits.变基通常会产生这个结果——这需要使用--force因为变基用新的和改进的1提交替换了一些现有的提交。 To really understand how this works, you need to understand how Git uses and finds commits, and how git push and other commands work.要真正理解它是如何工作的,您需要了解 Git 如何使用和查找提交,以及git push和其他命令如何工作。 It's a little bit tricky, First, take a look at my long answer to How to delete all unpushed commits without deleting local changes , to get an idea of what a drawing like:这有点棘手,首先,看看我对如何删除所有未推送的提交而不删除本地更改的长回答,以了解绘图是什么样的:

...--G--H   <-- master
         \
          I   <-- feature (HEAD)

might mean.也许意味着。 In particular, you need to remember how these letters stand in for the raw hash IDs, how each commit points backwards to its parent commit, and how a branch name points to the latest commit on / contained-in that branch.特别是,您需要记住这些字母如何代表原始 hash ID,每个提交如何向后指向其父提交,以及分支名称如何指向该分支上/包含的最新提交。


1 At least, we hope they're improved. 1至少,我们希望它们得到改进。


Setting up配置

Now let's suppose we have a series of commits that's not flawed per se—we don't really need to fix anything in them—but which were made earlier, like this:现在让我们假设我们有一系列本身没有缺陷的提交——我们实际上并不需要修复它们中的任何东西——但是它们是之前进行的,如下所示:

...--G--H   <-- master
         \
          I--J   <-- feature

(no attached HEAD indicates that we don't care which one was checked out before this point). (没有附加的HEAD表示我们不在乎在此之前检查了哪一个)。 We run git checkout master or git switch master, then git pull or similar, and acquire a new master commit, giving us this:我们运行git checkout mastergit switch master,然后git pull或类似的,并获得一个新的master提交,给我们这个:

...--G--H--K   <-- master (HEAD), origin/master
         \
          I--J   <-- feature, origin/feature

We also add or update these remote-tracking names , origin/master and origin/feature .我们还添加或更新了这些远程跟踪名称origin/masterorigin/feature They are our Git's memory of some other Git's branch names.它们是我们的 Git 的 memory其他一些 Git 的分支名称。 Our name origin/master identifies commit K , as does our own branch name master now;我们的名字origin/master标识提交K ,就像我们自己的分支名字master现在一样; and our name origin/feature tells us that over on origin , they have a copy of our branch name feature that identifies commit J too, just like our feature .我们的名字origin/feature告诉我们,在origin上,他们有一个我们的分支名称feature的副本,它也可以识别提交J ,就像我们的feature一样。 (Perhaps they got it when, earlier, we ran git push origin feature .) (也许他们在早些时候运行git push origin feature时得到了它。)

This next part is important: The commit hash IDs—the big ugly strings of letters and digits that these uppercase letters stand in for—are the same across both repositories.下一部分很重要:提交 hash ID(这些大写字母所代表的大而难看的字母和数字字符串)在两个存储库中都是相同的。 The branch names need not be, although in this particular case, they are as well, right now.分支名称不必是,尽管在这种特殊情况下,它们现在也是。

Rebase works by copying commits Rebase 通过复制提交来工作

In this setup, we decide that the flaw with our feature is that it's based on commit H , when the latest commit is now commit K .在这个设置中,我们认为我们的功能的缺陷是它基于提交H ,而最新的提交现在是提交K We'd like to have our feature branch based on commit K .我们希望我们的feature分支基于提交K To do that, we run:为此,我们运行:

git switch feature       # or git checkout feature

giving us:给我们:

...--G--H--K   <-- master, origin/master
         \
          I--J   <-- feature (HEAD), origin/feature

followed by:其次是:

git rebase master

The rebase command lists out the raw hash IDs of those commits that are on branch feature but are not on master . rebase 命令列出了那些在分支feature上但不在master上的提交的原始 hash ID。 In this case, that is the hash IDs of commits I and J .在这种情况下,即提交IJ的 hash ID。 (Note that commits H and earlier are on both branches.) Then, Git uses its special "detached HEAD" mode to start working with commit K , at the tip of master : (请注意,提交H和更早的提交都在两个分支上。)然后, Git 使用其特殊的“分离 HEAD”模式开始使用提交K ,在master的尖端:

...--G--H--K   <-- HEAD, master, origin/master
         \
          I--J   <-- feature, origin/feature

Git applies what we did in commit I and makes a new commit from it. Git 应用我们在提交I中所做的并从中进行新的提交。 This new commit has a new, different hash ID, but re-uses the author name and date-and-time-stamps from I , and re-uses the commit message from I , so that the commit looks an awful lot like commit I .这个新的提交有一个新的、不同的 hash ID,但是重用了I的作者姓名和日期和时间戳,并重用了I的提交消息,因此提交看起来很像提交I . In other words, it's a copy of commit I .换句话说,它是 commit I副本 2 We'll call this new copy I' : 2我们将这个新副本称为I'

             I'  <-- HEAD
            /
...--G--H--K   <-- master, origin/master
         \
          I--J   <-- feature, origin/feature

Having successfully copied I to I' , Git now copies J the same way, resulting in:成功将I复制到I'后,Git 现在以相同的方式复制J ,结果是:

             I'-J'  <-- HEAD
            /
...--G--H--K   <-- master, origin/master
         \
          I--J   <-- feature, origin/feature

The copying process is now done—there are no more commits to copy—so rebase executes its final step, which is to yank the name feature off the commit it used to name and make it point to the last-copied commit, in this case J' :复制过程现在完成了——不再有要复制的提交——所以 rebase 执行它的最后一步,即从它用来命名的提交中拉出名称feature ,并使其指向最后复制的提交,在这种情况下J'

             I'-J'  <-- feature (HEAD)
            /
...--G--H--K   <-- master, origin/master
         \
          I--J   <-- origin/feature

As the drawing implies, in this last step, Git re-attaches HEAD so that we're back in the normal mode of working with an attached HEAD , being on the branch.如图所示,在最后一步中,Git 重新连接HEAD以便我们回到使用附加HEAD的正常模式,在分支上。

Note that the two original commits here are no longer findable using the name feature .请注意,这里的两个原始提交不再使用名称feature找到。 If we did not have the name origin/feature remembering the other Git's feature , we would have abandoned these two commits entirely.如果我们没有名字origin/feature记住另一个Git 的feature ,我们将完全放弃这两个提交。 But our Git remembers that their Git remembers commit J using their name feature .但是我们的 Git 记得他们的Git 记得使用他们的名字feature提交J

In either case, note what we have done.无论哪种情况,请注意我们所做的。 We threw out, or at least tried to throw out, the old commits in favor of these new and improved ones.我们抛弃或至少试图抛弃旧的提交,转而支持这些新的和改进的提交。 We still have access to the old commits through our origin/feature name, because we're remembering that the Git over on origin is remembering commit J through its branch name feature .我们仍然可以通过我们的origin/feature名称访问旧的提交,因为我们记得在origin上的 Git 正在通过分支名称feature记住提交J


2 You can, if you like, copy any commit yourself using git cherry-pick . 2如果愿意,您可以使用git cherry-pick自己复制任何提交。 What rebase does is to detach your HEAD, then do an automated set of cherry-picks, followed by this branch-name motion, similar to git reset or git branch -f . rebase的作用是分离你的 HEAD,然后进行一组自动挑选,然后是这个分支名称动作,类似于git resetgit branch -f In older version of Git, rebase can default to an alternate strategy that does not literally run git cherry-pick , but these details don't usually matter.在旧版本的 Git 中,rebase 可以默认为不真正运行git cherry-pick的替代策略,但这些细节通常无关紧要。


How git push works git push如何工作

The git push command works by having your Git call up some other Git. git push命令通过让您的 Git 调用其他一些 Git 来工作。 This other Git has commits and branch names, too.另一个 Git 也有提交和分支名称。 Their branch names need not match your branch names, but if they don't, things get pretty confusing, so most people make their branch names the same here.他们的分支名称不必与您的分支名称匹配,但如果不匹配,事情就会变得非常混乱,因此大多数人在这里将他们的分支名称设为相同。

Their Git lists out, for your Git, their branch names and commit hash IDs.他们的 Git 列出了您的 Git分支名称和提交 hash ID。 3 This lets your Git figure out which commits you have that they don't, that they'll need. 3这可以让您的 Git 弄清楚您有哪些提交,而他们没有,他们需要。 Your Git then sends those commits to their Git, by their hash IDs.您的 Git 然后将这些提交发送到他们的 Git,通过他们的 hash ID。 Along with those commits, your Git sends any other internal objects their Git requires.除了这些提交,您的 Git 还会发送 Git 需要的任何其他内部对象。

Having sent the right objects, your Git now sends along one or more polite requests or commands.发送正确的对象后,您的 Git 现在会发送一个或多个礼貌请求或命令。 The polite requests have this form: Please, if it's OK, set your name ______ (fill in a branch or tag name) to ______ (fill in a hash ID).礼貌请求有这种形式:如果可以,请将您的姓名______(填写分支或标签名称)设置为______(填写hash ID)。 The commands have one of two forms: I think your name ______ (fill in a branch or tag name) is set to ______ (fill in a hash ID).这些命令有两个 forms 之一:我认为您的名字 ______(填写分支或标签名称)设置为 ______(填写 hash ID)。 If so, set it to ______!如果是这样,请将其设置为______! Or: Set your name ______ to ______!或者:将您的名字 ______ 设置为 ______!

The polite request form will ask them to set their feature to point to commit J' , our copy of J that we used as a new-and-improved version of J .礼貌的请求表单将要求他们将他们的feature设置为指向提交J' ,我们的J副本,我们用作J的新改进版本。 They , however, have no idea that this is meant to be a new-and-improved copy—all they can tell is that we're asking them to throw out commits I and J , and make their name feature remember commit J' instead.然而,他们不知道这是一个新的和改进的副本——他们只能说我们要求他们丢弃提交IJ ,并让他们的名字feature记住提交J' . They say no!他们说不 They say If I do that, I will lose some commits.他们说如果我这样做,我将失去一些提交。

That's what we want them to do: lose commits I and J , replacing them with the new-and-improved ones.这就是我们希望他们做的事情:丢失提交IJ ,用新的和改进的提交替换它们。 To make them do that, we must send them a command.为了让他们这样做,我们必须向他们发送命令。

If we use git push --force-with-lease , we'll send them that conditional command: I think your feature identifies commit J ;如果我们使用git push --force-with-lease ,我们将向他们发送条件命令:我认为您的feature识别提交J if so, make it identify J' instead.如果是这样,则改为识别J' If they accept this command and do it, we and they will have commits I'-J' and we can draw our repository like this now:如果他们接受此命令并执行此操作,我们和他们将提交I'-J' ,我们现在可以像这样绘制我们的存储库:

             I'-J'  <-- feature (HEAD), origin/feature
            /
...--G--H--K   <-- master, origin/master
         \
          I--J   [abandoned]

This --force-with-lease option is generally the right way to do this if it's allowed to do this at all.如果允许这样做,这个--force-with-lease选项通常是正确的方法。 Doing this forces anyone else who is using the feature branch, in yet another Git repository, to update their branches using the new-and-improved commits.这样做会迫使其他任何使用feature分支的人,在另一个 Git 存储库中,使用新的和改进的提交来更新他们的分支。 In general, it's a good idea to get everyone to agree that feature might be rebased this way, before you start rebasing it this way.一般来说,在开始以这种方式重新设置基础之前,让每个人都同意可以以这种方式重新设置feature是一个好主意。 What you need to do is figure out who "everyone" is.你需要做的是弄清楚谁是“每个人”。 If that's just yourself, you need only agree with yourself.如果那只是你自己,你只需要同意你自己。 If that's you and six co-workers, get agreement from the co-workers first.如果是你和六个同事,请先征得同事的同意。

Using git push --force , instead of --force-with-lease , omits the safety check: it just sends to the other Git the command set your feature without any conditional "I think" part first.使用git push --force ,而不是--force-with-lease ,省略了安全检查:它只是向另一个 Git 发送命令设置您的feature ,而不首先使用任何有条件的“我认为”部分。 If your Git is up to date with their Git, so that your origin/feature and their feature both identify commit J , this is OK.如果您的 Git 与他们的 Git 是最新的,那么您的origin/feature及其feature都可以识别提交J ,这没关系。 But what if, just after you finished your work and were about to push, someone else added a new commit L to the feature in the Git over on origin ?但是,如果在您完成工作并即将推送之后,其他人origin上的 Git 的feature中添加了一个新的提交L怎么办? Your force-push will tell that Git to abandon that commit, too.您的强制推送将告诉 Git 也放弃提交。

The force-with-lease option is better because your Git will say to the other Git that you believe their feature identifies commit J , not commit L . force-with-lease 选项更好,因为您的 Git 会告诉另一个 Git 您相信他们的feature识别提交J ,而不是提交L They'll say: Whoops, no, mine is now L and your git push --force-with-lease will fail.他们会说:哎呀,不,我的现在是L并且您的git push --force-with-lease将失败。 You can now git fetch , see that there's a new commit L , and fix up your rebase to copy commit L too, then try your git push --force-with-lease again now that your origin/feature says commit L .您现在可以git fetch ,看到有一个新的提交L ,并修复您的 rebase 以复制提交L ,然后尝试您的git push --force-with-lease再次因为您的origin/feature说提交L


3 The exact mechanism here was rewritten for Git smart protocol v2, which was first turned by default in Git 2.26. 3这里的确切机制是为 Git 智能协议 v2 重写的,在 Git 2.26 中首次默认开启。 I won't go into details, but there's a small but nasty little bug in early v2 protocols where your Git can push way too many objects sometimes.我不会详细介绍 go,但在早期的 v2 协议中有一个小但令人讨厌的小错误,您的 Git 有时会推送太多对象。 This bug is fixed in Git 2.27.此错误已在 Git 2.27 中修复。 If you have 2.26 and pushes are taking much too long, you can work around it with git -c protocol.version=0 push... , or just upgrade.如果您有 2.26 并且推送时间太长,您可以使用git -c protocol.version=0 push...来解决它,或者只是升级。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 更新被拒绝,因为你当前分支的尖端落后于它的远程分支 - Updates were rejected because the tip of your current branch is behind its remote counterpart GitLab 更新被拒绝,因为您当前分支的尖端落后于其远程对应分支 - GitLab Updates were rejected because the tip of your current branch is behind its remote counterpart Git 推送错误“您当前分支的提示落后于其远程对应分支” - Git push error "tip of your current branch is behind its remote counterpart" GitHub更新被拒绝,因为当前分支的提示位于其远程对应的后面 - GitHub updates were rejected because the tip of current branch is behind its remote counterpart 当前分支落后于其远程分支 - Current branch is behind its remote counterpart git 推送 - 更新被拒绝,因为当前分支的尖端落后于远程对应 - git push - updates rejected because tip of current branch behind remote counterpart 如何修复Git中的问题:“更新被拒绝,因为推送的分支提示位于其远程对应的后面” - How to fix issue in Git: “Updates were rejected because a pushed branch tip is behind its remote counterpart” 如何解决GitHub消息,其中显示当前分支的提示位于其远程对应的后面? - How do I resolve a GitHub message that says the tip of my current branch is behind its remote counterpart? 由于远程副本后面的当前分支,推送到裸存储库被拒绝 - Pushing to a bare repository is rejected due to current branch behind remote counterpart 推送到 github 时出错 - 更新被拒绝,因为推送的分支提示在其远程后面 - Getting an error pushing to github - Updates were rejected because a pushed branch tip is behind its remote
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM