本地项目明明都在,GitLab 仓库也建好了,结果最后一步git push死活推不上去——这种场景我遇到太多次了,而且每次卡住的人,翻来覆去就是那几个位置:SSH 认证失败、分支名对不上、远端已经有了历史、地址复制成 HTTPS 或 SSH 格式混用。今天这篇保姆级教程,就把"Git 本地项目上传到 GitLab"这条完整链路拆开讲清楚,从最基础的 Git 安装开始,把每一步操作、每个会踩的坑、每个命令背后的原因都说明白。不管你是刚入行的开发者、在学校做课程设计要交项目,还是公司里要往自建 GitLab 传代码,这篇文章都适合你。跟着走下来,你不只会得到一次成功的 push,还能建立起一个后续日常提交都不别扭的工作流。
1. 先把地基打牢:Git 安装与身份配置
很多人以为上传失败是后面 remote、push 出了问题,但实际上有两件最基础的事没做好,就会一路错到底:一是 Git 本身没装好,二是 Git 身份信息没配。尤其是第二条,你后面 commit 出来的记录会直接变成"无名氏",等推到 GitLab 再回头看,仓库历史全是一堆 unknown,特别难看。
1.1 Windows 上安装 Git 的正确姿势
如果你在 Windows 上,最省心的方式不是去官网慢慢点下载,而是在 PowerShell 或终端里直接用包管理器:
winget install --id Git.Git -e --source winget装完重开一个终端窗口,执行下面这句确认版本:
git --version能输出版本号就代表安装成功,比如git version 2.47.1.windows.1。如果提示找不到命令,大概率是安装时没有把 Git 加入 PATH,去"系统环境变量"检查Path里有没有 Git 的 bin 目录,或者干脆重装一遍。安装向导里那些可选步骤,大多数直接用默认值就行,唯一建议手动确认的,是选择"Git from the command line and also from 3rd-party software"这一项,它保证你在系统自带的终端里也能正常敲git,而不是只能打开 Git Bash。
macOS 用户可以直接用 Homebrew 装:
brew install gitLinux 用户则是 apt 或 yum 装,这里不展开,核心验证命令一样,都是git --version。
1.2 user.name 和 user.email:为什么必须配
这一步经常被跳过,但它是整个 Git 体系的身份凭证。你每次 commit,Git 都会把user.name和user.email写进提交记录里,GitLab 也是靠这个字段把提交关联到对应账号。
建议用你注册 GitLab 时用的同一个邮箱,这样你的 commit 在 GitLab 上会自动对应到你的头像和账号,而不会显示成一个陌生 ID。执行:
git config --global user.name "你的名字" git config --global user.email "你的GitLab注册邮箱"--global表示对当前用户所有仓库生效。验证是否配好,可以这样:
git config --list如果某台机器的某个项目要用不同身份,可以在项目目录里去掉--global单独设置,项目级配置会覆盖全局配置,平时不用特意管它。有一点提醒:不配 user.email 的话,很多环境在 commit 时会直接报错,提示Please tell me who you are,后面的操作全部中止,所以这一节千万别跳。
2. GitLab 仓库端的三个关键动作
本地环境准备好之后,先去 GitLab 网页端把仓库建好。别急着点各种按钮,这一步有三个容易让人后面吃苦头的细节:项目怎么创建、仓库地址选 HTTPS 还是 SSH、SSH 密钥怎么挂上去。
2.1 创建空项目:README 先不要勾
登录 GitLab 后,点"New Project",选"Create blank project"。项目名称一般用小写字母和连字符,比如my-ecommerce-backend,这样后续 clone 地址更干净。可见性方面,Private 是只有自己和被邀请的人能看,Internal 是登录实例的用户都能看,Public 是公开。公司内部一般用 Private 或 Internal,具体看你团队的规则。
这里最关键的一点是:不要勾选"Initialize repository with a README"。
这个选项会在远端帮你生成一次提交(初始化 README 文件的 commit),本地仓库跟远端没有任何共同历史,第一次git push就会被直接拒绝,报non-fast-forward错误。很多新手的"push 上不去",根源就是网页端勾了这个初始化。我们的做法是保持空仓库,本地项目推送过去后,GitLab 页面自然会有 README 显示。
2.2 HTTPS 和 SSH:克隆地址选哪一个
创建完成后,项目主页会有一个"Clone"按钮,展开后能看到两种地址格式:
| 地址类型 | 格式示例 | 认证方式 |
|---|---|---|
| HTTPS | https://gitlab.example.com/group/project.git | 用户名 + 密码或个人访问令牌 |
| SSH | git@gitlab.example.com:group/project.git | SSH 密钥对,无需每次输密码 |
我强烈建议在你自己电脑上使用 SSH 方式。原因不是 HTTPS 不行,而是 HTTPS 每次 push 都可能要输入用户名密码,或者依赖凭证助手缓存;SSH 只要把公钥配置一次,之后所有 GitLab 操作都是免密且稳定。HTTPS 也有适用场景,比如在别人电脑上临时 clone 一个仓库,不想留密钥,用 token 连一次就够了。
2.3 SSH 密钥生成和添加到 GitLab
如果你选了 SSH 地址,先用这条命令生成密钥:
ssh-keygen -t ed25519 -C "你的GitLab邮箱"一路回车即可,默认会生成在~/.ssh/目录下,Windows 的实际路径是C:\Users\你的用户名\.ssh\。生成完查看公钥内容:
cat ~/.ssh/id_ed25519.pub复制整段输出。回到 GitLab,右上角头像 → Preferences → SSH Keys,把这段公钥粘贴进去,标题随便填一个能识别来源的名字,比如"work-laptop",保存。
验证是否配置成功:
ssh -T git@gitlab.example.com注意把gitlab.example.com换成你自己的 GitLab 域名或 IP。第一次连接会提示确认 host key,输入yes回车。看到类似Welcome to GitLab, @yourname!的输出,就说明 SSH 通道已经通了。如果你是从别人电脑临时使用,也可以走 HTTPS 加个人访问令牌的方式,GitLab 个人访问令牌在 Preferences → Access Tokens 里创建,记得勾选write_repository权限,HTTPS 克隆时把它当密码填进去。
提示:个人访问令牌等于是你账号的一把钥匙,创建完只显示一次,别截图、别提交进仓库,更别发给别人。
3. 本地仓库初始化和关联远端仓库
GitLab 端弄完后,回到本地项目目录。这一章的节奏很关键,很多人习惯一股脑git add . && git commit && git push,结果分支名不一致、远端关联错误,又折返重来。我建议按顺序一步步来。
3.1 在项目目录里执行 git init
在你项目根目录打开终端,执行:
git init这会在项目里生成一个隐藏的.git文件夹,代表当前目录已经是 Git 仓库了。如果你不确定目录之前是否已经初始化,可以用git status看,如果报错not a git repository,那就是还没 init。
3.2 .gitignore 为什么要放在第一步做
在第一次git add .之前,先创建一份.gitignore文件,把不需要纳入版本管理的文件和目录忽略掉。常见的有:
node_modules/ target/ dist/ build/ .idea/ .vscode/ *.log .env .DS_Store为什么必须现在做?因为 Git 的忽略规则只对"尚未被跟踪"的文件生效。如果某个文件已经通过git add被纳入了版本管理,你再往.gitignore里写规则,它也不会生效,这正是很多人说"git 的过滤文件没有作用"的根本原因。解决方案也只能事后补救:git rm --cached 文件名把它从暂存区移除,再配合.gitignore才能停止跟踪。与其后面处理,不如在最开始就把规则写好。
另外强调一下.env、配置文件里的数据库密码这类敏感信息,一旦提交进仓库历史,即使后面删掉,历史记录里依然存在,所以第一道防线就靠.gitignore。
3.3 第一次 commit 和统一分支名
暂存全部文件并提交:
git add . git commit -m "chore: init project files"这里解释一下add和commit的关系:add是把你要提交的文件放进"暂存区",commit才是真正生成一条快照记录,两个动作分开才能让你有选择性地提交部分文件,而不是一次把所有改动都打进去。
接下来是很多人忽略的一步:分支名统一。不同版本的 Git,初始分支名可能是master,也可能是main,而 GitLab 新建仓库的默认分支通常是main。如果本地分支叫master而远端默认是main,执行 push 时要么多传参数,要么后续在网页端操作总觉得别扭。最简单解决方案是在首次提交后执行:
git branch -M main-M会把当前分支强制重命名为main(如果已存在同名分支则覆盖重命名)。执行后可以用git branch检查,输出* main就说明当前已经在 main 分支上了。
3.4 remote add 和验证
把本地仓库和 GitLab 上的远端仓库关联起来:
git remote add origin git@gitlab.example.com:group/project.gitorigin是我们给远端仓库起的别名,这是 Git 社区的默认约定,后续的pull、push、fetch都通过它来指代远端,不必真的记住那串完整地址。关联完一定要验证一下:
git remote -v正常会输出两行:
origin git@gitlab.example.com:group/project.git (fetch) origin git@gitlab.example.com:group/project.git (push)如果发现地址填错了,可以用git remote set-url origin 新地址修改,不用删掉重建。如果之前误添加了一个不需要的远端,也可以用git remote remove origin清理后重新 add。
4. 第一次 push 和后续的日常提交循环
所有关联都建立好之后,第一次推送其实就是一个命令的事。这一章除了第一次 push,我更想把后续日常提交的循环逻辑讲清楚,因为很多新手第一次上传成功后,第二天又开始懵了。
4.1 git push -u origin main :-u 到底是什么意思
执行第一次推送:
git push -u origin main这里的-u是--set-upstream的简写,意思是把本地main分支和远程origin/main分支建立起"跟踪关系"。建立之后,以后在 main 分支上直接敲git push或git pull,Git 就知道你要跟哪个远端分支同步,不用再带参数了。
正常推上去后,终端最后几行会显示类似这样的输出:
Enumerating objects: 15, done. ... * [new branch] main -> main到这一步,你的本地项目已经完整上传到 GitLab 了,网页上刷新就能看到所有文件和第一次提交。
4.2 日常更新:pull、add、commit、push 的顺序
上传成功不是终点,之后的每一天你都会重复一个四步循环:写代码、提交、推送、拉取别人的更新。我用一个比较稳的顺序给你:
- 刚开始开发前,先
git pull,把远端别人的改动拉下来; - 写代码;
git add 相关文件或git add .;git commit -m "描述你的改动";- 推送前如果担心远端又有更新,再
git pull一次,没问题就git push。
这里有一个经验性的小原则:如果本地有未提交的改动,先 commit 再 pull,而不是先 pull 再 commit。因为先 commit 了,你本地的工作就被纳入版本管理,后面遇到冲突,Git 能帮你对比和保留;如果先 pull 导致代码冲突,未提交的改动会混杂在一起,处理起来更容易丢内容。
多人协作时,建议用git pull --rebase拉取远端更新,它会把本地提交"搬到"远端最新提交之后,让历史呈线性,回看git log时清爽很多。不习惯 rebase 的人先用普通git pull也完全没问题,只是历史会偶尔出现一个 merge 提交节点,功能上没有任何隐患。
4.3 提交信息别乱写,前缀约定很有用
我在提交信息上吃过亏。早期我写update、fix、change这种毫无区分度的信息,等到一个版本上线后想回滚某个改动,看着一整页update完全不知道哪条是哪一个功能。后来我项目里统一用这种规范:
| 前缀 | 含义 | 示例 |
|---|---|---|
| feat | 新功能 | feat: add login page |
| fix | 修复问题 | fix: correct null pointer |
| docs | 文档改动 | docs: update readme |
| refactor | 重构,不改功能 | refactor: simplify auth service |
| chore | 构建、配置、杂项 | chore: init project files |
配合git log --oneline看历史,一眼就能找到某个功能的提交节点,配合git cherry-pick或git revert做精准操作都很方便。这条规范成本极低,回报很高,强烈建议从第一次提交就开始做。
4.4 分支合并初体验:fetch、merge、checkout
日常上传之外,GitLab 仓库最常用的场景就是多人协作的分支合并。我在热搜里看到有同学问git pick和git fetch的区别,这里简单带一下:Git 里没有单独叫pick的常用命令,你说的很可能是git cherry-pick,那是挑选某个具体的 commit 应用到当前分支;而git fetch是把远端所有分支的最新提交引用拉下来,但不会自动合并到你的当前分支。
最基础的分支操作是这样的,先基于 main 拉一个自己的功能分支:
git checkout -b feature-login开发完成后,切回 main 并更新:
git checkout main git pull origin main然后合并功能分支:
git merge feature-login遇到冲突时,Git 会在文件里标出需要你手动解决的地方,解决完执行git add和git commit收尾。初期阶段不用急着搞复杂的分支策略,先跑熟这个流程,GitLab 上的分支保护、MR 流程后面自然就理解了。
5. 高频报错排查:从认证失败到 push 被拒
只要接触 Git 和 GitLab,就一定会碰见报错。这一章是我觉得全文最有价值的部分,因为我把常见问题和排查链路完整串起来,不是单纯丢结论。
5.1 SSH 认证失败:Permission denied 的完整排查链路
报错长这样:
git@gitlab.example.com: Permission denied (publickey). fatal: Could not read from remote repository.按这个顺序排查:
第一步,确认本地有没有私钥文件:
ls -al ~/.ssh/能看到id_ed25519和id_ed25519.pub就说明密钥存在。如果整个目录都不存在,回第 2.3 节重新生成。
第二步,确认公钥有没有正确上传到 GitLab。执行:
cat ~/.ssh/id_ed25519.pub复制内容,去 GitLab 的 Preferences → SSH Keys 里核对是否和已添加的公钥完全一致。公钥必须以ssh-ed25519或ssh-rsa开头,不要多复制换行。
第三步,用详细模式看 SSH 认证过程:
ssh -vT git@gitlab.example.com在输出的日志里找两行关键信息:如果看到Server accepts key,说明服务端接受你的密钥了;如果只有Offering public key之后再无下文,说明密钥没被服务端识别,大概率第一步或第二步出了问题。
第四步,如果你电脑上同时有多个 SSH 密钥(比如一个 GitHub、一个公司 GitLab),且给它们起了非默认的文件名,SSH 默认不会自动使用那个密钥。解决办法是在~/.ssh/config文件里显式指定:
Host gitlab.example.com HostName gitlab.example.com User git IdentityFile ~/.ssh/id_rsa_companymacOS 和 Linux 用户如果还遇到bad permissions报错,执行一次chmod 600 ~/.ssh/id_ed25519收紧私钥权限。
5.2 push 被拒:non-fast-forward 的两种处理方式
这个错误信息通常长这样:
! [rejected] main -> main (fetch first) error: failed to push some refs to 'git@gitlab.example.com:group/project.git' hint: Updates were rejected because the remote contains work that you do not have locally.它表示远端已经有了本地不存在的提交。最常见的两种原因:
第一种,你当初在 GitLab 网页端勾了"Initialize repository with a README",远端有一条 README 初始化提交,本地完全没有这段历史。这时候执行:
git pull origin main --allow-unrelated-histories--allow-unrelated-histories的意思是允许两个没有共同祖先的仓库合并,用得很少,但处理"本地已有项目 + 网页端初始化过 README"这种场景正好。
第二种,你和别人在同一个分支上协作,对方先推了代码。这时候不要用--allow-unrelated-histories,直接:
git pull origin main --rebase git push origin main把本地提交变基到远端最新提交之后,再推送。如果你确定要完全覆盖远端内容(只建议在个人仓库或共享分支明确可以覆盖时用),可以强推:
git push -f origin main强推是覆盖远端整条分支历史,慎重,一般不要在团队共享分支上执行,否则别人下次 pull 会很痛苦。
5.3 代理配置导致的 connection refused
热搜词里有一条git clone failed to connect to 127.0.0.1 port 7890: connection refused,我猜测大概率是之前给 Git 配过本地代理端口,然后代理服务没开,或者端口已经变了。排查方式很简单:
git config --global --list看有没有http.proxy或https.proxy项。如果有且当前不需要代理了,执行:
git config --global --unset http.proxy git config --global --unset https.proxy如果还需要代理,修正端口号即可。这里的核心点是:Git 的网络连接不受系统代理自动接管,它就是看自己配置文件里有没有 proxy 项,所以你把别处看到的代理地址填进 Git 配置里,代理一关或端口一换,Git 就直接连接失败了。临时环境变量里的http_proxy、https_proxy也可能导致同样问题,Shell 里用env检查一下,有就unset掉。
5.4 其他常见错误的快速对照表
下面这些错误我在日常排查里遇到频率很高,直接列成表,方便定位:
| 报错信息 | 常见原因 | 解决方案 |
|---|---|---|
could not read Username for 'https://...' | HTTPS 方式没有可用的认证信息 | 改用 SSH,或者用 token 当密码 |
remote: HTTP Basic: Access denied | 密码过期或 token 权限不足 | 去 GitLab 重新生成个人访问令牌 |
fatal: not a git repository | 当前目录不是 Git 仓库 | 确认在项目根目录,执行git init |
fatal: refusing to merge unrelated histories | 合并的两个分支无共同历史 | 加--allow-unrelated-histories |
| IDEA 的 GitLab 登录显示 versions older than 14.0 not supported | 新版 IDEA 的 GitLab 集成插件要求 GitLab 版本较新 | 用命令行 + SSH 克隆,绕开 IDE 登录;或升级 GitLab 实例 |
关于最后一条多说一句:IDE 里的 GitLab 登录功能失败,并不代表你没法用 GitLab。JetBrains 的插件和 GitLab 实例版本有兼容要求,如果你们用的自建 GitLab 还老,直接在 IDEA 里用 VCS 菜单添加 Git 仓库 URL,然后靠 SSH 或 token 连,完全没有问题。老版本 GitLab 实例本身也建议尽快升级到官方还在支持的版本,这不光是功能问题,还涉及安全维护。
6. 上传之外的进阶操作与小体会
把项目传上去了,日常 push 也熟练了,可以再往前走一步。这一章聊几个我实际用下来觉得很值的小技巧,还有我踩过一些坑之后总结的个人偏好。
6.1 大文件使用 Git LFS
GitLab 默认对单文件大小有限制,很多实例是 100MB。如果你的项目里有大的图片、设计稿、数据集或者二进制包,直接git add会失败,或者推上去后其他人 clone 起来非常吃力。解决方案是 Git LFS,它在 Git 里存的是指向大文件的指针,真正的大文件内容单独存储,命令也很简单:
git lfs install git lfs track "*.zip" git lfs track "*.psd"随后把生成的.gitattributes一并提交。关键是时间点:一定要在大文件还没有被普通 Git 追踪之前配置好。如果已经提交了大文件,再上 LFS 就是重写历史的活了,新手阶段不建议碰,最干脆的做法是把大文件从仓库里移除、加进.gitignore,以后新建的文件走 LFS。
6.2 提交后反悔:git commit --amend
如果你刚刚 commit 完,发现漏了一个文件,或者提交信息打错字了,而且这个提交还没有 push 到远端,用 amend 很方便:
git add 漏掉的文件 git commit --amend -m "修正后的提交信息"它会把你刚才的提交替换成一个新的提交,不会多出一条历史记录。但注意,如果这个提交已经 push 出去了,就不要轻易 amend,因为别人可能已经基于它做了操作,你改历史后再 push 需要强推,又回到了上一章说的风险区域。
6.3 IDE 集成:IDEA、PyCharm 中如何上传
如果你不太习惯命令行,IDEA 和 PyCharm 的 VCS 菜单也支持完整的 Git 操作。菜单路径一般是:Settings → Version Control → Git,配置好 Git 可执行文件路径。然后在 VCS 菜单里选择 Enable Version Control Integration,选 Git;再把 GitLab 地址通过 VCS → Git → Remotes 添加进去。
IDE 里操作的好处是 diff 和冲突解决界面直观,但底层执行的还是 Git 命令。我个人建议把命令行操作学熟,再把 IDE 当作可视化辅助,两边互相印证,出问题时不至于两眼一抹黑。之前说过的 IDEA 登录 GitLab 报版本不支持,并不影响 IDE 的 Git 功能本身,因为 IDE 的 Git 集成不走那个登录插件。
6.4 我的一点小建议
最后说一点个人体会。上传 GitLab 这件事,真正重要的不是记住那几条命令,而是理解一个顺序:先把.gitignore搞好,再 commit,再设置追踪关系,最后才 push。这个顺序保住了你在远端仓库里的"数字形象",也降低了以后每次提交的心理负担。还有一件事我一直坚持:个人的 SSH 密钥和访问令牌绝不放进任何项目文件里,~/.ssh和 GitLab 设置页是它们唯一该待的地方。细节做扎实了,后面不管是大仓库、多人协作还是 CI/CD,都不会因为这些基础问题绊脚。