ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Ubuntu 18.04 下 Git 推送远程仓库完整教程:从环境配置到 SSH 密钥与推送排错

Ubuntu 18.04 下 Git 推送远程仓库完整教程:从环境配置到 SSH 密钥与推送排错

1. 为什么本地代码必须有一个远程落脚点

很多人第一次听到"把项目传到远程仓库"这件事,脑子里冒出来的第一个念头是:我代码放在自己电脑上好好的,为什么要折腾这一趟?这个问题不搞清楚,后面所有命令都只是照抄,一旦报错就完全不知道从哪下手。所以先把这件事的动机讲透。

远程仓库本质上就是一份带版本历史的云端备份。你在本地写代码,硬盘坏了、系统崩了、误删了文件夹,代码就没了。而远程仓库每提交一次就留一个快照,任何一次历史状态都能翻出来。这是最朴素的价值,也是最容易被忽略的价值——大多数人都是丢过一次代码之后才真正重视这件事。

第二个价值是协作与展示。你写了一个小工具、一个课程设计、一个练手项目,放在本地只有你自己看得到。推到远程仓库之后,别人可以克隆下来跑,可以提问题,可以看到你的提交记录。对于找实习、找工作的人来说,一个持续有提交记录的仓库,比简历上写"熟悉某某技术"有说服力得多。

第三个价值是强制你养成规范习惯。本地写代码可以随便命名、随便提交,但一旦要推到远程,你会开始在意提交信息写什么、哪些文件不该传上去、目录结构是不是清晰。这种"有人看着"的心理暗示,对新手养成工程习惯非常有用。

Ubuntu 18.04 这个环境本身也有它的特殊性。它是一个长期支持版本,系统自带的软件源里 Git 版本不算新,但足够稳定;它的默认 shell 是 bash,终端操作是日常;它的文件权限体系和 Windows 差别很大,这一点在后面配置密钥和推送时会反复踩到。所以这篇文章不是泛泛地讲"怎么用 Git",而是针对 Ubuntu 18.04 这个具体环境,把从零到推送成功的完整链路拆开讲。

适合读这篇内容的人有三类:第一类是完全没接触过 Git、第一次要把代码传上去的新手;第二类是以前在别的系统上用过 Git、换到 Ubuntu 之后发现各种权限和路径问题的人;第三类是已经能推代码、但想搞清楚背后原理、顺便把密钥和忽略规则配置规范的人。三类人关注的点不一样,我会在对应章节里分别展开。

2. 动手之前先把环境和身份理清楚

2.1 确认系统里到底有没有 Git

Ubuntu 18.04 桌面版通常预装了 Git,但服务器版或者精简安装的镜像不一定有。所以第一步不是急着敲命令,而是先确认现状。打开终端,输入:

git --version

如果返回类似git version 2.17.1这样的信息,说明已经装好了,可以直接跳到下一节。如果提示command not found,那就需要安装。安装命令很简单:

sudo apt update sudo apt install git -y

这里有个细节值得说:apt update不是可选项。Ubuntu 18.04 的软件源索引如果很久没更新,直接 install 可能会拉到旧版本甚至报 404。先 update 再 install 是稳妥做法。安装完成后再次执行git --version确认,看到版本号才算真正就绪。

提示:如果你在公司或学校的网络环境下,apt update可能会因为源地址访问慢而卡住。这种情况可以换成国内镜像源,但换源本身是另一套操作,不在本文范围内,遇到再说。

2.2 配置用户名和邮箱,这一步不能省

Git 的每一次提交都会记录"是谁提交的"。这个信息来自两个全局配置项:user.name和user.email。如果不配置,第一次提交时 Git 会直接报错拒绝,提示你告诉它你是谁。

git config --global user.name "你的名字" git config --global user.email "你的邮箱"

关于这两个值填什么,有几个实际经验:

  • user.name填你希望别人在提交记录里看到的名字,可以是真名也可以是昵称,但建议和远程仓库账号的显示名保持一致,方便别人对应。
  • user.email建议填你注册远程仓库时用的邮箱。虽然不强制一致,但一致的话,远程平台能正确地把提交归属到你的账号上,贡献图才会亮。
  • --global表示这是全局配置,对当前用户的所有仓库生效。如果你有多个身份(比如公司一个、个人一个),可以在具体仓库里用不带--global的命令单独覆盖。

配置完可以用下面命令检查:

git config --global --list

看到刚才填的两项就对了。这一步看起来简单,但它是后面所有提交能成功的前提,千万别跳过。

2.3 理解工作区、暂存区、本地仓库这三层结构

在敲任何推送命令之前,必须先在脑子里建立一张图:Git 把代码分成三个区域。

