◆

Git push 推送分支到远程仓库详解

2023-02-02 01:370

git push

从将本地的分支版本上传到远程并合并。

在动手之前先理清 push 到底做了什么:它把本地某个分支上远程还没有的提交对象发送过去,然后请求远程仓库把对应的分支引用(ref)移动到新的提交上。关键点在于远程分支的移动必须是"快进"(fast-forward)的,也就是新提交必须包含远程当前提交的全部历史。一旦不满足这个条件,git 就会拒绝推送并提示 rejected ... non-fast-forward,这是在保护别人已经推上去的提交不被我们悄悄覆盖掉。

理解了这一层,后面各种参数和报错就都有了统一的解释。

  • 命令格式
git push <远程主机名> <本地分支名>:<远程分支名>

冒号前后分别是源和目标,这个写法叫 refspec。它意味着本地分支名和远程分支名可以不一样,比如把本地的 dev 推到远程的 feature/login 完全合法。远程主机名一般就是 origin,它是 git clone 时自动创建的默认远程别名,可以用 git remote -v 查看当前仓库配了哪些远程地址。

如果本地分支名与远程分支名相同,则可以省略冒号:

git push <远程主机名> <本地分支名>
  • 示例 以下命令将本地的 master 分支推送到 origin 主机的 master 分支。
git push origin master

相等于:

git push origin master:master
  • 如果本地版本与远程版本有差异,但又要强制推送可以使用 --force 参数:
git push --force origin master

--force(简写 -f)跳过了前面说的快进检查,直接把远程分支的引用挪到我们本地的提交上。这意味着远程上那些我们本地没有的提交会从分支历史里消失,如果那些提交是同事刚推上来的,他的工作就丢了。所以这个参数要谨慎:共享分支(master、main、develop)上原则上不要用,只在自己独占的功能分支上用。

更安全的强制推送:--force-with-lease

多数需要 --force 的场景(rebase 之后、commit --amend 之后重推),其实都可以换成一个更安全的参数:

git push --force-with-lease origin feature/login

它的逻辑是:先检查远程分支当前的提交,是否和我们本地记录的远程分支状态(origin/feature/login)一致。一致说明从我们上次 fetch 到现在没有别人推过东西,覆盖是安全的,推送执行;不一致说明有人插了新提交,推送被拒绝。这样既能完成重写历史,又不会把别人的提交冲掉。

有一点要注意:如果在这之前跑过 git fetch,本地的远程跟踪分支就已经更新成别人的最新提交了,--force-with-lease 的检查会通过,保护就失效了。所以正确姿势是 rebase 完直接推,不要先 fetch 再强推。

推送新分支并建立跟踪关系

本地新建的分支第一次推送时,远程还不存在这个分支,加上 -u 让 git 顺便记下上下游对应关系:

git push -u origin feature/login

-u 是 --set-upstream 的简写。设置好之后,这个分支后续直接敲 git push 和 git pull 就行,不用每次都写远程名和分支名。查看已有的跟踪关系可以用:

git branch -vv

删除远程分支

回到 refspec 的写法上:冒号左边留空,表示"用空内容去覆盖远程分支",效果就是删除它。

git push origin :feature/login

现在更推荐用语义明确的 --delete,两条命令等价,后者不容易写错:

git push origin --delete feature/login

推送标签

标签默认不会随 git push 一起推送,需要显式指定。推送单个标签:

git push origin v1.0.0

把本地所有标签一次推上去:

git push origin --tags

如果希望在推送分支的同时带上那些指向被推送提交的标签,用 --follow-tags,它比 --tags 更克制,不会把无关的本地标签一起推上去:

git push --follow-tags origin master

几个常用参数

# 演练:完整走一遍流程但不真的推送,用来确认将要发生什么
git push --dry-run origin master

# 推送所有本地分支
git push --all origin

# 跳过 pre-push 钩子(比如钩子里的测试跑得太慢时临时用)
git push --no-verify origin master

--dry-run 在执行有风险的操作前特别有用,尤其是配合 --force 或 --delete 时,先加上它确认一遍目标对不对。

常见报错怎么处理

rejected / non-fast-forward:远程有我们本地没有的提交。正确做法是先把远程的改动整合到本地再推,而不是直接强推:

git pull --rebase origin master
git push origin master

--rebase 会把我们的本地提交挪到远程最新提交之后,历史是一条直线,比默认的 merge 方式产生的历史更干净。整合过程中如果有冲突,解决完 git rebase --continue 即可。

src refspec xxx does not match any:本地根本没有这个分支名,通常是拼错了,或者是在一个还没有任何提交的新仓库里推送。用 git branch 确认分支名。

Updates were rejected because the remote contains work that you do not have locally:和第一种是同一类问题,多见于本地初始化仓库后关联到一个已有内容的远程仓库(远程有 README 而本地没有)。同样用 git pull --rebase 先整合。

Permission denied (publickey):这是认证问题不是 push 逻辑问题,说明 SSH key 没配好或没加到平台账号上。用 ssh -T git@github.com 单独验证连通性。

💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEGit push 推送分支到远程仓库详解加载中