我做过一个小统计,但凡哪天工作群里冒出一条“谁帮我看看,分支推不上去了”,接下来的对话大概率会沿着“你git pull了吗”“你ssh配置了吗”“你是不是没commit”一路滑向玄学。版本控制这东西,平时看起来人人都会,真正手忙脚乱的场景全集中在“创建分支、提交代码、推送远端”这三板斧上。今天这篇就拿GitLab当远端,把从安装Git到在新分支上提交代码的完整链路,掰开揉碎讲一遍。不管你是刚入职要用公司GitLab的应届生,还是被同事拉着救火的老开发,这套流程踩过的坑我都提前帮你踩了,照着做基本能一次跑通。
1. 环境准备:Git安装和基础配置
1.1 三平台安装Git的实操差异
先说安装。Windows上我建议直接从官网下载安装包,一路Next是可行,但有几个坑值得多看一眼:安装路径尽量别带空格和中文字符,选默认的“Git Bash”组件别去掉,换行符处理那里选“Checkout as-is, Commit as-is”,也就是第二个选项,否则后面在Windows上改过的脚本文件,推到Linux服务器上大概率会出现诡异的换行问题。
macOS上最简单的是先装Homebrew,然后执行brew install git。如果你机器上已经自带git,但版本比较老,直接用brew upgrade git更新。Linux这边分系,Ubuntu/Debian用apt install git,CentOS/RHEL用yum install git。装完统一验证一下:
git --version能看到类似git version 2.39.2这样的输出就说明装好了。如果系统提示“git: command not found”,大概率是安装过程没走完,或者安装完没重启终端,PATH还没刷新,先检查这两点。
1.2 设置用户名和邮箱,这一步不能跳
装完后的第一件事不是clone也不是创建分支,而是设置身份信息。Git每次提交都会记录提交者姓名和邮箱,如果你不设,后面commit时会报错,或者会自动以“用户名@主机名”这种乱七八糟的形式提交。设置命令如下:
git config --global user.name "你的名字" git config --global user.email "你的邮箱@example.com"--global参数表示全局生效,也就是这台机器上所有仓库都用这个身份。如果某个项目想要单独指定不同身份,去掉--global,在仓库目录内执行一遍就行。检查是否配置成功,用git config --list就能看到所有配置项。
有个细节值得说:很多公司的GitLab账号是和邮箱绑定的,你commit记录里显示的邮箱如果和你GitLab账号不一致,提交记录虽然能推上去,但平台上经常不显示你的头像,也没法直接关联到你的账号。所以这个邮箱最好和你注册GitLab时用的邮箱保持一致。我见过好几个人因为邮箱不一致,代码明明是他写的,看记录却成了别人,排查起来非常闹心。
2. 对接GitLab:SSH密钥配置与远程仓库
2.1 SSH和HTTPS选哪个
连接GitLab仓库有SSH和HTTPS两种协议。HTTPS的优点是配置简单,clone的时候直接输入GitLab账号密码或者访问令牌;缺点是每次push和pull都可能要重复认证,就算用凭据管理器记住了密码,在公司电脑上这也不算高效,而且如果你开了二次验证,HTTPS方式用起来更麻烦。
SSH的优点是配一次之后长期免密,而且更安全,提交推送都不用再输密码,CI/CD这类自动化流程几乎都依赖SSH。缺点是首次配置稍微麻烦一点。我一般建议干活的主力机直接配SSH,一次性投入,后面每天省下的时间相当可观。
2.2 生成密钥并添加到GitLab
配SSH分三步。第一步,在本地生成密钥对:
ssh-keygen -t rsa -b 4096 -C "你的邮箱@example.com"执行后会问保存路径,直接回车用默认路径~/.ssh/id_rsa。然后问你设置passphrase,我建议直接留空回车,不然每次push都要输密码,还不如用HTTPS。生成完之后,~/.ssh目录下会多出两个文件,id_rsa是私钥,打死不要外传,id_rsa.pub是公钥,就是要贴到GitLab上去的那把钥匙。
第二步,把公钥内容复制下来。最简单的方式:
cat ~/.ssh/id_rsa.pub把输出的内容完整复制,包括开头的ssh-rsa和最后的邮箱后缀,一行都不能少。
第三步,打开GitLab网页端,右上角头像点进去,选择“Preferences”,左侧找到“SSH Keys”,把公钥粘贴到“Key”文本框里,Title随便填一个能提醒你自己的名字,比如“work-laptop”,点击“Add key”保存。
验证是否配置成功:
ssh -T git@gitlab.com如果看到Welcome to GitLab, @你的用户名!这样的欢迎语,就说明通了。注意,公司自己搭的GitLab可能不是默认的22端口,输出会不一样,但核心是看到“Welcome”或者至少没有出现Permission denied,就算成功。
2.3 克隆远程仓库到本地
SSH配好之后,clone代码就用SSH地址,不要在GitLab页面上用“Download ZIP”下载源码包。原因很简单:ZIP包没有.git历史记录,你拿到手的是一个“死代码快照”,完全脱离版本控制。正确的克隆命令:
git clone git@gitlab.com:your-group/your-project.git这条命令会在当前目录生成一个与仓库同名的文件夹,里面就是完整的工作副本和所有历史提交。克隆完之后,进入目录,先跑一条git remote -v看一眼远程地址,确认和你预期的GitLab地址一致。这一步虽然简单,但能帮你提前发现配置错误,不用等推送时才暴露。
注意:如果你们公司的GitLab是用内网IP搭的,克隆地址里可能不是域名而是IP。只要SSH密钥配了,IP地址一样能通,不用纠结域名的问题。
3. 核心操作:创建新分支并推送到GitLab
3.1 三种创建分支的方式对比
分支是Git的精髓,但很多新手对“在哪儿创建分支”是有误解的。其实有两条路径:在GitLab网页上创建,然后本地拉取;或者在本地创建,然后推送上去。我更推荐先在本地创建,因为本地创建的同时就能关联当前工作内容,提交前还能检查代码,主动权在手里。
本地创建分支的命令,最常见的有三个:
git branch feature/login git checkout -b feature/login git switch -c feature/login第一行命令只是创建分支,但不会切换过去,你还在原分支上。第二行是创建并切换到新分支,一条命令搞定,最常用。第三行是Git 2.23版本之后推出的新写法,语义更清晰,推荐新项目直接用这个。
有人会问,我到底该从哪个分支切出来?这个没有标准答案,但主流做法是:从当前最新的主分支切。所以在创建新分支之前,务必先确认当前分支,并且把基础分支拉到最新:
git checkout master git pull origin master git checkout -b feature/login如果团队用的是main,就把master替换成main。这样做的好处是,新分支的基础代码是最新的,后面开发完合并回去时冲突会少很多。从这个细节上就能看出好的Git习惯和乱来之间的差别。
3.2 推送新分支到GitLab
本地创建完分支还不够,它只存在于你的电脑上。你写了一段代码,想给同事看,或者想在GitLab上发起合并请求,就必须把分支推到远端。命令如下:
git push -u origin feature/login这里-u参数全称是--set-upstream,作用是建立本地分支和远端分支的追踪关系。加了这个参数之后,以后在这个分支上直接执行git push和git pull,Git就知道你要推送/拉取的是远端的origin/feature/login,不需要再每次带完整参数。如果不加-u,第一次推送后本地分支和远端分支没有关联,后面git pull会报错或者行为不符合预期。
推送成功之后,GitLab仓库页面的分支列表里就能看到feature/login了,点进去就能查看代码,也可以发起Merge Request。
3.3 分支管理的进阶习惯
分支创建本身不难,难的是让分支协作起来不乱。有几个习惯我强烈建议建立:
- 分支名称要能看懂是干什么的。
feature/login比test强一百倍,fix/order-price-bug也比bugfix清楚得多。团队如果有一套自己的命名规范,直接按团队规范来。 - 一个分支只做一件事。新功能、修bug、优化重构,都分开分支,不要混在一起。混在一起的后果是review代码时没法局部回滚,出现问题时定位也难。
- 分支合并完之后,随手删掉远端分支和本地分支:
git branch -d feature/login git push origin --delete feature/login这样长期下来,GitLab上的分支列表不会几百个躺在那里变成“僵尸分支”。我见过不少项目,GitLab上分支上百个,问了一圈没人知道那些分支是干嘛的,也不敢删,版本管理就变成了版本坟场。
4. 上传代码:add、commit、push的完整链路
4.1 git status和git diff:提交前先看清楚
很多误操作都是因为没看清状态就急着提交。拿到手的第一步永远是:
git status这个命令会列出所有变更的文件,包括新增文件、修改文件、删除文件和未跟踪文件。看懂这个输出是基本功:modified:表示已跟踪文件的修改,new file:表示已暂存的新文件,Untracked files:表示Git从来没跟踪过的新文件。
如果想知道具体改了什么内容,用下面这条命令:
git diff显示的是工作区里修改过但没有暂存的具体代码变动。如果改动很多,可以只看某个文件:
git diff src/views/Login.vue这一步能防止你“闭着眼提交”,尤其是改了很多文件的时候,git status只显示文件名,git diff能看到内容,配合着看才能确认你改的东西是预期内的。
4.2 git add的三种姿势分别用在什么场景
准备就绪后,把文件加入暂存区(staging area)。常用三种姿势:
git add . # 添加当前目录下所有变更 git add src/views/Login.vue # 只添加指定文件 git add -p # 交互式逐块添加git add .最省事,但也最危险,因为会把当前目录下所有文件都加进去,包括你可能不想提交的临时文件、调试代码。所以我更推荐精确到文件,或者至少先看一眼git status再决定。如果是大改动,用git add -p可以逐块确认,虽然第一次用会觉得麻烦,但用顺了之后你会发现这是避免“把调试代码也提交上去”的利器。
4.3 写好commit信息,比你想的重要
暂存区准备好之后,下一步就是提交:
git commit -m "feat: 新增登录功能"commit信息怎么写,直接决定了一个项目的可维护性。我不是在讲情怀,是实实在在会影响到人的:你三个月后回来看一个提交记录,如果写的都是“更新”“修改”“test”,你根本不知道这次提交做了什么;如果同事和你协作,他review代码时看commit信息也完全跟不上思路。
约定俗成的写法是参考社区流行的commitizen规范,核心格式是“类型: 描述”,类型包括feat(新功能)、fix(修复bug)、docs(文档变更)、style(格式调整)、refactor(重构)、test(测试相关)等。描述的动词用一般现在时,简洁但能说明意图。
如果提交完之后发现信息写错了,在还没推送的情况下可以修改:
git commit --amend -m "feat: 新增登录功能(修正:补充校验逻辑)"注意,--amend会改写上一次提交记录,如果这个提交已经推送到了远端,不要再amend,否则会制造分叉历史,给团队带来不必要的麻烦。
4.4 git push推送代码与校验跟踪关系
commit完成后,代码还在本地,推送才会上远端:
git push如果前面用了-u建立了追踪关系,这里直接git push就行。如果没有追踪关系,会报错提示你需要指定upstream,那就要执行一次前面讲的完整命令:git push -u origin 分支名。
push的过程其实分两步:先是把所有本地提交和远端分支对比,找出本地有而远端没有的提交;然后把这些提交的差异打包发送过去。如果远端已经有了别人提交的内容,而你没有拉取,就会遇到经典的“rejected”错误,这个具体怎么处理,我们放到下一节说。
推送成功后,正常会输出类似master -> master或feature/login -> feature/login这样的提示,看到就说明已经成功上传到GitLab了。
提醒:push之前,最后再确认一遍你当前所在的分支。我见过不止一次有人在新功能分支上写代码,结果commit完之后发现当前分支竟然是master,差点把半成品直接推到主干。提交之前养成习惯,可以用
git branch --show-current看一下当前分支名。
5. 常见问题排查与避坑技巧实录
5.1 推送被拒:Non-fast-forward
这是最常见也最让人慌的报错。原因是远端分支上有你不是最新提交,也就是说,你和同事改了同一分支,别人先推上去了,你的本地提交记录和远端历史产生了分叉。Git为了保护远端数据,拒绝了这个推送。
解决办法是按顺序操作:
git pull origin 分支名 git pushgit pull会先拉取远端变更到本地,再尝试合并。如果两边改的不是同一个文件,Git会自动合并,直接推送就行;如果改了同一个文件的同一区域,就会产生冲突,提示你所在文件有冲突,需要手动解决。
解决冲突的过程:打开冲突文件,搜索<<<<<<<和>>>>>>>标记,这两个标记之间的内容就是冲突区域。上面是你的代码,下面是对端代码,手动选择保留哪个、合并哪部分,删掉标记之后保存文件,然后:
git add 冲突文件 git commit -m "merge: 合并远端变更" git push如果你不想用merge的方式,也可以用git pull --rebase,它的效果是先暂存你的本地提交,拉到远端最新代码后,再把你的提交“垫”到最新代码前面,历史记录会更线性干净。初次使用rebase时容易心生畏惧,但掌握后是真的好用。
5.2 认证失败:Permission denied / login failed
用HTTPS连接GitLab时,如果账号密码输不对,或者启用了二次验证但没使用Access Token,就会报login failed. check api token or gitlab version这类错误。解决思路是:GitLab不推荐直接用账号密码做Git操作,而是生成一个Personal Access Token,在HTTPS认证时用户名为你的账号名,密码填Token而不是登录密码。Token在GitLab的“Access Tokens”里生成,记得勾选write_repository权限。
用SSH连接时如果报Permission denied (publickey),大概率是公钥没加到GitLab上,或者本地用的不是默认私钥路径。可以先用ssh -T git@gitlab.com测试,如果显示Permission denied,回头检查粘贴公钥时是不是漏了字符。如果确认公钥没问题,再看看到底用的是不是默认私钥,如果不是,在~/.ssh/config里配置一下对应主机的IdentityFile路径。
5.3 误将大文件提交入库
Git本身不限制单文件大小,但GitLab有仓库体积限制,超过限制就会报类似上传失败的错。很多人遇到这个问题,是因为不小心把node_modules、dist这类构建产物或者超大的压缩包提交上去了。
最直接的办法是:提前用.gitignore文件规避,把常见的依赖目录、构建产物目录、环境配置文件都写进去。如果已经误提交了,处理起来比较麻烦,可以用git rm --cached 文件名把文件从Git索引里移除但保留本地文件,然后重新commit、push,但要注意这只会让“后续版本”不包含该文件,历史里还是有大文件的,仓库体积不会真正减小。彻底清除历史需要用到git filter-branch或者BFG工具,这个过程比较烧脑,建议下次动手续前先上.gitignore。
5.4 commit信息或者分支名写错了
分支名推上去之后发现拼写错了,比如把feature/login打成了feature/lgoin,处理方式是重命名分支并重新推送:
git branch -m feature/lgoin feature/login git push origin feature/login git push origin --delete feature/lgoin第一条命令在本地重命名当前分支,第二条推送到新名字的远端分支,第三条删除错误名字的远端分支。这样操作后,GitLab上就只保留一个正确分支。
如果commit写错信息,在推送之前用git commit --amend,修改完之后再push。如果已经推送了,注意amend会改变commit hash,需要强制推送git push --force,但这会覆盖远端历史,团队协作时务必先和同事确认,否则别人基于旧commit开发的代码会直接崩掉。
5.5 Windows环境下的换行符问题
在Windows上开发,如果代码里又混用了CRLF和LF,很容易出现一种诡异现象:文件内容没变,但git diff显示所有行都被修改了。原因是Git默认把换行符做了转换,Windows上是CRLF,Linux上是LF,转换后对比就出现了大量假差异。
解决办法是在仓库根目录建一个.gitattributes文件,统一指定文件的换行符策略,比如:
* text=auto *.sh text eol=lf *.bat text eol=crlf这样Git在拉取和提交时会自动处理换行符,团队成员的差异就会小很多。这条经验尤其适合那种团队里混合使用Windows和macOS的情况,早配上早省心。
5.6 拉代码遇到网络问题
代码仓库在远端,网络不稳定或者代理配置有问题时,拉取和推送都可能报网络错误。常见表现是连接超时、unable to access这类提示。解决思路分两层:
第一层,检查本地能不能访问GitLab服务器,最简单的验证方式:
ping gitlab.example.com如果ping不通,先解决网络连通性问题。第二层,如果通但Git操作还是慢或报错,考虑设置Git的http协议超时时间:
git config --global http.lowSpeedLimit 1000 git config --global http.lowSpeedTime 30这两个参数的意思是,如果网速低于1000字节/秒并持续30秒,Git就报错而不是无限等待。设置之后,网络差时至少能快速失败,而不是卡在那里等很久。
6. 本地分支与远端代码保持一致:同步技巧
6.1 先fetch再对比,不要急着pull
很多人的习惯是直接git pull,一拉就完事。但pull其实是两步操作的组合:fetch(拉取远端数据)+ merge(合并到当前分支)。如果你只想看看远端有什么变化,先不要动当前工作区,用git fetch才是好习惯:
git fetch origin执行完,远端分支的最新状态会同步到本地名为origin/xxx的引用下,但你的工作区不会改变。此时可以查看差异:
git log HEAD..origin/master --oneline这个命令会列出远端master分支上有,但你的本地master还没有的提交。想看看具体文件差异,用:
git diff HEAD origin/master确认无误之后再合并,或者直接pull。用这个流程可以避免“一pull就把别人的半成品代码拉进自己工作区”的尴尬。
6.2 分支合并和删除
当新功能开发完,合并到主分支的方式,一般有两种路径。一种是在GitLab上发起Merge Request,走review流程,这个纯网页操作,这里不多说。另一种是本地直接合并:
git checkout master git pull origin master git merge feature/login git push origin mastergit merge会把分支上的提交记录合并进当前分支。如果合并过程中没有冲突,Git会生成一个merge commit;如果有冲突,处理方式跟前面pull冲突一样。
合并完之后,可以清理掉已经没用的小分支:
git branch -d feature/login注意,-d只能删除已经合并到当前分支的分支,如果你要强制删除一个没合并的分支,Git会报错并提醒你,这时如果确需强制删除,需要改成-D。我建议只在你明确知道这个分支彻底不需要了才用-D,否则误删代码会很难找回。
6.3 本地分支和远端分支的追踪关系管理
有时候执行git branch -a,你可能会看到本地某个分支和远端分支的追踪关系断了,表现就是git status提示“Your branch is based on 'origin/xxx', but the upstream is gone”。这个提示的意思是,你追踪的远端分支已经被删了,但本地还在。
处理方式很简单,要么删掉本地分支,要么重新指定追踪关系:
git branch -u origin/新的远端分支名-u会重置当前分支的上游分支,后续代码同步都能正确指向新的远端分支。这个细节平常不起眼,但一旦远端分支被删除,不处理的话push和pull都会产生混乱的报错。
7. 从零到一:一个真实的上传流程演示
前面讲的是碎片化命令,最后我给你走一遍完整流程,从克隆仓库到新分支上传代码,这一套流程如果你能跟着跑通,日常开发基本就没什么能拦住你的了。
假设场景:公司GitLab上有个项目叫demo-project,你现在要开发一个“用户注册”功能。
第一步,克隆代码到本地:
git clone git@gitlab.com:yourname/demo-project.git cd demo-project第二步,确认当前分支并同步到最新:
git branch --show-current git pull origin master如果当前分支不是master,先用git checkout master切过去再pull。
第三步,创建新分支并切换:
git checkout -b feature/user-register第四步,编写代码。代码写的过程中,随时可以看状态:
git status git diff第五步,添加所有新增和修改的文件:
git add src/views/Register.vue src/api/user.js精确指定文件比git add .更稳,避免手滑把临时文件也带上。
第六步,提交并写清楚信息:
git commit -m "feat: 新增用户注册页面和接口"第七步,推送新分支到GitLab:
git push -u origin feature/user-register第八步,登录GitLab网页端,在分支列表里找到feature/user-register,点击“Create merge request”,填好描述和reviewer,提交合并请求。
整个流程跑下来,你会明显感觉到,Git的核心动作其实就那几个:查看状态、添加暂存、提交、拉取推送、合并。把这些动作练到肌肉记忆,遇到报错时再对照前面第5节的内容排查,基本就能顺畅应付绝大多数场景了。
这几年用下来,我的体会是:版本控制工具本身不难,难的是流程意识。提前想清楚自己在哪个分支、从哪个分支切出来、往哪儿推,比背100条git命令都管用。最后分享一个小技巧:你可以在GitLab的仓库页面设置默认分支为受保护状态,这样master或main分支不能直接被push,所有改动必须走merge request流程,强制让每个改动都被review一遍,对代码质量和团队协作的规范性帮助极大。