
本地项目明明都在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 体系的身份凭证。你每次 commitGit 都会把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按钮展开后能看到两种地址格式地址类型格式示例认证方式HTTPShttps://gitlab.example.com/group/project.git用户名 密码或个人访问令牌SSHgitgitlab.example.com:group/project.gitSSH 密钥对无需每次输密码我强烈建议在你自己电脑上使用 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 gitgitlab.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 gitgitlab.example.com:group/project.gitorigin是我们给远端仓库起的别名这是 Git 社区的默认约定后续的pull、push、fetch都通过它来指代远端不必真的记住那串完整地址。关联完一定要验证一下git remote -v正常会输出两行origin gitgitlab.example.com:group/project.git (fetch) origin gitgitlab.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 pullGit 就知道你要跟哪个远端分支同步不用再带参数了。正常推上去后终端最后几行会显示类似这样的输出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 pagefix修复问题fix: correct null pointerdocs文档改动docs: update readmerefactor重构不改功能refactor: simplify auth servicechore构建、配置、杂项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 的完整排查链路报错长这样gitgitlab.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 gitgitlab.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 gitgitlab.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 initfatal: refusing to merge unrelated histories合并的两个分支无共同历史加--allow-unrelated-historiesIDEA 的 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 LFSGitLab 默认对单文件大小有限制很多实例是 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都不会因为这些基础问题绊脚。