
刚接触Linux的朋友八成都会遇到同一个头疼问题文件改来改去过两天想找回某个能用的版本结果发现早被覆盖了。只能对着屏幕后悔当初怎么就没多存几个副本。后来我自己也经历了那个“新建文件夹(3).zip”的混乱阶段Windows下攒了一堆最终版、终极版、打死也不改版直到切到Linux环境下做开发被同事推荐用了git才算真正从这种噩梦里面解脱出来。git这个东西官方的说法叫“分布式版本控制系统”名字听着有点唬人但用接地气的理解方式它就是一个“会拍照的文档管家”。每当你完成了一个阶段的修改就可以让git给整个项目拍一张照片存进它的相册里。以后不管你怎么折腾改坏了、删错了、想回到过去的某个状态只要翻相册随时都能把当时的文件原样恢复出来。而且它不只是给自己用在多人协作的时候git还能充当一个非常称职的协调员谁改了什么、什么时候改的、改动了哪些地方全部记录得清清楚楚谁也别想浑水摸鱼。这篇文章我就结合自己在Linux下使用git的实际经验从安装配置讲起再到日常最常用的命令、分支管理和冲突处理最后补充远程仓库搭建、免密登录和忽略文件的玩法。废话不多说直接进正文。1. 版本控制到底在解决什么问题1.1 没有版本控制的日常有多痛先说说没有版本控制的日子。假设你正在开发一个网站昨天刚把首页的样式调好今天想改一下导航栏的颜色改完发现整体配色完全不搭想退回昨天那版。如果你没有做任何备份那就只能靠CtrlZ可一旦关掉编辑器或者改的地方太多撤销基本等于失效。更崩溃的是多人协作的场景。两个同事同时修改同一个文件一个改好了传到共享目录另一个紧接着把自己的版本传上去前面的修改直接被覆盖。然后就是永无止境的争吵这代码明明是我写的怎么不见了这种场景在没有任何版本管理工具的团队里几乎每天都会上演。还有写论文、写标书、做方案的人文件夹里往往躺着各种“初稿”“修改稿”“最终稿”“最终稿2”“绝对不改稿”时间久了根本分不清哪个才是真正最新的。本质上大家想要的东西很简单每一次重要变更都能留个底随时可以后悔随时可以回到过去。1.2 git的快照思路拍照而不是记差异git处理这个问题的方式和大多数人第一直觉有点不一样。很多人以为版本控制就是把每次修改的“差异”记下来像是一个补丁叠加一个补丁。git不是这么干的它更像一个摄影师。每当你执行一次提交git就把当前所有文件的状态完整地拍一张照片存成一个“快照”。如果某个文件没有变化git并不会重复存储一遍完整文件而是直接引用上一次的快照内容这样既节省空间又保证了任何时候都能快速恢复到任意一个历史节点。这个设计带来的好处非常明显。你想回到三天前的状态git直接把那个快照摊开铺在你面前而不是逆着顺序挨个撤销补丁。对于大型项目、长时间维护的老项目来说这种基于快照的设计在分支切换和代码合并时的速度优势会体现得非常明显。还有一个生活中很形象的类比。你用手机拍照拍完觉得不满意可以删但如果没有拍照后面想回忆当时的场景就完全没辙。git的每一次提交就相当于在关键节点主动按下快门。不过它比手机相册更强的一点是——每张照片都附带完整的说明信息谁拍的、什么时候拍的、这张照片相对于上一张改了什么、为什么要做这次修改。这就是commit message提交信息存在的意义。1.3 git的三个区域怎么理解理解了快照思想再看git相关的操作就顺理成章了。git把整个项目分成了三个区域工作区、暂存区和版本库。工作区Working Directory就是你在电脑上能看到的那些文件和文件夹平时在这里编辑、修改、删除文件。暂存区Staging Area / Index可以理解成一个临时存放区你用git add命令把想记录的文件先“选中”放进来。放在这里面的文件git知道它们被修改了但还没正式拍照存档。版本库Repositorygit真正存放所有历史快照的地方。你用git commit就是把暂存区里的内容正式拍成一张照片永久保存到版本库中。用拍照片的流程来类比工作区是你的拍摄现场文件是演员和道具暂存区是候场区git add就是点名让一些演员站到候场区去git commit才是真正按快门出片。你可以在候场区反复调整这一批拍哪些、下一批拍哪些完全由你掌控。2. 安装git不同Linux发行版的正确姿势2.1 用包管理器快速安装git在Linux上安装git其实非常简单绝大多数发行版都可以直接用自带的包管理器搞定。我自己用过比较多的几个系统安装命令基本都长这样Debian/Ubuntu系sudo apt update sudo apt install git -yCentOS/RHEL/Fedora系老版本用yum新版本用dnfsudo yum install git -y # 或者 sudo dnf install git -yArch/Manjaro系sudo pacman -S git装完之后验证一下版本git --version正常会输出类似git version 2.39.2这样的信息。如果你所在的系统比较老包管理器里的git版本比较旧也可以用添加软件源的方式来装新版。比如Ubuntu上可以用git官方维护的PPACentOS上可以考虑从源码编译安装但这些属于进阶玩法日常使用直接用系统自带的就行。2.2 源码编译安装什么时候需要有些特殊场景下包管理器里的git版本实在太旧或者你需要定制某些编译参数才会用到源码编译。我早年在一台老旧的CentOS 6服务器上就踩过这个坑系统自带的git还停留在1.7时代连很多新语法都不支持市面上的教程大多基于2.x版本照着操作老是报错。最后只能从源码编译装了一个新版本。编译安装的步骤大致是这样# 先安装依赖 sudo yum install -y curl-devel expat-devel gettext-devel openssl-devel zlib-devel gcc perl-ExtUtils-MakeMaker # 下载源码包 wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.39.2.tar.gz # 解压并编译 tar -zxvf git-2.39.2.tar.gz cd git-2.39.2 make prefix/usr/local all sudo make prefix/usr/local install编译的过程稍微有点耗时大概几分钟到十几分钟不等取决于机器性能。装完之后记得看一下/usr/local/bin和/usr/bin下哪个git版本在前必要时用软链接把新版指到前面否则可能还是会调用到老版本。我个人建议除非有硬性需求不然还是尽量用包管理器安装省心很多。2.3 安装后的三个关键配置git装好之后第一件事不是急着建仓库而是先告诉git你是谁。这个身份信息会写进每一次提交记录里相当于照片上的摄影师署名。如果跳过这步commit的时候git会报错或者用一串看不懂的默认字符当作者名。git config --global user.name 你的名字 git config --global user.email 你的邮箱这里有一个非常容易踩的坑user.email一定要填一个真实有效的邮箱。如果你是往开源社区提交代码这个邮箱最好和你注册代码托管平台的邮箱保持一致这样你的提交才能正确关联到你的账号上。我见过有人随便填了一串字符结果在GitHub上看过去的提交记录作者头像显示不出来也无法统计到贡献图里等到想补救的时候历史提交已经改起来非常麻烦了。除了身份信息我还会顺手设置两个东西。一个是默认编辑器当你执行git commit不带-m参数时git会弹出一个编辑器让你写提交信息。系统默认往往是vim如果你不熟vim可以换成nano或者VS Codegit config --global core.editor nano另一个是常用别名把一些比较长的命令缩短。日常使用频率最高的几个git config --global alias.st status git config --global alias.co checkout git config --global alias.br branch git config --global alias.lg log --oneline --graph --all --decorate设置好之后git st就是git statusgit lg就是查看各种分支的图形化提交历史效率提升非常明显。查看已有的配置项可以执行git config --list。3. 核心命令实操从init到第一次提交的完整流程3.1 git init与第一次add、commit现在假设你有一个项目文件夹想把它交给git管理。进入目录执行cd my-project git init这个命令会在当前目录下创建一个隐藏的.git文件夹所有版本历史、分支信息、配置数据都存在这里面。从这一刻起这个项目就进入git的管辖范围了。接着你可以看看当前仓库的状态git status如果是刚新建的文件git会提示你这些文件还处于未跟踪untracked状态。所谓未跟踪就是git知道有这个文件存在但还没有给它拍过任何照片也不知道它的历史。要让git开始关注这些文件需要先把它们放进暂存区git add .这个命令会把当前目录下所有未被忽略的文件全部加入暂存区。你也可以只添加指定文件git add src/main.py config.iniadd完成之后再执行git status你会看到文件变成了绿色的“新文件”意味着他们已经站在候场区随时可以拍照。这时候就可以正式提交了git commit -m 初始化项目添加主程序和配置文件到这里第一张照片就拍完了。git会给出一个很简短的反馈比如1 file changed, 15 insertions。意思是这次提交涉及1个文件新增了15行内容。这行信息虽然不起眼但在排查问题的时候非常有用它能让你快速判断一次提交影响了哪些文件。3.2 提交信息怎么写才不算白写在实际工作中我见过太多人的提交信息是“update”“fix”“修改bug”这种信息基本没有价值。几个月之后回头看你根本想不起来这次提交到底改了什么更别谈定位问题了。我个人比较推崇的写法是遵循一种简洁的约定第一行用一句话概括本次提交做了什么控制在50个字符以内就像邮件的标题如果需要补充细节空一行后写正文。举个例子git commit -m 修复登录接口在密码错误时未返回友好提示的问题 -m 原因是后端捕获异常后直接返回500状态码没有处理业务异常分支现增加错误码响应。多个-m参数每一个会作为独立段落写入提交信息这种格式化写法在团队协作中谁看谁舒服。还有一个小技巧当你改到一半发现有些文件还不想提交可以用git add指定文件只提交这一部分形成一个逻辑完整的小提交。不要一个提交里塞上十多个不相关文件的改动否则将来定位问题会让人头大。3.3 查看历史与文件变迁提交了几次之后你再看git log就能看到一条提交历史记录git log --oneline输出大概是这样的3f2b1c7 修复登录接口错误提示 a9e8d21 新增用户注册功能 5c0f4e2 初始化项目每一行最前面的那一串字符是这个提交的唯一ID相当于照片的编号。想看看某次提交到底改了什么内容可以执行git show 3f2b1c7想比对两个提交之间的差异git diff 5c0f4e2 3f2b1c7这些操作都是“只读”的不会影响仓库当前状态你可以放心大胆地看。如果想看当前工作区里面哪些文件被改动了、改了什么不提交任何附加参数执行git diff就行git diff我自己习惯用git diff --stat先看看改动涉及哪些文件、每个文件改了多少行心里有个全局概念然后针对具体文件git diff filename查看详细差异。3.4 时光机git reset回退与reflog救命“会拍照的文档管家”最迷人的能力就是随时可以回到历史。假设你提交了一个有问题的版本想回到上一个版本的状态执行git reset --hard HEAD~1这里的HEAD可以理解成一个指针永远指向当前所在的分支的最新一次提交。HEAD~1表示往回退1次HEAD~2表示往回退2次。如果知道具体的提交ID也可以直接指定git reset --hard 5c0f4e2但是reset --hard这个命令要格外小心它会同时重置暂存区和工作区的文件内容。也就是说你当前工作区里还没提交的修改也会跟着被丢掉。如果没有备份这些改动就真的找不回来了。所以我通常建议在执行reset --hard之前先用git status确认一下工作区是否干净。那万一真的手贱执行了误操作把一条重要提交给打没了怎么办git其实还有一个“后悔药”叫git reflog。reflog记录了git本地仓库所有分支和HEAD的移动历史哪怕你reset掉了某个提交只要提交对象还在仓库里就能通过reflog找到它的ID然后重新恢复回来。git reflog输出类似3f2b1c7 HEAD{0}: reset: moving to 5c0f4e2 a9e8d21 HEAD{1}: commit: 新增用户注册功能看到了吗即使你reset到了5c0f4e2reflog里仍然记录着a9e8d21这个提交曾经存在过。这时候你只要执行git reset --hard a9e8d21就能回到那个状态。我每次在交流群里看到有人说“git误删了代码怎么办”我都会先让他试一下git reflog——大部分情况下都能救回来。4. 分支与合并多线拍摄与冲突处理4.1 分支的本质一个可移动的指针分支是git里最强大的功能之一也是初学者最容易绕晕的概念。其实说白了分支就是指向某个提交的一个指针。默认情况下git会为你创建一个主分支名字在新的默认配置里通常叫main老版本叫master这个主分支长期稳定存放的是可发布的代码版本。当你需要开发一个新功能不想影响主分支的时候可以新建一个分支git branch feature-login然后切换过去git checkout feature-login在新版git里也可以一行搞定git switch -c feature-login这个新分支和主分支在创建的那一刻指向同一个提交但在那之后你在新分支上的每一次提交都会让这个分支的指针向前移动而主分支的指针停在原地不动。相当于你在主线上分出了一条新的时间线两边可以同时往前走互不干扰。用拍照的方式来理解分支就是同一个项目里开出了多条并行的拍摄线路。主线拍摄的是稳定版本功能分支拍摄的是实验性玩法。等新功能稳定了再把这条分支的照片合进主线相册大家都能看到。4.2 团队协作中最常见的冲突场景多人同时开发几乎绕不开合并merge这个动作。把功能分支合并到主分支git checkout main git merge feature-login如果两边改的文件互不重叠git会自动合并整个过程静默且高效。但有一种情况git会非常“诚实”地告诉你出现了冲突conflict两个分支都修改了同一个文件的同一行内容而且修改结果还不一样git不知道该听谁的。假设你和同事同时在改一个配置文件config.ini你把它里面的timeout从30改成了60同事把它从30改成了90。两个分支先后合并到主分支时git就会停下来报告冲突。此时你打开那个文件会看到类似这样的内容timeout HEAD 60 90 feature-loginHEAD部分是你当前所在分支主分支的内容feature-login部分是你的要合并进来的分支的内容。到底保留哪个需要人工判断——这恰恰说明git是一个老实人它不会替你做业务决策只会诚实地把所有矛盾摆在桌面上。4.3 冲突解决的实战步骤解决冲突的流程并不复杂用编辑器打开冲突文件找到冲突标记根据实际情况删除不需要的内容保留正确的配置。把所有、、标记行全部删干净只留下最终想要的代码或配置内容。保存文件后执行git add告诉git这个文件的冲突已经解决好了。最后执行git commit提交完成这一次合并。还是拿上面那个配置举例如果公司要求超时时间为60秒就把feature-login那部分删掉最终文件只留下timeout 60然后git add config.ini git commit -m 合并功能分支统一超时时间为60秒。我自己的经验是冲突产生的根本原因通常不是git不会处理而是团队之间缺乏沟通。改公共配置之前先看看当前分支状态及时同步最新代码能很大程度上减少冲突。还有一条比较实用的建议在merge之前先把自己的功能分支 rebase到最新的主干上可以保持提交历史的线性整洁后面细说。4.4 分支管理经验小而精、及时清理用git久了我养成的习惯是功能分支一定要“小而精”一个分支只做一个完整的事情。比如“feature-login”就只做登录功能“fix-payment-bug”就只修支付bug。这样分支合并的时候冲突范围可控代码review也容易归档起来逻辑清清楚楚。分支合并完成后顺手把这个分支删掉git branch -d feature-login小写的-d只会删除已经合并过的分支如果有未合并的提交git会提醒你防止误删。确定要强删的话用-D但一般不建议这么干。5. 远程仓库与免密登录5.1 把本地仓库推到远程服务器本地玩git玩得再溜也只是单机版。真正要跟团队协作、或者多台机器同步代码还得引入远程仓库。常见的远程仓库形态有自建的GitLab、轻量级的Gitea也有大家耳熟能详的GitHub等代码托管平台。不管是哪种操作逻辑都差不多本地仓库和远程仓库之间通过git命令进行push推和pull拉。假设你在GitLab上新建了一个空仓库拿到了一个仓库地址。在本地已有的项目里关联这个远程地址git remote add origin gitgitlab.example.com:username/my-project.git这里的origin是一个远程仓库的别名默认叫法你也可以改成别的名字。把这个本地仓库的主分支推送到远程git push -u origin main-u参数的作用是将本地main分支和远程main分支建立关联以后直接执行git push或git pull就能自动匹配对应的远程分支不需要每次都带参数。如果你是从远程clone项目到本地git clone gitgitlab.example.com:username/my-project.gitclone下来之后git会自动把远程地址设为origin并且本地生成一个main分支跟踪远程的main分支。我见过不少新手分不清clone和init的使用场景——init是从零开始建本地仓库clone是把已有的远程仓库整个复制到本地两者别搞混。5.2 SSH密钥免密登录再也不用输密码推送代码的时候如果每次都要求输入用户名密码用不了几次就会把人烦死。更推荐的做法是配置SSH密钥一次配置长期免密。先生成密钥对ssh-keygen -t ed25519 -C 你的邮箱一路回车就能在~/.ssh/目录下生成两个文件id_ed25519是你的私钥绝对不能泄露给任何人id_ed25519.pub是公钥可以公开需要放到GitLab或GitHub的后台设置里。然后把公钥内容复制出来cat ~/.ssh/id_ed25519.pub复制输出的一整行内容粘贴到代码托管平台的SSH Keys设置页面保存即可。之后再用git clone、git push、git pull这些操作SSH协议会自动完成身份验证完全不需要输密码。这里提一句SSH协议除了免密还自带传输加密功能安全性比裸用的HTTP方式高。所以在公司内网搭建git服务器或者用云服务器自建GitLab我都强烈建议走SSH协议。5.3 两条常见远程报错的排查远程操作中最常见的报错我列两个有代表性的。第一个是fatal: remote origin already exists。当你想给本地仓库重新关联远程地址执行git remote add origin xxx时如果origin已经存在git就会报这个错。解决办法是先把旧的远程关联删掉再加新的git remote remove origin git remote add origin gitgitlab.example.com:username/my-project.git第二个是登录认证相关的报错比如HTTPS方式推代码时遇到Login failed. Check API token or GitLab version. Log in via Git if the version...。这个信息通常出现在使用IDE插件或者第三方工具连接GitLab远程仓库时核心问题一般是API访问令牌无效、GitLab版本和插件不兼容、登录方式不对。排查思路也很直接确认自己的账号凭证有没有过期去GitLab设置里重新生成一个访问令牌然后更新到IDE的凭证管理里检查GitLab服务器版本和本地客户端、插件版本是否兼容老版本GitLab对某些API调用支持不全实在不行就退回最朴素的git命令行方式用SSH协议完成克隆和推送。这种问题没什么高深技巧关键是要学会看日志里的关键词并且一步步排查凭证、版本、协议这三个维度。5.4 pull与fetch的取舍最后补充一个关于更新的细节。git pull和git fetch都能从远程拿东西但两者有区别。git fetch只是把远程仓库的最新状态下载到本地更新远端的追踪分支不会改动你当前工作区的文件git pull则是fetch之后自动merge到当前分支一把梭到位。有这么一种情况我会刻意用fetch而不是pull——远程分支被同事强制推送过历史被改写重置了直接pull可能产生一堆杂乱无章的合并提交。这时候先用git fetch看一下远程发生了什么再决定是reset还是merge操作空间大得多。日常协作比较顺利的话直接pull问题不大但了解这个区别对排查疑难问题很有帮助。6. 忽略文件与.gitignore实战6.1 为什么要设置忽略规则很多新手刚开始用git习惯性把项目里所有文件一股脑全部add进去。直到有一天提交了一个含密码的配置文件或者上传了一个几百MB的编译产物才发现事情闹大了。在真实项目里有很多文件是不应该被git跟踪的编译生成的中间文件、依赖包目录、本地配置文件、IDE的私有配置、日志文件、临时文件、密钥文件。这些文件要么体积巨大要么包含敏感信息要么是机器相关、因人而异的内容提交进版本库只会带来灾难。.gitignore文件就是用来告诉git这些目录和文件我不管你不要跟踪它们。6.2 .gitignore语法与常用模板一个典型的.gitignore文件长这样# 编译产物 target/ build/ dist/ # 依赖目录 node_modules/ vendor/ # 日志与临时文件 *.log *.tmp .DS_Store # IDE配置 .idea/ .vscode/ # 本地环境配置含敏感信息 .env config.local.js语法非常直观每一行写一个忽略规则以斜杠/结尾表示忽略整个目录以通配符匹配文件名比如.log匹配所有.log文件以!开头表示不忽略重新纳入跟踪范围以#开头是注释。例如你想忽略所有.env文件但必须保留.env.example这个模板文件.env !.env.example还可以用目录加通配符的组合比如docs/*.tmp只忽略docs目录下一级的tmp文件但不影响docs子目录下的其他文件。GitHub官方仓库里收集了各种主流语言和框架的.gitignore模板需要的时候直接去翻比自己从零写省力很多。我自己每开一个新项目第一件事就是把对应语言的.gitignore模板拉下来放到项目根目录这就相当于提前给管家立好规矩哪些东西不用拍照。6.3 已经跟踪的文件怎么改口说忽略有个坑特别容易踩项目一开始没配.gitignore编译产物已经被git add甚至git commit跟踪上了后面才补配了.gitignore结果发现这些文件“赖着不走”无论忽略规则怎么改git status里还是能看到它们的变动。原因很简单.gitignore只对未跟踪的文件生效。如果一个文件已经进入版本库git就会无视忽略规则持续跟踪它的变化。这时候你需要手动把它从git的跟踪列表里移除但保留它在本地磁盘上git rm -r --cached target/ git rm --cached .env--cached参数的意思是“只从git索引中移除不删物理文件”。执行完之后再把这些规则写进.gitignore提交一次之后这些文件就不再受git管理也不会被误提交了。我在团队培训的时候经常强调这个细节因为很多人第一次处理这个场景都会被绕进去知道git rm --cached这个命令的人十分钟能搞定不知道的人能折腾一下午。7. 改动太多无从下手善用git diff与status查漏7.1 提交前检查改动的习惯我自己写代码有个铁律提交之前必须git diff看一眼改动。别小看这一步很多时候你以为自己改了A文件实际上因为手滑还动了B文件你以为删掉了一行废代码实际把正常逻辑也给删了。只有亲自看过diff才能确认这次提交的内容“干净”。# 查看工作区所有改动 git diff # 只看某个具体文件的改动 git diff src/main.py # 查看暂存区与上次提交的差异 git diff --cached如果把改动比作要装进相册的照片diff检查就是按下快门前快速过一遍取景框看看画面里有没有乱七八糟的东西入镜。尤其是多个需求并行开发的时候这个习惯能避免把不该提交的文件卷入同一个commit。7.2 大型代码库中精准定位问题提交项目大了以后提交历史会变得非常长。有时候你想知道“某一行代码是什么时候被谁加进来的”用git log加文件路径就能查git log --oneline -- src/main.py还可以直接搜某段代码是什么时候出现的。git有专门的能力来“追溯”问题行也就是git blamegit blame src/main.py这个命令会把文件的每一行都标注上它来自哪次提交、谁写的、什么时候写的。看起来像一份“甩锅名单”但更准确的说法是一份“责任清单”。出了问题顺着行号往回追通常几分钟就能锁定是哪个提交、哪次改动引入的bug。这个操作在大型项目里尤其好用我很多次帮人排查线上问题第一步就是用git blame定位代码行再拿提交ID去查完整的diff效率极高。8. 高频踩坑记录与实用排查清单8.1 报错与解决对照表把日常使用中没见过的高频问题整理成一个表格方便大家直接对照着处理报错/现象原因解决办法Please tell me who you are没配置user.name和user.email执行git config --global配置身份信息fatal: not a git repository当前目录不是git仓库或者没执行git init在项目根目录初始化仓库或cd到正确的仓库目录fatal: remote origin already exists远程地址关联重复git remote remove origin后重新添加fatal: refusing to merge unrelated histories两个仓库没有共同的提交祖先常见于强行关联了不同历史合并时加--allow-unrelated-histories参数error: failed to push some refs to ...本地版本落后于远程直接推送被拒绝先git pull拉取远程最新代码解决冲突后再推送Your branch is ahead of origin/main by 3 commits本地有3个提交还没推送执行git push推送到远程warning: LF will be replaced by CRLF换行符风格不一致Linux和Windows混用在.gitignore或git config中统一换行符处理规则8.2 换行符与文件名乱码的处理跨平台协作时换行符是一个容易被忽略的大坑。Windows默认用CRLF表示换行Linux和macOS用LF。git在提交的时候有一个自动转换机制但不同系统的默认行为不完全一样于是经常出现“明明没改文件git status却提示文件被修改”的怪现象。我自己在Linux工作、偶尔把仓库Clone到Windows上处理一些事情就遇到过这种情况。解决办法是统一git的换行符策略# 提交时把CRLF转成LF检出时转换为当前系统风格 git config --global core.autocrlf input在纯Linux环境下我习惯把core.autocrlf设为input提交到仓库的行尾一律是LF这样能有效减少莫名其妙的diff噪音。还有一个中文相关的坑。如果仓库里的文件名包含中文在某些终端下执行git status会显示成八进制的转义字符比如\346\265\213\350\257\225.txt根本看不懂。这是因为git默认对非ASCII字符做了转义处理。执行一下git config --global core.quotepath false再次查看中文文件名就能正常显示出来了。这个配置在内网中文项目里几乎是刚需。8.3 误删文件与分支恢复的三个保命命令我把最实用的三个“后悔药”命令放在一起建议大家都记住# 恢复工作区误删的文件还没提交过修改的 git checkout -- filename # 恢复误删但已提交过的文件 git restore filename # 查看本地所有分支操作历史找回不小心删掉的分支或提交 git reflog我同事有一次在服务器上误删了一个还没推送的本地分支那个分支上有他两天的开发成果。当时急得不行后来我让他执行git reflog找到那个分支最后一次commit的ID然后用git branch new-branch-name commit-id把分支重建起来两天的工作完好无损地找回来了。从此以后我们团队新人都必须记住reflog这个命令。9. 常用命令速查与面试高频考点9.1 Linux下git命令速查表操作场景命令初始化仓库git init克隆远程仓库git clone 仓库地址查看状态git status添加文件到暂存区git add 文件名 / git add .提交git commit -m 说明查看提交历史git log --oneline查看某次提交的改动git show 提交ID查看工作区改动git diff创建并切换分支git switch -c 分支名合并分支git merge 分支名拉取远程最新git pull推送本地提交git push暂时保存当前工作区改动git stash恢复stash的改动git stash pop回退到某个提交git reset --hard 提交ID强制推送到远程git push --force慎用9.2 面试中最高频的几个git问题很多公司在Linux相关岗位的面试里都会带几个git问题挑几个高频的说说git pull和git fetch有什么区别fetch只把远程数据下载到本地不动工作区pull在fetch之后还会自动merge到当前分支。实际使用中fetch更安全pull更省事。git merge和git rebase有什么区别merge会生成一个单独的合并提交保留各分支的分叉历史缺点是历史图可能比较乱rebase会把当前分支的提交“垫”到目标分支的后面让提交历史变成一条直线非常整洁但会重写提交ID所以不要对已经推送、多人共享的分支做rebase。很多团队会规定feature分支合入主干用rebase主干分支合入用merge。HEAD、工作区、暂存区指什么HEAD是指针指向当前分支的最新提交工作区是能看到的文件暂存区是add之后、commit之前的中间区域。这个问题经常和“git reset的三种模式有什么区别”一起问reset --soft只动HEADreset --mixed默认模式动HEAD和暂存区reset --hard连工作区一起重置。一个commit被覆盖或丢失了如何找回优先想到git reflog通过重新指向commitID来恢复。这是考察对git底层对象模型的掌握程度。git的提交对象、树对象、blob对象组成了一个完整的图结构只要对象不被gc垃圾回收就还有机会找回来。10. 实战心得我的git使用习惯最后分享几个自己在长期使用中养成的习惯算不上标准答案但都是实打实的经验。第一个习惯是提交粒度要小。我几乎不会把一整天的工作堆成一个commit。通常每完成一个逻辑完整的子任务就commit一次。比如写完一个接口、修完一个功能bug、调完一个页面样式都会对应一次提交。这样项目的历史就像一份精细的施工日志每一条都能看懂将来出问题定位也快。宁可提交次数多一点也别让一次commit里塞满十几处不相关的改动。第二个习惯是提交信息要有明确的前缀。我习惯用fix:代表修复bugfeat:代表新功能docs:代表文档变更refactor:代表重构chore:代表杂务。这种约定不需要高大上的框架自己团队内部约定好就行配合日志查看扫一眼就知道每个提交的类型。第三个习惯是推送前务必review自己的改动。用git diff看过一遍确认无误再git push。有时候看diff还能发现自己随手留下的调试日志、暂时没用的注释、意外改动的配置这些在推送到远程之前清理掉能省去后面不少麻烦。第四个习惯跟git关系不大但价值极高定期备份整个仓库目录。即便git本身已经是可靠的版本管家但也架不住服务器硬盘损坏、误删.git文件夹这种极端情况。我见过有人把包含完整历史的.git目录直接删了各种快照全都没了那种无力感真的很难受。所以我每隔一段时间会把重要仓库的.git目录打包备份一份放在另一个位置。这是最后一道保险可以永远用不上但不能没有。git这个东西刚接触的时候会觉得概念多、命令多但只要理解了三区域模型和快照思想主线操作就那么几条剩下的大多是这两个核心理解的延伸。多用、多踩坑、多reflog慢慢自然就熟练了。希望这篇文章能帮正在Linux下摸索git的你少走一点弯路。