工作区就是你用编辑器实际改文件的地方,你新建一个main.py、改一行README.md,改的都是工作区。暂存区是一个中间缓冲地带,你用git add把想提交的改动放进去。本地仓库是真正存历史的地方,你用git commit把暂存区的内容打包成一个快照存进去。

为什么要有暂存区这个"多此一举"的设计?因为一次提交应该只包含一件逻辑上完整的事。比如你同时改了三个文件,其中两个是修 bug、一个是加新功能,你希望分成两次提交。暂存区就让你能挑着提交,而不是一股脑全塞进去。理解这一点,后面看到git add和git commit分开就不会觉得奇怪了。

而远程仓库是第四层,它在本地仓库之外。git push做的事,就是把本地仓库里已有的提交,同步到远程仓库去。注意关键词是"已有的提交"——你工作区里没 add、没 commit 的改动,push 是推不上去的。这是新手最常见的困惑:"我明明改了代码,怎么推上去还是旧的?"原因就是改动还停在第一层,没走到第三层。

3. 在远程平台建仓库时那几个容易选错的选项

3.1 新建仓库的入口和命名

登录远程仓库平台后,找到新建仓库的入口,通常是一个明显的按钮。点进去之后会让你填仓库名。命名有几个实用建议:

  • 用英文、小写、单词之间用连字符,比如image-tool-demo,不要用中文或空格。中文名在某些命令行操作和 URL 里会变成一串编码,徒增麻烦。
  • 名字尽量体现项目内容,别叫test、aaa、新建文件夹。仓库名是别人看到的第一印象。
  • 如果只是练手,可以勾选初始化选项;如果是把已有本地项目推上去,不要勾选初始化。

3.2 初始化选项到底勾不勾

这是新手最容易踩的坑,单独拿出来讲。新建仓库页面通常有几个勾选项:添加 README、添加 .gitignore、选择开源协议。

如果你是要把本地已经存在的项目推上去,这几个选项全部不要勾。原因在于:一旦远程仓库被初始化,它就有了自己的提交历史(比如一个 README 提交)。而你的本地项目也有自己的历史。两个互不相关的历史要合并,就会遇到refusing to merge unrelated histories这类报错,新手看到直接懵。

反过来,如果你是从零开始一个新项目,那勾上 README 是好的,它让仓库一建好就有一个初始提交,克隆下来不是空的。

判断标准很简单:本地有没有代码?有,就别勾;没有,就勾。这个判断做对了,能省掉后面一大堆麻烦。

3.3 仓库地址的两种形式

建好仓库后,平台会给你一个地址,通常有两种形式:HTTPS 和 SSH。

HTTPS 形式长这样:https://某平台/用户名/仓库名.git。它的特点是配置简单,推送时输入账号密码即可。但每次推送都要输,比较烦,而且现在很多平台已经不支持用登录密码推送了,需要用访问令牌代替。

SSH 形式长这样:git@某平台:用户名/仓库名.git。它需要你先生成一对密钥,把公钥配置到平台上,之后推送就不用再输密码。一次配置,长期省事。

我的建议是:长期用就配 SSH,临时用一次就 HTTPS。下面两节分别讲这两种方式在 Ubuntu 18.04 上的具体操作。

4. 两种连接方式:HTTPS 与 SSH 的取舍和配置

4.1 HTTPS 方式的完整流程

HTTPS 方式最直接。在本地项目目录下执行:

git remote add origin https://某平台/用户名/仓库名.git

origin是给这个远程地址起的别名,是约定俗成的叫法,你也可以叫别的,但没必要。加完之后可以用git remote -v查看,确认地址没写错。

推送时:

git push -u origin master

这里的-u是--set-upstream的简写,作用是把这个本地分支和远程分支关联起来。关联之后,以后在这个分支上直接敲git push就行,不用再写后面那一串。第一次推送建议加上。

关于分支名,Ubuntu 18.04 自带的 Git 2.17 默认分支名还是master。如果你在平台上建仓库时默认分支被设成了main,那推送时就要写git push -u origin main,或者先把本地分支改名。分支名不一致是推送失败的常见原因之一,报错信息通常是src refspec master does not match any,看到这个就去检查分支名。

HTTPS 推送时会要求输入用户名和密码。这里有个坑:现在主流平台基本都要求用访问令牌代替密码。令牌需要在平台的设置里生成,生成后当密码用。如果你输的是登录密码,会提示认证失败。令牌生成后要立刻复制保存,页面关掉就再也看不到了。

4.2 SSH 密钥的生成与配置

SSH 方式稍微多几步,但一劳永逸。第一步生成密钥对:

ssh-keygen -t rsa -b 4096 -C "你的邮箱"

