1. 为什么代码要“拉”和“提”:先搞清楚Git在协作里的角色
1.1 从一次文件复制粘贴的惨剧说起
先说个我当年带新人时遇到的真实场景。某同事拿到一个Coding仓库地址后,第一反应是问我:“源码能直接打包发我一份吗?我改完再发回去。”我愣了两秒,告诉他这条路看着简单,实际走两次就会出大问题:你改了A文件,另一个人改了B文件,隔一天各发一版,谁先覆盖谁?谁加的代码丢了?根本说不清。后来他在项目里同时维护三个带日期后缀的压缩包,不到两周就彻底乱了。
Git这套东西之所以值得学,核心就一句话:它让多人修改同一份代码变得有序、可追溯、能回滚。Coding作为代码托管平台,相当于把大家的代码集中存放,充当“公共版本库”。你从它那里拉代码到本地,改完再把改动提交回去,整个过程全程留痕。这种协作方式,配合分支、合并、冲突处理,才是现代软件开发的常态。
1.2 Git、Coding和本地仓库,到底谁是谁
很多人一开始会把Git和代码托管平台混为一谈,这其实是被“网页上传下载代码”的思维带偏了。
打个比方:Git是版本管理工具,它是安装在你自己电脑上的一个程序,管的是本地文件夹里的代码以及每一次修改记录;Coding是国内常用的代码托管平台,相当于一个公共的远程仓库,帮团队保存一份最新代码和完整历史;你的电脑上还有一个本地仓库,这是Git初始化之后在你项目文件夹里生成的隐藏目录,里面存着你的提交历史和当前分支状态。
整个协作链条是这样的:本地仓库是唯一的“工作现场”,代码托管平台是“公共存档点”,你日常做的事情就是在这两者之间同步。有一句口诀很好记:“拉下来改,改完提交,提交完推送”。理解了这句话,后面的命令其实都是在做同一件事的不同环节。
1.3 这套工具链适合谁、能解决什么
这篇内容适合三种人:刚进公司需要用Git协作的实习生、以前只用网盘或压缩包传代码的开发者、以及自己写项目但想建立版本管理习惯的爱好者。它的价值不在于让你记住一堆命令,而在于让你形成“每次改动有记录、每个版本可还原、每次协作有边界”的工作方式。
我特别想强调一个观念:Git不是给“大神”用的,它恰恰是给“会犯错”的人用的。正因为人都会改错、删错、合并错,才需要版本控制来兜底。你不需要理解底层数据结构,先把拉取、提交、推送这三个高频动作练熟,就已经能覆盖工作中八成以上的场景了。
2. 开工前的准备:账号、仓库与连接方式二选一
2.1 仓库准备与本地Git安装
在Coding上拉取和提交代码,前提是得有个仓库。两种常见情况:一是团队管理员已经把仓库建好,把你的账号加入了成员列表,给你一个仓库地址;二是你自己新建项目,在项目里点“代码仓库”,初始化一个空仓库。对新手来说,建议先用一个测试仓库来练习,别直接在生产项目上试错。
本地需要安装Git客户端。不同系统安装方式不一样,Windows推荐直接去官网下载安装程序,一路默认选项;macOS更简单,装了Homebrew的话一行命令搞定;Linux发行版一般用自带包管理器就能装。装完之后打开终端,输入git --version,能够输出版本号就说明装好了。
这里有个很多人忽略的点:Coding网页上虽然也能直接编辑文件、上传压缩包,但我强烈不建议新手在网页上直接改代码提交。网页编辑器没有语法提示、没有本地调试、没有暂存区概念,改错一个字符就只能靠肉眼排查,非常痛苦。正确姿势永远是本地clone下来,用你顺手的IDE改完再推上去。
2.2 首次配置:用户名和邮箱为什么必须认真填
装好Git之后,第一件事不是去clone代码,而是先做全局身份配置。两条命令:
git config --global user.name "你的名字" git config --global user.email "你的邮箱"这两行配置会跟着你的每一次提交写进Git记录里。也就是说,你提交的每一笔commit,都会标记上这个名字和邮箱,队友在看提交历史时看到的也是这个名字,它是你在团队协作里的“签名”。
我见过不少新人随便填了个昵称或者邮箱,结果提交记录里一片乱码,后续找责任人、追溯问题时特别尴尬。更麻烦的是,如果你以后想把记录关联到Coding账号上,邮箱不匹配会导致头像、动态都对不上。所以这一步别省,也别瞎填,填你在Coding上注册用的真实姓名和邮箱最稳妥。
验证配置有没有生效,可以输入git config --global --list,会列出当前所有全局配置项。这里顺便提一个进阶习惯:如果同一个电脑上要处理多个平台的仓库,建议用--global只配一套通用的,遇到特殊情况再针对单个仓库用git config --local覆盖,这样更灵活。
2.3 SSH Key vs HTTPS:怎么选、怎么配
连接Coding远程仓库常用的两种方式:HTTPS和SSH。我在带新人时经常被问选哪个,我的建议是:能用SSH尽量用SSH,配一次之后永久免密,比每次输账号密码舒服太多。
先说HTTPS方式。它最简单,clone地址就是一条以https://开头的URL,clone时输入Coding的用户名和密码就能拉。缺点也很明显:每次push都要重新认证,虽然是Coding有访问令牌机制不用输密码,但要频繁地管理令牌,体验一般。如果只是临时拉一次公开仓库代码,用HTTPS完全没问题。
SSH方式需要先自己生成一对密钥。在终端执行:
ssh-keygen -t rsa -b 4096 -C "你的邮箱"一路回车就行,生成的公钥在~/.ssh/id_rsa.pub文件里。然后把公钥内容整个复制,到Coding个人设置里找到“SSH公钥”或“部署公钥”入口,粘贴保存。保存后测试连接:
ssh -T git@e.coding.net如果输出欢迎信息并提示认证成功,说明SSH配置完成。从此再clone项目时选SSH地址,拉取和推送都不需要再输密码了。配置过程中有个细节:公钥是“谁访问仓库的凭证”,所以不要把它公开乱发;如果你换了电脑,记得在新机器上重新生成一把新密钥并在平台补上,旧机器的那把可以删掉。
3. 把代码拿到本地:clone和pull到底有什么区别
3.1 第一次拿代码:git clone
拿到仓库地址之后,第一次把代码拉到自己电脑上,用的是git clone。这个命令会把远端仓库的完整代码、分支信息和提交历史一次性复制到本地。
git clone git@e.coding.net:你的用户名/模拟项目X.git执行完之后,当前目录下会多出一个和仓库同名的文件夹,注意不是把代码直接散在当前目录,而是装进一个新文件夹。如果你想让代码放到指定目录,可以在仓库地址后面加一个目录名参数。
clone和“下载压缩包”最大的区别在于:压缩包只是代码快照,里面没有历史记录;clone得到的是完整仓库,你在本地能看到每一次提交、每一个分支、每一处改动。这也意味着clone之后,你可以在本地随时查看曾经某次提交的代码,甚至切换历史版本,完全不依赖网络。
我第一次带新人时,对方clone完就问我:“怎么代码和一个网站文件夹长得一样?是不是下错了?”其实这说明clone成功了,.git这个隐藏文件夹就是版本库核心,正常情况下你不需要进入它,Git命令会替你操作。
3.2 日常同步:git pull的工作原理
clone解决的是“第一次拿代码”,但团队协作中代码是每天都在变的。队友好几个小时前推送了新代码,你本地还是老版本,这时候再直接改代码会出大问题,必须先同步。
同步的命令是git pull,它本质上是两个动作的合并:git fetch加git merge。fetch是从远端拉取最新的提交记录和代码变化,merge是把这些变化合并进你当前的本地分支。理解这个拆解很重要,因为有时候你会看到有些人说“我pull失败了”,其实失败点往往就出在merge这一步。
日常早起干活前先pull一次,这是个好习惯。操作很简单:
git pull origin master意思是:把origin这个远端仓库的master分支新内容拉到本地,合并到当前所在分支。如果当前分支就是本地master,后面两个参数可以省略,直接git pull也行。
3.3 拉取失败的常见原因与排查办法
拉取失败大概是新手遇到最多的报错之一,我见过的情况基本集中在三类。
第一类是网络问题。报错信息里出现超时、连接被拒绝之类,先检查网络,确认能正常访问外网,再排查Coding服务状态。这类问题一般过一阵子自己就好了。
第二类是身份认证失败。HTTPS方式会提示输入密码错误,SSH方式会提示权限不足。排查思路很简单:确认你登录Coding用的是哪个账号,SSH公钥是否正确绑定到该账号,或者改用HTTPS地址试试,验证是不是SSH密钥本身的问题。
第三类是最常见的“本地有未提交改动”导致的合并冲突。比如你本地改了某个文件但没提交,远端也有人对同一个文件的同一行做了修改,pull时就会提示冲突,中止合并。这时候Git会告诉你具体是哪几个文件冲突了,需要你先处理冲突,或者先撤销本地改动再pull。
如果你不确定当前状态,任何时候都可以用git status查看本地仓库的状态,它会清楚列出未提交的修改、未跟踪的文件以及当前所在分支。这个命令没有副作用,可以随时放心执行。
4. 把修改交上去:add、commit、push三步走
4.1 工作区、暂存区、版本库:一份代码的三个状态
很多人第一次接触提交代码时,会被git add、git commit、git push三个命令绕晕。其实就是一份代码在不同位置之间的搬运:工作区是你实际编辑文件的文件夹;暂存区是临时寄存你想提交的改动的地方;版本库是Git记录每一次提交的地方。
我常用一个生活类比来解释:你在厨房做菜(工作区),把需要的食材洗好切好放进备菜盘(暂存区),最后开火炒完装盘(提交到版本库),端上桌给客人(推送远端)。如果你不把食材放进备菜盘,锅就拿不到它;如果你放进备菜盘但没炒完,客人也吃不到。这就是add和commit的关系。
为什么要分开?因为好处很大:你可以只提交一部分文件留一部分不提交,同一次提交内包含多个文件的修改也能逐个标记。比如你同时改了功能代码和测试代码,可以把它们分成两个commit分别提交,以后回溯“哪次提交引入了功能、哪次补了测试”就一目了然。
查看这些状态,用git status就够了,它会把工作区和暂存区的情况列得很清楚。理解了三个状态,你再看所有Git提交命令,都会有“原来在搬运东西”的感觉。
4.2 从改完代码到推送成功的标准操作
假设你现在已经把代码改好了,接下来走完整流程。
第一步,查看改动:
git status会列出一堆文件,前面标着“modified”表示修改过,“Untracked”表示这个文件是新建的、Git之前没见过它。
第二步,把想提交的文件加入暂存区:
git add 文件名如果想一次添加所有改动,用git add .,但建议新手先养成“显式添加”的习惯,确认自己到底要提交哪些文件,避免把调试用的临时文件也提交上去。
第三步,提交并写清楚提交说明:
git commit -m "修复了登录页面的按钮样式"提交说明是对这次改动的简短描述。我见过有人写“update”、写“修改”,甚至写“asdfgh”,这种说明一旦多了,回看历史时基本是灾难。好的提交说明应该让人不看代码也能明白这次做了什么,比如“修复订单列表在空数据时的报错”就比“fix bug”好得多。
第四步,推送到远端:
git push origin master执行完后,你的改动就到了Coding的对应分支上。自此,这份改动才算真正进入了团队的公共代码池,别人pull之后才能看到。
整套流程总结起来就是:看状态、加暂存、写说明、推远端。这四步反复练,肌肉记忆形成了,你就已经掌握了Git使用频率最高的部分。
4.3 分支:为什么推荐在feature分支上干活
直接往master分支上提交代码,对单机练习没问题,但正式项目里这么干风险很大。因为master通常代表“稳定可发布的代码”,所有人都应该以它为基准。你改到一半的代码直接推到master,万一有语法错误,拉下来的人全都会受影响。
推荐的模式是:从master拉出一个功能分支,在功能分支上开发、提交、推送,功能验证无误后再合并回master。操作起来也不麻烦,创建并切换到新分支:
git checkout -b feature/login-fix这条命令的意思是创建名为feature/login-fix的新分支并切过去,下面的add、commit、push流程照旧,只是push时把目标分支名改一下即可。后边功能完成,再到Coding网页上发起合并请求,由审核人确认后合入master。
一开始你可以不理解分支底层的实现原理,但要从“不要在车间主路上施工,去旁边的辅路上干活”这个角度理解它的价值。等你自己不小心把错误代码推到master、害得队友全部报错过一次之后,你就会彻底认同这个习惯。
5. 冲突来了怎么办:一次完整的处理过程
5.1 冲突到底是怎么发生的
先明确一件事:冲突不是Bug,它是Git的正常保护机制,说明“你和队友在同一个地方做了不一样的修改,Git不知道该听谁的”。
具体场景是这样的。你和队友都在master的基础上拉出了自己的分支。你改了文件A的第三行,队友也改了文件A的第三行。你想把队友的分支合并进自己的,或者你想把自己的分支合回master,Git会对比两个版本,发现第三行两边都被改了且改动不一样,它无法自动决定选谁,就会停下来报冲突。
打个比方就是:两个人同时在一张白纸的同一位置写下了不同的字,后来要把两页纸叠起来,你根本没法判断该保留哪个。但如果是你写了第一行、他写了第二行,那完全没问题,Git能自动合并。
5.2 解决冲突的实操步骤
遇到冲突不要慌,按下面的路子走,基本都能处理干净。
第一步,先看结果。执行git status,Git会列出所有冲突文件,处在“both modified”状态。
第二步,打开冲突文件看内容。Git会在冲突区域插入特殊标记:
<<<<<<< HEAD 你当前分支的代码 ======= 要合并过来的分支的代码 >>>>>>> feature/login-fix<<<<<<< HEAD到=======之间是当前分支的内容,=======到>>>>>>>之间是对方分支的内容。你要做的判断是:保留谁、删掉谁、或者把两边合并成一段新代码。改完之后记得把这些标记行全部删掉,只留下最终的代码。
第三步,把标记为已解决。对每个冲突文件,重新执行git add 文件名,告诉Git“这个冲突我处理好了”。
第四步,commit收尾或者继续合并。如果是在执行merge过程中出现的冲突,处理完冲突后执行git commit直接完成合并提交;如果你正处在rebase流程,处理完冲突后执行git rebase --continue让它继续。
整个处理过程中,最危险的操作是“手动乱删”,直接把别人的代码整段清掉。碰到不确定的地方,最好拉上相关的同事当面确认一下,或者至少把别人的实现看一眼再决定。代码这东西,删了要是后悔,找回的成本远高于停两分钟先问清楚的成本。
5.3 养成三个习惯,让冲突少发生
冲突虽然能解决,但能少是最好。我自己实践下来比较管用的是下面这三个习惯。
第一个:写代码之前先pull。一天工作开始、或者准备动手改一个文件之前,先git pull把自己分支同步到最新,最大程度减少“基于过期代码修改”的情况。
第二个:改动尽量集中,不要同时碰多个人的代码上下文。如果你只是修登录按钮,就别顺手把订单列表也重构了。改动文件越少,和队友撞车的概率越低。
第三个:单个分支的存活周期别太长。一个功能分支拖了两周都没合回去,期间主干被别人改了无数次,合回来时冲突一堆,处理成本极高。尽量把功能拆小,做完一个就早点合并,让冲突暴露在“改动还很少”的时候。
6. 完整工作流演示与新手避坑清单
6.1 一次标准的日常操作全流程
把前面所有内容串起来,给你一个可以直接照着操作的完整流程。
早晨到工位,先打开终端进入项目目录,执行git pull把最新代码同步下来。然后创建一个今天要干活的新分支:git checkout -b feature/today-task。在IDE里改代码,改完后用git status确认改动了哪些文件,用git add 文件名逐个添加,用git commit -m "完成xxx功能"提交,最后git push origin feature/today-task推到远端。
到这一步,你的代码已经在Coding的feature分支上了。如果想要同事帮忙看代码,去平台页面发起合并请求,选择“合并到master”,填好说明提交审核。审核通过后,你也可以顺手把本地feature分支切回master并拉一次,保持本地状态和远端一致。
这一套流程下来,你涉及到的命令其实不超过十个:checkout、pull、status、add、commit、push、merge,以及偶尔用到的fetch、rebase、log。能熟练使用这些,日常开发完全够用。
6.2 新手最容易踩的坑
这里列几个我在带人过程中反复见到的坑,每个都能省下一个下午的排查时间。
第一个坑是提交信息乱写。有人用中文、有人用英文、有人写“1”、有人写“asdf”,回看历史就是一本烂账。我的建议很简单:团队内部约定一套提交信息格式,比如类型(范围): 描述,像fix(login): 修复点击登录无响应,这样扫一眼就知道改了哪、改了啥。没有约定的话,至少写“做了什么”,别写“改了一下”。
第二个坑是把不必要的文件提交上去。比如IDE配置目录、依赖缓存目录、编译产物,这些文件不该入库。解决方法是维护一个.gitignore文件,把不需要追踪的目录和文件写进去,Git就会自动忽略它们。用git status看到一堆Untracked目录时,先停下来想想是不是该加进.gitignore,而不是直接git add .全提交了。
第三个坑是在大文件上栽跟头。有些项目会不小心把几百MB的压缩包、模型文件或者日志塞进仓库,导致每次拉取和推送都走得极慢。Coding对仓库体积通常会有限制,一旦超了,后面的push会各种报错。这个坑极难清理,因为Git会把历史记录也保存下来,即使你现在删掉文件,历史的那个大对象还在。所以要养成习惯:入库前先确认文件类型和大小,别把“生产过程产物”当成代码提交。
第四个坑是push之前不pull。这就好比你在一份旧草稿上写新内容,写完直接交上去,很容易和别人刚交上来的内容冲突。顺序永远应该是:pull最新的,再改,再提交,再推送。偶尔你会被人提醒“远端有更新,需要先pull”,遇到这个提示就乖乖先pull。
6.3 个人体会
用了几年Git,我现在最大的感受是:它不是在束缚你,而是在帮你兜底。提交记录让我随时能找回想不开的某一天删掉的代码,分支让我敢大胆尝试,冲突处理让我更谨慎地对待别人的工作成果。这些能力不是背命令背出来的,而是每天在真实项目里反复拉取、提交、处理冲突练出来的。
所以,如果你现在还是“只会下载压缩包”的选手,别怕,花一个下午把这篇里的命令敲一遍,克隆一个测试仓库,改几个文件提交上去,再故意制造一次冲突解决一下,基本就入门了。剩下的,就像骑车一样,上了路自然会越来越顺。