
一个项目做久了最怕的不是功能做不到而是改来改去最后自己都不知道哪份代码才是最新的。我见过太多人用“最终版”“最终版2”“真最终版”这种文件名交接工作每次看到都想叹气。如果你也被这个问题折磨过那Git就是你绕不开的工具。这里不打算只给你列一份命令清单而是把我自己从接触Git到真正用它管好项目的完整经验梳理一遍。不管你是刚下载Git还不会配置的新手还是已经敲过一些git命令但总觉得没吃透的初级开发者这篇文章都值得你花点时间过一遍。我会从最基础的安装环境说起讲到日常高频命令、分支协作、冲突处理再到我踩过的那些坑尽量让每个环节都能直接拿到项目里去用。1. Git到底是什么为什么大家都用它1.1 版本控制的本质需求先想一个问题你写文档时是不是也经历过这样的场景——写了一段觉得不对想退回之前的状态但CtrlZ只能一步两步退退多了就全没了。于是你只能手动多存几个副本每个副本叫“稿子V1”“稿子V2”“稿子最终版”。代码比文档更复杂多人同时改一份代码即使每个人负责不同模块合并的时候也经常互相覆盖。Git就是一个版本控制系统。它干的事情很简单帮你记录每一次文件变更建立一条完整的时间线。你随时可以回到过去任何一个节点看看那时候的代码长什么样或者把某次改动撤销掉。更关键的是Git支持多人并行开发每个人在自己那份代码上操作最后再把所有人的工作合并起来Git会尽量自动帮你处理好大部分合并逻辑。1.2 分布式和集中式的关键区别老牌的版本控制工具比如SVN是集中式的所有的版本信息都存在一台中央服务器上每个人提交代码都必须联网连到那台服务器如果服务器挂了所有人都没法提交。Git是分布式的每个人的电脑上都是一份完整的仓库包含了全部历史记录。哪怕远程服务器彻底没了任何一个人的本地仓库都能把整个项目恢复出来。这一点在实际工作中非常有用。我在没网的环境里照样能提交代码、查看历史、切换分支等回到办公室再一次性推送到远程仓库。这种“先本地记录、再同步远端”的模式让Git在日常开发中特别灵活。1.3 三个区、三种状态这是理解Git的地基Git的核心模型是三个区域工作区、暂存区也叫索引、版本库。工作区就是你电脑上看得见的那些文件和文件夹你平时的修改都发生在工作区。暂存区是一个中间地带你把哪些文件放进去就相当于告诉Git“这些改动我准备记录了先放在这儿”。版本库则是Git真正保存历史快照的地方每一次提交都会在这里生成一个不可变的记录节点。对应这三个区域文件就有三种状态已修改modified、已暂存staged和已提交committed。如果你理解了这三个概念后面所有命令都能顺下来。很多新手最大的困惑就是搞不清楚add和commit的区别因为很多图形化工具在提交时会把这两步合并成一步。实际上add是选择要记录哪些改动commit才是真正生成版本历史记录。2. 环境准备装好Git和首次配置2.1 不同系统下的安装方式我在Windows上用git-scm提供的安装包装过很多次下载地址直接搜“Git for Windows”就能找到官方站点。安装过程基本一路Next就能完成但有几个选项值得留意在选择默认编辑器时如果你不熟悉Vim建议选Notepad、VS Code这类你平时用的编辑器否则后面遇到需要输入提交信息的情况会卡在Vim里出不来不知道按什么键才能退出。在调整PATH环境变量那一步选默认的“Git from the command line and also from 3rd-party software”就行这样你在任何终端窗口里都能直接使用git命令。行结束符的处理选“Checkout as-is, commit as-is”更省心这个后文会专门讲。macOS上安装就简单许多系统自带的终端里输入git命令时如果没装系统会引导你安装Xcode Command Line Tools。如果你更习惯用Homebrew一条brew install git也能搞定。Linux发行版一般直接用包管理器装Debian系用apt install gitRedHat系用yum或dnf install git。安装完成后在终端输入git --version能输出版本号就说明装好了。这一步是最容易让人卡住的地方所以我会单独把它放在前面说等你装好之后再做其他操作。2.2 新机器上必须先做的两件事Git装好之后不能急着用第一件事是告诉Git你是谁。在终端里执行git config --global user.name 你的名字 git config --global user.email 你的邮箱这两行配置会写入用户目录下的.gitconfig文件里之后你每一次提交都会带上这个名字和邮箱别人看提交历史时就知道是谁做的改动。团队协作时这个信息一定要填真实可识别的否则出了问题都不知道找谁。如果不同项目想用不同的身份可以去掉--global选项在具体仓库目录里重新配置一次这样当前仓库会覆盖全局配置。第二件事是检查一下换行符和默认编辑器配置git config --global core.autocrlf input git config --global core.editor code --waitcore.autocrlf这个选项的核心作用是把Windows和Linux/macOS之间的换行符差异尽量抹平。Windows默认用CRLFLinux和macOS用LF。如果团队里有人用Windows有人用macOS不统一换行符的话你会经常看到Git提示某个文件整个被修改了实际上可能只是换行符变了。我给Windows用户的建议是设置成true让Git在检出代码时自动转成CRLF、提交时转成LF存进仓库这样仓库里统一是LF大家相对少一些被换行符折腾的痛苦。3. 核心命令与实操流程3.1 获取仓库的两种方式第一种是新建仓库。你有一个空目录想让它变成Git管理的项目执行git init这条命令会在当前目录下生成一个.git文件夹里面存着所有版本历史、配置信息、分支指针等数据。平时别去手动改它除非你清楚自己在做什么。第二种是克隆远程仓库。别人已经建好的项目你想拿下来参与开发执行git clone 仓库地址git clone会做三件事把远程仓库的所有代码下载下来、把完整的历史版本记录下载下来、自动建立本地分支和远程分支的跟踪关系。你克隆完之后直接就能在本地切换分支、查看历史不需要额外配置什么。3.2 日常提交工作流改代码、暂存、提交、推送在实际项目中最频繁的一组操作就是下面这条链路git status # 查看当前工作区的状态 git diff # 查看未暂存的具体改动内容 git add . # 把所有改动加入暂存区 git commit -m 提交说明 # 把暂存区内容提交到本地版本库 git push # 把本地提交推送到远程仓库这套流程我建议所有新手练到闭着眼都能敲的程度因为一天里能重复几十遍。很多人图省事上来就是git add .加git commit也不看git status到最后提交了一堆自己没想清楚要提交的东西这就容易埋坑。正确的习惯是先git status看看哪些文件变了再git diff看看具体怎么变的确认没问题再add和commit这个过程能帮你养成“每次提交都有清晰目的”的好习惯。提交信息也值得花点心思。别写“update”“改了一下”这类毫无信息量的说明而应该写清楚这次改动解决了什么问题比如“修复登录界面在移动端输入框被键盘遮挡的问题”。别人看提交历史时靠的就是这些信息来理解项目演变脉络三个月后的你自己也一样需要依赖它。3.3 查看历史与对比差异提交做得多了你就需要回头看历史。最常用的命令是git log --oneline --graph --all加上--oneline让每次提交只显示一行--graph用字符画显示分支结构--all显示所有分支。这样看到的提交历史就像一棵树分叉和合并一目了然。如果你觉得输出太啰嗦还可以加上--decorate参数让分支和标签标记显示出来实际用起来比默认格式舒服很多。git diff在代码评审时特别有用。不带参数时git diff比较的是工作区和暂存区之间的差异也就是“我改了但还没add的内容”。带--staged或者--cached参数时比较的是暂存区和上一次提交的差异也就是“我已经add了准备要提交的内容”。这两个场景别搞混否则容易漏掉改动或者误判。3.4 新项目从零到推送到远程的完整流程假设你在某个代码托管平台上新建了一个空仓库本地已经写好了初版代码想把它推上去。完整的流程大概是这样cd 你的项目目录 git init git add . git commit -m 初始化项目 git branch -M main git remote add origin 远程仓库地址 git push -u origin maingit branch -M main是把当前分支名强制改成main。很多平台的默认分支名是main而老版本Git初始化时默认创建的分支叫master这一步是为了让本地分支名和远程保持一致减少无谓的分支重命名操作。git remote add origin 地址是把远程仓库地址登记为名字叫origin的“远程别名”以后你就不用每次敲一长串地址直接说git push origin main就行。-u参数的作用是建立当前本地分支和远程分支的跟踪关系以后直接git push不带参数也能知道推送到哪里去。4. 分支管理与团队协作4.1 分支到底是什么为什么它是Git的杀手锏分支可以理解成从主线上分出去的一条独立工作线。你在分支上提交代码完全不影响主线。等分支上的功能开发完了再把分支合并回主线。这就像写文章的时候你想尝试一个大胆的新写法于是复制了一篇草稿在上面改改得满意了就转正改得不行就扔掉完全不污染原稿。在Git里创建分支、切换分支都非常轻量基本就是移动一个指针的事所以业界主流的协作方式几乎都建立在“开分支、合并分支”这套模型上。常见的工作流是main分支永远保持可发布状态新功能从main分出一个feature分支开发好了经过测试再审阅合并回去。4.2 常用分支操作命令创建并切换分支git checkout -b feature/login这条命令等价于分别执行git branch feature/login和git checkout feature/login。Git 2.23以后也可以用git switch -c feature/login来切换语义上更清晰。查看当前仓库所有分支git branch -a-a参数会同时显示远程分支。当前所在分支前面会有一个星号标记或者用git status第一行也能看到。合并分支git checkout main git pull git merge feature/login合并之前先切换到目标分支拉取最新代码再执行git merge把功能分支合并进来。如果合并过程没有任何冲突Git会自动生成一个合并提交。如果有冲突它会提示你哪些文件需要手动处理。删除分支git branch -d feature/login功能开发完并且已经合并回主线后本地分支就可以删掉了。如果分支还没合并但你确定不要了需要用-D强制删除。远程分支的删除用git push origin --delete feature/login。4.3 合并时冲突怎么解冲突是Git新手最容易慌的地方。先明确一点冲突不是错误而是Git在帮你做保护。什么情况会冲突比如你和同事同时改了一个文件里相近的几行代码Git没办法自动判断该保留谁的于是停下来把决定权交给人来处理。冲突发生时你打开那个文件会看到类似这样的标记 HEAD 你当前分支上的代码 被合并分支上的代码 feature/login HEAD和之间是当前分支的内容和之间是待合并分支的内容。你需要做的就是阅读这两段内容和同事确认到底应该保留什么然后把不需要的删除把冲突标记也一起删掉保存文件。之后执行git add该文件再git commit合并就完成了。我处理冲突的经验是能提前沟通就提前沟通。如果你知道同事正在改某个文件你自己也打算改同一个文件最好提前说一声。比等到合并时再扯皮省力得多。4.4 分支合并还是变基两个方案怎么选合并分支有两种主要方案git merge和git rebase。merge会生成一个合并提交把两个分支的演变轨迹交织在一起缺点是提交历史会出现分叉和合并节点看多了会觉得乱。rebase则是把当前分支的提交“搬运”到目标分支的最新提交之后好处是提交历史是一条干净的直线缺点是它改写了提交历史如果处理不当会丢失上下文。针对还没有推送到远程的本地分支用rebase整理成一条干净的提交线是完全合理的。但一旦提交已经被别人拉取走了就不要再随便rebase了因为你会重写历史别人下次pull的时候会看到一堆莫名其妙的重复提交严重时甚至会把协作搞乱。团队里一般会有约定俗成的规范公共分支只允许merge个人功能分支在合并前可自行整理历史。如果你拿不准那就优先用merge简单可靠不会出幺蛾子。5. 撤销、回滚与灾难恢复5.1 精确理解reset、revert和restore的区别在Git里做撤销最忌讳的就是凭感觉乱敲命令。这里我把三个最容易混淆的命令一次说清楚。git restore用于丢弃工作区/暂存区的改动不会销毁提交历史。如果你改了一堆代码觉得全改错了想回到上一次提交的状态执行git restore .就能把工作区恢复成当前HEAD的内容。git reset用于移动当前分支的HEAD指针分为三种模式--soft只移动HEAD指针暂存区和工作区都不动。相当于是“提交完后悔了想重新提交”。--mixed默认移动HEAD同时把暂存区内容清空但工作区代码保留。这就是“add完了但还没commit时想撤销add”的操作。--hard移动HEAD暂存区和工作区全部重置成指定提交的状态。这个命令特别危险因为它会直接丢弃工作区的所有未提交改动而且无法找回。git revert则完全不同。它不会移动HEAD而是生成一个新提交这个提交的作用是把之前某次提交的改动“反向应用一遍”。换句话说revert是安全的撤销方式因为它保留了撤销记录适用于已经推送到远程的公共分支。我在实际项目里处理“线上代码有问题需要紧急回滚”这个场景时永远优先用revert而不是reset。因为远程分支是大家共用的reset会改变提交历史别人再pull就会产生大量冲突revert只是在历史末尾加了一条回滚记录不影响别人已有的提交。5.2 文件被我搞丢了还能找回来吗Git最让人安心的一点就是只要你提交过基本都能找回来。假设你不小心用git reset --hard把一次提交弄丢了但你知道那次提交的哈希值可以执行git reflogreflog会列出HEAD指针最近所有移动的记录包括被你reset跳过的那次提交。找到对应的哈希值用git reset --hard 哈希回去就行。reflog是本地维护的引用日志记录了你在这个仓库里做过的几乎每一次操作是Git的“后悔药”。万一你连哈希值都不记得了也可以通过git fsck --lost-found来扫描丢失的悬空提交。这招通常只在极端情况下用到但知道这个机制会让人安心很多只要提交进了Git的数据库就不容易彻底消失。5.3 误加了大文件或敏感信息怎么办我见过不少人在项目里误提交了一个几GB的安装包或者数据库备份文件导致仓库体积暴涨每次clone都痛苦不堪。处理办法是如果只是把文件加入暂存区但还没提交用git rm --cached 文件把它从Git索引移除再配合.gitignore规则阻止再次添加。如果已经提交了并且推送到远程那就需要重写历史了Git官方推荐用git filter-branch或者BFG Repo-Cleaner这类专门工具来清洗历史。其中的关键提醒是重写历史后需要强制推送远程其他人的本地提交也需要重新同步这是一个需要整个团队协调配合的操作千万不能一个人闷头处理。如果误提交的是密码、密钥这类敏感信息请记住一个原则第一时间去相关平台修改密码或吊销密钥。因为Git历史里的一切都是公开可查的光是删除新提交里的文件完全不够你得假设它已经泄露了然后立刻止损。6. 常见问题与排查技巧实录6.1 问题速查表症状、原因与解决我把自己和身边同事踩过的高频问题整理成了一张速查表直接照着对号入座就行。症状常见原因解决方法git push时报权限被拒没有配置SSH key或认证失效检查ssh -T gitgitee.com输出重新添加公钥到平台commit时弹出奇怪的编辑器、卡住没法输入默认编辑器是Vim不熟悉基本操作按Esc后输入 :wq 回车强制退出重新配置core.editor某文件全是改动但明明没动过换行符CRLF/LF问题或文件编码问题统一core.autocrlf设置检查是否有关联的lint格式化工具git log中文乱码提交信息编码不一致设置core.quotepath false和i18n.logoutputencoding utf-8.gitignore规则不管用文件已经被Git跟踪忽略规则只对未跟踪文件生效执行git rm --cached 文件取消跟踪不小心reset掉了提交HEAD移动后原提交成了悬空节点用git reflog找到哈希并reset回去这些问题我在不同项目中基本都中过招每个背后都有一次深刻的踩坑回忆。6.2 为什么你的.gitignore规则失效了.gitignore文件的作用是告诉Git哪些文件不要跟踪。很多新手会习惯性把node_modules、target这类目录写进去但执行后却神奇地发现这些文件夹还是在仓库里。原因是.gitignore只对尚未被Git跟踪的文件生效。如果某个文件之前已经被git add过了就算后来再写进.gitignore也不会被忽略它已经进入了Git的跟踪列表。解决办法是你需要先把它从Git索引中移除但保留工作区的文件git rm -r --cached node_modules加了--cached参数的意思是只移除Git的跟踪记录不影响实际磁盘上的文件。执行之后再提交一次Git就会正式解除对这个目录的跟踪之后.gitignore规则才会正常生效。6.3 认证问题SSH key还是HTTPS密码克隆仓库地址可以选HTTPS也可以选SSH。HTTPS方式每次push都需要输入账号密码稍微麻烦一点。用SSH方式需要在本地生成一对密钥私钥留在本地公钥配置到代码托管平台上之后push就不再需要每次都输密码了。生成密钥的命令是ssh-keygen -t ed25519 -C 你的邮箱执行后会问你保存路径和口令直接回车使用默认路径、不设口令也行。生成的公钥文件一般在~/.ssh/id_ed25519.pub把里面的内容复制到代码托管平台的SSH Keys设置页面里保存。测试是否配置成功用ssh -T gitgithub.com这个命令会尝试连接GitHub的SSH服务看到欢迎信息就说明公钥已经生效。不同平台的测试域名不一样但原理相同。6.4 提交写错字或漏了文件怎么办提交写错了说明这是非常常见的场景。如果提交还没有推送到远程处理起来很简单git commit --amend -m 新的提交信息这个命令会把最后一次提交的信息替换掉不会新增一条混乱的提交记录。如果提交之后发现漏了一个文件同样可以用--amend补救先git add漏掉的文件再执行git commit --amend --no-edit这样会把漏掉的文件补进上一条提交里而且不修改提交信息。但注意--amend会改变提交的哈希值也就是说它会重写最近一次提交。如果这条提交已经推送到远程并且有其他人拉取了就别再用了应该用新的提交来修正。6.5 拉取远程代码时总出现冲突很多人习惯一上来就git pull然后发现pull报冲突心里一慌。其实git pull的本质是git fetch加git merge如果远程分支和你本地分支都修改了同一个文件的同一块区域合并自然会冲突。我个人习惯是在每天开工之前先pull一次减少并发修改的时间窗口。在开始改一个模块之前也会先同步最新代码。改完代码准备提交时如果pull有了新的远程更新我会先把本地提交整理好再用git pull --rebase生成一条更干净的提交线。实在合并不了再老实解决冲突冲突解决完记得用git status确认没有遗漏的冲突标记然后在编辑器和终端里都检查一遍再提交。7. 写在最后的一些补充经验分享几个我在实际工作中切实受益的习惯。一个是提交粒度要小功能拆细一点每次提交只做一件事不要一把梭把所有改动塞进一个提交里。另一个是提交信息用中文还是英文无所谓关键是团队统一描述要清晰因为提交历史本身就是项目最重要的文档之一。再一个就是多用git log和reflog查看自己以前的记录时间久了你会对这些命令形成肌肉记忆项目管理水平也会明显不一样。如果你刚开始接触Git不要试图一天记住所有命令先把init、status、add、commit、push、pull、clone、branch、merge这几个高频命令练熟其他命令等你真正遇到场景的时候再查也来得及。这个工具最强大的地方在于它的容错机制只要你主动提交过绝大多数情况下代码都不会真正丢失。用多了以后你会发现它真的能帮你省下很多冤枉时间。