-t rsa指定算法,-b 4096指定长度,-C是注释,一般填邮箱方便识别。执行后会问你密钥存哪,直接回车用默认路径~/.ssh/id_rsa即可。然后会问你要不要设密码短语,可以留空直接回车,也可以设一个增加安全性。留空的话以后推送完全无感,设了的话每次用密钥要输一次。

生成完会有两个文件:id_rsa是私钥,绝对不能给别人;id_rsa.pub是公钥,要配置到平台上。查看公钥内容:

cat ~/.ssh/id_rsa.pub

复制输出的全部内容,到平台的 SSH 密钥设置页面,新建一个密钥,把内容粘进去保存。

4.3 验证 SSH 是否打通

配置完别急着推送,先验证连接:

ssh -T git@某平台

第一次连接会问你是否信任该主机,输入 yes 回车。如果看到类似"成功认证但不开 shell"的提示,说明通了。如果提示权限拒绝,说明公钥没配对,回去检查是不是复制漏了字符,或者复制成了私钥。

这里有个 Ubuntu 18.04 上常见的权限问题:~/.ssh目录权限必须是 700,私钥文件必须是 600。如果权限太开放,SSH 会拒绝使用这个密钥,报错信息里会有"permissions are too open"字样。修复命令:

chmod 700 ~/.ssh chmod 600 ~/.ssh/id_rsa

这个坑在从 Windows 拷贝.ssh目录过来的时候特别容易遇到,因为 Windows 的权限模型和 Linux 完全不同。

5. 从本地目录到远程仓库的完整推送链路

5.1 在项目目录初始化本地仓库

进入你的项目文件夹:

cd /path/to/your/project

然后初始化:

git init

这一步会在当前目录创建一个隐藏的.git文件夹,所有版本历史都存在里面。执行成功会提示"初始化了一个空的 Git 仓库"。

注意:git init要在项目根目录执行,不要在某个子文件夹里执行。如果你不小心在错误的层级初始化了,删掉那个.git文件夹重新来即可,命令是rm -rf .git。这个操作不可逆,执行前确认路径没错。

5.2 用 .gitignore 把不该传的文件挡在外面

在git add之前,先创建.gitignore文件,这是很多人忽略但极其重要的一步。有些文件根本不该进仓库:编译产物、依赖目录、日志、本地配置、密钥文件。一旦传上去,不仅仓库臃肿,还可能泄露敏感信息。

在项目根目录创建.gitignore:

touch .gitignore

然后用编辑器写入规则。一个通用的 Python 项目示例:

# 依赖目录 node_modules/ venv/ __pycache__/ # 编译产物 *.pyc *.class build/ dist/ # 日志和临时文件 *.log *.tmp .DS_Store # 本地配置和密钥 .env *.key config.local.*

规则写法有讲究:node_modules/末尾的斜杠表示只匹配目录;*.log表示匹配所有后缀为 log 的文件;config.local.*里的星号是通配符。写完之后,被匹配的文件就不会出现在git status的待提交列表里。

如果某个文件已经被 add 进去了才发现要忽略,光改.gitignore是没用的,得先把它从暂存区移除:

git rm --cached 文件名

这个命令只从 Git 的追踪里移除,不会删你磁盘上的文件,可以放心用。

5.3 添加、提交、关联远程、推送四步走

准备工作做完,正式推送就四步。

第一步,把所有要提交的文件加入暂存区:

git add .

末尾的点表示当前目录下所有改动。如果只想加某个文件,把点换成文件名即可。

第二步,提交:

git commit -m "初始化项目"

-m后面是提交信息。提交信息要写清楚这次改了什么,别写"更新""修改"这种没信息量的词。好的提交信息像"添加图片压缩功能""修复路径拼接错误",别人看历史记录时能一眼明白。

第三步,关联远程仓库:

git remote add origin git@某平台:用户名/仓库名.git

第四步,推送:

git push -u origin master

如果前面 SSH 配好了,这一步会直接成功,输出里能看到对象计数和写入进度。看到类似"分支 master 设置为跟踪来自 origin 的远程分支 master"就说明关联也建好了。

5.4 推送成功后的验证

推送完别急着关终端,做两件事确认。第一,本地执行:

git log --oneline

能看到刚才那条提交记录,说明本地仓库没问题。第二,刷新远程仓库页面,应该能看到你的文件和提交信息。两边都对上了,才算真正完成。

如果远程页面还是空的,或者只有初始化时那个 README,说明推送没成功,回去看终端有没有报错。常见错误和处理方式在下一节展开。

6. 推送失败时的排查顺序和典型报错

6.1 先看报错信息,别瞎试

新手遇到推送失败,第一反应是反复重敲命令,或者上网搜一堆命令乱试。正确做法是先完整读一遍报错信息。Git 的报错通常很具体,会告诉你哪个环节出了问题。下面按出现频率从高到低列几个典型情况。

