1. 为什么几乎所有团队都让新人在第一天先配好Git身份
我以前带过不少新人,第一天到岗几乎都绕不开同一件事:配Git。你以为这就是敲两行命令的功夫,但实际问一圈就会发现,能卡住的点特别多——用户名是全拼还是中文、邮箱到底用哪个、为什么明明配了却提交不了、老板让拉代码又不给权限怎么办。这篇文章就把整套操作串起来讲一遍,从零开始,按照真实团队里一天的工作流走:配好身份、配上密钥、拉下代码、改个分支、提交上去。覆盖面不深,但足够让一个完全没碰过Git的同事安全地度过第一个工作日。
为什么配置身份要放在所有操作的最前面?因为Git和很多软件不一样,它并不靠登录来控制你做了什么,而是每个提交都带着一个"签名"。这个签名不是登录态,是写死在每次commit记录里的作者信息。你的用户名和邮箱一旦写错或没写,轻则提交记录显示一堆Unknown,重则和别人的提交混在一起,等到了复盘和追溯问题的时候就非常麻烦。另一个原因是,Git把事情拆成了"本地操作"和"远程交互"两段,配置身份属于本地操作的根基,密钥则负责远程交互的通道,两者是两码事,别混为一谈。
这篇文字面向的是两类人:一类是完全零基础、第一次要碰公司代码仓库的新人;另一类是已经会基本操作、但经常卡在权限配置和分支混乱上的同学。我会按照实际操作顺序来写,尽量少讲理论废话,每一步都解释清楚为什么这么做。
1.1 全局配置和局部配置怎么选
配置用户名的命令长这样:
git config --global user.name "你的名字" git config --global user.email "你的邮箱"这里的--global表示写入当前用户主目录下的~/.gitconfig,这台机器上的所有仓库都生效。还有一种写法是把--global换成--local,那只会作用于当前仓库目录下的.git/config,精度更高,适用于那种"这台电脑上同时维护着个人项目和公司项目、需要不同身份"的情况。
我的建议是:新人的第一台工作电脑直接用--global,先保证所有仓库提交出去都带着正确身份,省心。等之后真的出现"不同仓库不同身份"的需求,再把个别仓库切到--local。但要注意顺序,--local会覆盖--global,也就是说如果你先配了全局身份,再在某一个仓库里配了局部身份,那么在这个仓库里提交时用的是局部身份。反过来,局部没配,默认取全局。
检查是否配置成功,不要靠猜,直接输入:
git config user.name git config user.email如果两条命令都正确输出了你刚填的内容,说明配置已经生效。这个检查动作看着傻,但真能拦住一半的"我明明配了呀"问题——很多时候新人打开一个全新的终端窗口,shell环境没刷新,配了等于白配。
1.2 常用配置的自查清单
配置完身份之后,顺带可以确认一下其他几个常用项。比如默认分支名、行长提示、是否自动转换换行符等。以下是我建议新人配好的三项:
git config --global init.defaultBranch main git config --global core.autocrlf input git config --global pull.rebase falseinit.defaultBranch指定了git init后默认创建的初始分支名,避免每次都以一个不太合适的名称开头。core.autocrlf input在macOS和Linux上非常关键,它让提交到仓库时统一换行符,减少那种"我什么都没改但整文件diff全是红色"的惨案。pull.rebase false保持默认的merge行为,避免新手在处理pull下来的提交时完全不理解rebase在干什么。
配置本身没有固定标准,团队有规定就按团队规定来。关键是配置完之后自己心里有数:我在这台机器上以什么身份提交、默认分支叫什么、pull远程代码时用哪种合并方式。这几点清楚了,后面所有操作才不容易出乱子。
2. SSH密钥和HTTPS两种拉代码方式的区别与密钥配置全流程
下载代码有两种主流方式:HTTPS和SSH。新人在这一步最常见的困惑就是:明明我电脑密码是对的,为什么clone的时候一直弹窗输密码,输完又说权限不对?或者干脆卡在认证失败动不了。这个问题的根源在于,两种方式走的是完全不同的认证链路。
HTTPS方式在每次push/pull时都需要输入账号密码。现在很多托管平台改成了token认证,密码输入框填的其实是你生成的个人访问令牌。SSH方式则不同,它在本地生成一对密钥(公钥和私钥),公钥填到远端平台,私钥留在本地,之后的传输都不需要再输密码。
绝大多数团队都推荐用SSH方式,因为一次配置长期有效,而且不会因为token过期导致突然推不上代码。下面把SSH密钥配置的全流程完整过一遍。
2.1 生成密钥对之前的检查工作
不要一上来就ssh-keygen。先看看这台机器上是否已经存在密钥。很多人家里和公司共用一台电脑,或者重装过系统但主目录没清理干净,可能已经有一把旧密钥了。直接覆盖的话,会导致之前配过这把公钥的所有平台全部失效。
ls ~/.ssh如果看到id_ed25519和id_ed25519.pub这类文件,说明已经有现成的密钥对。这时候你只需要把公钥内容(.pub文件)复制到目标平台,不需要重新生成。如果确实没有任何密钥文件,或者旧密钥你确定已经没用了,再进入下一步。
我个人比较推荐用ed25519算法而不是传统RSA。原因是它生成的密钥更短、生成速度更快、安全性不弱于RSA,而且新版的Git和主流托管平台全都支持。如果团队里有非常老的服务器或老平台兼容性要求,才需要退回去用RSA。
2.2 生成密钥并正确安装
ssh-keygen -t ed25519 -C "你的邮箱"一路回车,不要设置passphrase(除非你有特殊安全要求)。不设置的原因是:一旦你设置了短语,每次SSH连接都要输入短语,新人很容易把这里的输入和git账号密码搞混,然后卡在原地。
生成完之后,执行:
cat ~/.ssh/id_ed25519.pub复制输出的整行内容,格式一般是ssh-ed25519一串字符 邮箱。然后打开你代码托管平台的设置页面,找到SSH Keys或公钥管理,粘贴并保存。
这里有一个细节:不同平台对公钥名称有不同要求,有的要求必须以ssh-ed25519开头,有的是任意名称都可以。我习惯直接复制整行,不删任何部分,包括末尾的邮箱注释。因为邮箱注释纯粹是给人看的标识,不会影响认证,反而能帮助你在管理多个密钥时认出哪把是哪个机器的。
2.3 验证SSH连接和常见失败原因
ssh -T git@你的托管平台域名这里假设托管平台使用标准端口22。如果网络环境特殊,某些局域网策略会封锁22端口,那就要改用HTTPS端口走SSH协议的方式。在这类情况下,可以在~/.ssh/config里添加端口配置,或者直接用备用地址,具体以托管平台官方文档为准。验证成功的标志是会看到一段欢迎信息,然后进程自动退出。有些平台返回的欢迎信息里有你的用户名,那说明公钥已经正确识别了。
如果验证失败,按优先级排查以下三点:
- 公钥是否复制完整,有没有多出换行或截断。
- 粘贴到平台时是否保存成功。
- 本地SSH agent是否加载了正确的密钥,执行
ssh-add -l看一下密钥列表。
把密钥这张牌打稳之后,clone代码就非常简单了:
git clone git@仓库地址.git选哪种协议这件事,本质上是"一次性粘贴公钥换之后永久免密"和"每次都输入认证信息"的取舍。SSH除了第一次配置稍微麻烦,后续体验真的顺滑很多,新人不应该在这里省时间。
3. 一次完整提交背后的四步链路:status、diff、add、commit、push
很多教程直接把git add和git commit连着念,好像这两个操作是固定的组合拳。但如果你真的只在一天里机械地执行"add然后commit",就会错过Git最核心的价值:对变更的精确控制。提交代码之前,我想先用一个小例子解释一下Git的数据流动路径。
工作区就是你当前看到的文件目录,暂存区是介于工作区和仓库之间的一个中间层,仓库则是保存所有历史提交的地方。你的每一次提交,其实是把暂存区的内容固定成一个快照,然后写进仓库历史里。为什么要多一个暂存区?因为有时候你一个上午改了三个文件,但只希望其中两个进入当前提交,另一个还没改完不想掺和进来。这时候暂存区就相当于一个筛选器。
3.1 从一张干净的status开始
写代码之前先看一眼仓库状态,这是一个值得养成的好习惯。
git status干净的状态会显示"working tree clean"或者是中文提示"工作区干净"。实际操作里,我建议在每次提交之前、提交之后、切换分支之前各看一次status,这三个节点是最容易出状态的。
改完代码之后,status会显示哪些文件被修改了。此刻不急着add,先看一眼改动内容到底是不是你预期的。很多新人会在这个阶段把调试用的临时打印语句一并提交进去,等到有人review代码的时候才尴尬。
git diff直接看工作区和暂存区之间,也就是尚未暂存的改动。如果某个文件的改动是你想要的,就把它暂存起来:
git add 文件名只要暂存一个文件,效率和安全性都远高于git add .。git add .虽然快,但会把当前目录下的所有改动一次性纳入,新手很难判断到底纳入了什么。我自己的习惯是:明确要提交哪些文件,就一个个add,不用add .,除非改动非常小而明确。
3.2 提交信息和常用提交命令写法
暂存完毕之后,提交:
git commit -m "本次提交的说明"提交信息怎么写,是区分"会用Git"和"用好Git"的分水岭。别写"update"、"fix bug"、"修改"这种等于没写的信息。好的提交信息应该能让你在三个月后看到这条记录时,立刻想起来这次改了什么、为什么改。例如:
git commit -m "修复登录接口在Token过期时返回状态码错误的问题"这是最简单的单行写法。如果改动内容比较复杂,可以用多行提交信息:
git commit -m "标题行:简洁说明这次提交做什么" -m "具体细节:为什么这样做,涉及哪些文件,需要注意什么"提交完成后再次查看status,如果显示工作区干净,说明本地提交已经形成。这时候你的修改还只在本机,远端仓库完全不知道。接下来:
git push第一次推送新分支时,Git会提示你设置上游分支,按提示使用自动推荐的命令即可。推送完成后,再去托管平台页面上刷新,就能看到你的提交记录。
3.3 为什么push之前必须再看一遍diff
在我见过的最常见工作流事故里,第一名是"把密钥文件提交进了仓库",第二名是"把本不该改的文件顺手提交了进去"。这两种事故的最佳防线都是git diff和git status。尤其是git diff --cached,它查看的是暂存区即将被提交的内容,几乎是commit之前的最后一道关卡。
git diff --cached这道命令会把已经暂存但还没提交的改动完整列出来。如果你在提交之前习惯性执行这步,至少能拦住90%的"坏提交"。
有一次一个A同学改了三天代码,全公司都在等他的提交。结果他自己也没看,把本地调试时写的硬编码密码和数据库地址一起推到了公共分支。还好发现得早,但代码已经被其他人拉走了,最后只能全体改密码,git历史里还得做一次大清洗。如果他当时commit前多看一眼git diff --cached,这场事故完全能避免。
4. 分支操作的核心逻辑:从创建、切换到合并的完整过程
分支是Git里最让新人发懵的概念之一。原因在于,大部分人还在用"文件夹复制粘贴"的思维理解版本控制,觉得分支就是把一份代码复制成好几份。其实分支在Git里的本质只是一个指向某个提交的轻量指针,创建和切换分支的耗时几乎都是文件系统刷新,而不是代码拷贝。
我用一个类比来解释分支:假如你正在写一篇长文档,主干版本是所有人都能看的那一版。你想实验新的章节结构,但暂时不想影响主干,于是复制了一份草稿在旁边改。改到满意之后,再把新章节内容合并回主干。Git分支就对应这份草稿,只不过Git对草稿的管理精细得多,每一处改动都能追溯、能对比、能回滚。
4.1 查看与创建分支
git branch不带参数执行,会列出本地所有分支,当前所在分支前面有*标志。想查看所有本地和远程分支,用:
git branch -a创建新分支用git branch 分支名,但实际开发中用得更多的是一条组合命令:创建并切换。
git checkout -b feature/新功能名这条命令的意思是创建一个新分支并马上切过去。新分支会从当前所处的提交点开始,包含当前分支已有的全部内容。这就是分支创建的起点。如果你是更喜欢新命令写法的人,也可以用git switch -c,效果完全一样,checkout和switch在切换分支场景下等价,只是switch语义更清晰,只负责切换,不掺和其他功能。
4.2 切换分支时最容易踩的坑:未提交的改动
切换分支最常遇到的坑是:当前分支有未提交的改动,你切走了,改动也跟着"走"了,或者干脆被Git拦下来不允许切换。这其实是Git的自我保护机制。Git允许你带着未提交的改动切换分支,但如果目标分支和当前分支在这些文件上不一样,Git就无法同时兼顾两边,只能报错拒绝切换。
这个问题的解法特别简单,但是新人在慌乱中往往会选错:切分支之前,要么把改动commit掉,要么用git stash临时存起来。
git stashstash会把未提交的改动保存到一个独立的工作目录缓存中,然后工作区恢复正常。等你切回这个分支之后:
git stash pop就能把改动恢复回来。这套流程我最推荐给新人,因为"先提交再切分支"虽然最干净,但如果你正在代码写到一半、逻辑还没完整的情况下创建了一个临时提交,后续整理历史时还得处理一下,不如直接stash干净。
4.3 合并分支和简单冲突处理
功能开发完成,要把分支合回到主干。首先切回目的分支:
git checkout main然后执行合并:
git merge 功能分支名如果两边的改动没有交集,Git会自动完成合并,提交历史直接前进。但一旦两个人改过同一个文件的同一块区域,就会出现内容冲突。Git会在冲突文件里插入标记:
<<<<<<< HEAD 这是当前分支的内容 ======= 这是被合并分支的内容 >>>>>>> 功能分支名解决冲突没有银弹,本质就是打开文件,逐段确认保留哪一边、删除哪一边。解决完保存,然后:
git add 冲突文件 git commit注意,这里的git commit不需要再写消息,Git会提供一个默认的合并提交消息。这整个流程走通一次之后,你对Git的理解会明显上一个台阶,因为分支合并才是Git真正区别于SVN这类集中式工具的分水岭。
另外提醒一句:不要在功能分支上直接改与功能无关的代码,更不要在一个分支上同时塞两个完全独立的功能。每条分支保持单一职责,合并时的冲突和review成本才会降到最低。
5. 高频报错场景与对应的排查思路
最后整理几个我在带新人过程中反复遇到的高频报错。这些报错本身并不可怕,可怕的是新人面对报错时习惯性复制粘贴搜索,然后乱敲命令,越弄越糟。掌握了下面的排查思路,大多数问题都能在几分钟内自己解决。
5.1 提交身份不生效或显示错误身份
症状:提交代码后,在远端平台网页上看到提交人显示为自己的机器登录用户名,或者干脆显示Unknown,而不是你刚才配置的名字。
原因:配置的user.name和user.email没有作用到当前仓库。可能情况有三种:一是配置用了--local但当前仓库不是你以为的那个仓库;二是全局配置写错之后没验证就直接commit了;三是某些环境变量或别名覆盖了Git配置。
排查步骤:
git config --list这个命令会列出所有生效的配置项,并且能告诉你每一项来自哪个文件。如果发现user.name的值和你配置的不一致,再查看是不是存在仓库级别的覆盖:
git config --local --list最后以实际生效的值作为标准重新配置即可。配置完成之后,不需要重新提交历史的旧commit,但新提交都会正确。
5.2 密钥配置后仍提示Permission denied
这个问题值得单独拿出来讲。很多人的排查过程是:生成密钥、粘贴公钥、测试SSH连接,连接是通的,push也没问题,但换了一台机器或者过了一段时间之后突然权限失败。这种"曾经好了又不行"的情况,绝大多数出在本地终端环境变化上。
首先是重启后的SSH agent问题。某些操作系统不会自动把私钥加入agent,需要手动执行:
eval "$(ssh-agent -s)" ssh-add ~/.ssh/id_ed25519然后再用ssh -T git@托管平台域名测试连接。如果这条测试命令能通过,但git命令仍然权限不足,那请检查Git走的是不是SSH协议——少部分人会同时配置了全局credential helper和SSH key,而Git在某些托管平台上默认走了HTTPS。这时候查远端地址:
git remote -v如果显示的是https://开头,而你机器上以HTTPS方式认证已经失效,那就应该把远端地址改成SSH格式。修改方式:
git remote set-url origin git@托管平台域名:组织名/仓库名.git改完后再push,就会走SSH通道。
5.3 误提交到错误分支或需要撤销最近一次提交
这类场景几乎每个人都至少遇到一次。典型情况是:本来该在功能分支上做的修改,结果忘了切换分支,直接在main上改完并commit了。处理思路取决于你希望达到的结果:
- 如果只是想撤销最近一次的commit,但保留工作区修改,用软重置:
git reset --soft HEAD~1这个命令会把提交撤销,但保留暂存区和文件修改内容,你可以立刻切换分支再提交。
- 如果是彻底放弃最近一次提交的所有修改,用:
git reset --hard HEAD~1但--hard非常危险,它会直接抛弃所有未提交和已撤销的改动。使用之前务必确认你真的不需要这些内容。如果不确定,建议先用git stash或复制备份整个项目目录,再做硬重置。
需要特别强调的是,无论是哪种reset,都不要在你已经推送到了公共分享分支且别人可能拉取过的情况下使用。公共分支的历史改写会波及所有拉取过该分支的人,形成一团糟。在这种情形下,优先办法是git revert,它会生成一次新的提交来抵消错误的提交内容,不动历史。
5.4 拉代码时遇到merge冲突或失去改动
5.4 拉代码时遇到merge冲突
新人操作里最紧张的时刻就是git pull之后看到一屏冲突标记。其实git pull的本质是"获取远程改动再合并到本地",它和git merge用的是同一套冲突处理逻辑。你已经在4.3里学会了本地冲突的解法,全程照搬就行:打开冲突文件、逐段保留正确内容、add、commit。
但有一点值得单独提醒:在执行pull之前,先看一眼自己有没有未提交的修改。如果本地有未提交的改动,同时远程也有相同文件的修改,pull会混合出非常难处理的局面。稳妥的习惯是:
git status # 如果有未提交改动,先 stash 或 commit git pull远程没有冲突就一切正常,有冲突就按4.3的流程处理。
5.5 误删分支后的恢复思路
有一种错误几乎没人能避免:清理本地分支时手一抖,删了还没合并完的分支。传统观念里分支一经删除就彻底没了,但实际上只要这个分支上还有提交,就还有可能找回来。
git branch -d 分支名删除成功后,如果你意识到它还没合并,可以查看Git的引用日志找回旧提交:
git reflog引用日志记录的是所有HEAD指针移动过的记录,包括分支创建、切换、合并、重置。在输出里找到你删除之前所在的那条记录,找到对应提交号,然后恢复:
git branch 恢复后的分支名 查询到的提交号只要这个操作及时执行,几乎都能恢复。核心原理是Git的垃圾回收机制不会立刻清掉那些没有引用指向的提交,给了你一个"后悔窗口期"。
这个场景也是Git和其他版本控制工具相比最有魅力的一点:大部分操作都是可逆的,至少可查证的。你的每次操作都留有日志,怕的不是犯错,而是不知道自己做了什么。理解reflog的本质,会让你在Git面前敢操作,因为你知道自己随时有退路。
我在实际带人的过程中,反反复复强调的最重要的一件事,不是背下所有命令,而是建立起一条"操作前确认状态、操作后检查结果、出问题先查日志"的自检循环。Git的命令再多,拆开看也就那么几个核心动作——配置、拉取、修改、提交、分支、合并。这篇文章没有覆盖到所有高级操作,但你在日常工作中高频使用的核心链路已经完整走通了。第一次配密钥可能需要折腾半小时,第一次解决冲突可能需要反复检查好几遍,这些都是正常的。等到这些操作变成肌肉记忆,你就能把更多精力放在真正重要的代码逻辑上,而不是跟版本管理工具较劲。