6.2 认证失败类

报错关键词:Authentication failed、Permission denied (publickey)、fatal: Could not read from remote repository。

这类问题几乎都出在认证环节。如果是 HTTPS,检查是不是用了访问令牌而不是登录密码;如果是 SSH,按这个顺序排查:

  1. 公钥是否真的配置到了平台,有没有复制完整。
  2. ~/.ssh和私钥的权限是否正确(700 和 600)。
  3. 用ssh -T单独测试连接,看具体报什么。
  4. 如果本地有多个密钥,可能需要配置~/.ssh/config指定用哪个。

6.3 历史冲突类

报错关键词:refusing to merge unrelated histories、Updates were rejected because the remote contains work that you do not have。

第一种情况是本地和远程各有独立历史,通常是因为建仓库时勾了初始化选项。解决办法是拉取时加参数允许合并无关历史:

git pull origin master --allow-unrelated-histories

解决冲突后再推。

第二种情况是远程有你本地没有的提交,比如你在网页上直接改了 README。Git 拒绝覆盖,要求你先拉取。这时候:

git pull origin master

拉下来合并,处理可能的冲突,再推。

6.4 分支名不匹配类

报错关键词:src refspec master does not match any、error: failed to push some refs。

这通常是本地分支名和远程默认分支名不一致。先看本地有什么分支:

git branch

如果本地是master而远程默认是main,可以推送时指定:

git push -u origin master:main

冒号左边是本地分支,右边是远程分支。或者干脆把本地分支改名:

git branch -M main

-M是强制改名,改完再推。

6.5 大文件被拒类

报错关键词:file is 某大小; this exceeds 平台的文件大小限制。

远程平台对单个文件大小有限制,超过就会被拒。常见于不小心把数据集、模型文件、视频传上去。处理方式是先把大文件从提交里移除,加进.gitignore,然后重新提交。如果大文件已经在历史里了,清理起来比较麻烦,需要用到历史重写工具,新手建议直接重建仓库更省事。

排查这类问题的通用思路是:先定位是认证、历史、分支还是文件的问题,再针对性处理。不要一上来就git push -f强推,强推会覆盖远程历史,如果远程有别人的提交,会造成数据丢失,是很危险的操作。

7. 日常维护:让仓库保持干净可用的几个习惯

7.1 提交粒度与提交信息

仓库建好只是开始,后面每天都要提交。两个习惯决定你的仓库是"能看"还是"一团乱"。

第一,一次提交只做一件事。修 bug 就只提交修 bug 的改动,加功能就只提交加功能的改动。不要攒一周的改动一次性提交,那样历史记录毫无价值,出问题也没法回退到某个具体状态。

第二,提交信息写清楚"做了什么"。推荐用动词开头,比如"添加""修复""重构""删除"。如果团队有规范,按规范来。个人项目也建议保持一致的风格,时间长了回头看会感谢自己。

7.2 定期拉取,避免冲突堆积

如果你在多台机器上开发,或者有协作者,记得每次开始工作前先拉取:

git pull

拉取会把远程的新提交同步到本地。养成这个习惯,冲突会在小范围内解决,而不是攒到最后一次性爆发。冲突本身不可怕,可怕的是拖了很久才处理,那时候改动交织在一起,很难理清。

7.3 分支的简单用法

个人项目也建议用分支。主分支保持稳定可用,新功能在单独分支上开发,做完再合并。基本操作:

git branch 新功能名 # 创建分支 git checkout 新功能名 # 切换过去 # 开发、提交... git checkout master # 切回主分支 git merge 新功能名 # 合并

这样主分支永远是能跑的版本,实验性的代码不会污染它。合并时如果两个分支改了同一处,会产生冲突,需要手动编辑冲突文件,把<<<<<<<、=======、>>>>>>>这些标记之间的内容整理成最终想要的样子,然后重新 add 和 commit。

7.4 关于回退的一点经验

写代码难免提交错东西。如果只是提交信息写错了,用:

git commit --amend

可以修改最近一次提交的信息。如果想把某个文件从最近一次提交里撤出来,用git reset系列命令。这些操作会改写历史,如果已经推送到远程,再推就需要强推,而强推有风险。所以我的经验是:推送之前多检查一遍,git status和git diff看一眼要提交什么,比事后回退省心得多。

Ubuntu 18.04 上这套流程跑通之后,换到更新的系统版本操作基本一致,命令没有变化。真正需要适应的是不同平台在认证方式上的调整,比如令牌的引入、默认分支名的变化,这些属于平台侧的演进,跟着官方提示走就行。把本地这套三层结构、密钥配置、忽略规则理解透,无论平台怎么变,你都能快速对上号。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进