
我刚开始接触Git时总觉得它不过是个“多存几个版本”的文件夹直到第一次把分支、合并、回滚这些概念用顺才真正理解它为什么叫“版本控制工具”。如果你也想迈出第一步这篇文章可以当作你的入门地图从Git仓库的底层工作方式讲起到初始化、第一次提交、分支管理、连接远程仓库再到很多人会关心的“怎么删除Git仓库”以及误删后的恢复一条龙走完。它更适合刚接触版本控制、或者长期用“目录复制日期命名.zip”管理代码的同学也适合想把过去积累的零散内容整理成正式项目的人。1. 动手前先搞清楚Git仓库到底存的什么1.1 三个工作区域的底层模型要理解Git仓库最好的类比是“游戏存档机制”。工作区Working Directory就是你电脑上能看到的那个项目文件夹里面所有文件就像游戏里你正在操作的角色状态暂存区Staging Area是存放“准备入档”内容的地方想清楚哪些改动需要记录就先把它们放进这里本地仓库Local Repository则是最终的存档库每次执行提交操作Git会把暂存区内容打成一个快照永久写入本地仓库。这个设计乍看多了一步“暂存”但实际体验下来你会感谢它它把“改代码”和“记录代码”两件事拆开让你可以分批次提交相关改动而不是被迫把一揽子混乱改动全部塞进同一条历史记录里。还有一个概念叫HEAD说白了就是“当前你在哪“的指针。你在不同分支之间跳跃HEAD就跟着指向不同的位置。理解这个以后回滚、切换、恢复这些操作都不再玄乎。1.2 git init与git clone两种起点选对场景创建一个新的无历史项目用git init要基于已有的远程项目开始协作用git clone。两者的选用逻辑很简单从零开始选前者要接手别人的代码库选后者。mkdir my-blog cd my-blog git init执行完这些命令目录下会出现一个隐藏文件夹.git里面装着全部版本数据。可以把它理解成项目专用的“私人档案柜”你写的内容、提交的记录、分支的指针都存放在这里。这个目录很特殊日常使用时基本不需要碰它但后续如果要做备份、迁移或彻底重置它就成为了关键节点。这里给新手一个建议git init之后第一时间看一眼默认分支名。早期很多工具默认是master现在很多平台默认是main。如果你的默认分支不是自己期望的可以立即改名git branch -m main“仓库”与“普通文件夹”的本质区别在于普通文件夹只保存文件的最终结果而Git仓库保存的是文件的导游路线图每一站都能回去。1.3 第一次配置提交人信息与仓库边界Git在提交时会把你的身份信息一并写入历史记录。所以在创建第一个仓库前必须设置用户名和邮箱否则很多工具会直接拒绝提交git config --global user.name 你的名字 git config --global user.email 你的邮箱加--global表示全局生效这样其他仓库也会沿用同一套身份。如果只想针对当前仓库设置不同的身份去掉--global即可。我踩过的一个坑是本地仓库没设置用户信息就匆忙提交结果提交记录里显示的是“Unknown”。有些Git图形化工具在本地会使用系统默认用户名换到协作环境时特别容易造成追溯困难。团队成员看到的每一次提交都需要知道是谁、在什么时候、改了什么这一步值得你多花一分钟做好。另外明确“仓库边界”也很重要一个仓库最好聚焦一个项目。不要把所有琐碎文件堆进同一个仓库否则历史会变得又脏又难查。.git目录存放整个项目的全部版本信息一旦删除就相当于删掉了所有历史存档这件事后面第5章还会重点展开。2. 第一个提交从工作区到仓库2.1 文件状态未跟踪、已暂存、已提交创建好新仓库之后随便放一个文件进去然后运行git status你会看到类似这样的输出Untracked files: (use git add file... to include in what will be committed) README.md这里的“Untracked”意思是Git知道这个文件存在但还没有开始正式跟踪它的变化。此时你的选择很简单git add README.md git status再看status文件已经进入“Changes to be committed”也就是暂存区。改动进入暂存区后执行提交这一步动作。git commit -m docs: 初始化项目说明文档提交完成就像完成一次游戏存档。此后你又改了几次文件只要重新git add再git commit每一个关键节点就都留下来了。我个人对刚接触Git的人的忠告是不要动不动就git add .把所有东西一股脑推上去。有点耐心用git add -p可以按块选择改动把无关的调试代码从提交里剔除掉。提交记录越清晰未来的自己越感谢现在的自己。2.2 提交信息的规范与“后悔药”提交信息写的质量如何决定你三个月后回查历史时的体验。我看过太多“update file”、“aaaa”、“new”这种提交信息过几天就完全看不懂改了什么。这里给出一套可以直接抄作业的写法git commit -m feat: 新增用户登录功能 git commit -m fix: 修复未登录时跳转首页的异常 git commit -m docs: 更新API接口文档比如feat表示新功能fix表示修复bugdocs表示文档style表示代码格式调整refactor表示重构。动词开头、直白简洁是团队协作的基本礼仪。如果提交完发现“哎呀信息写错了”或者“少提交了一个文件”在还没有推送到远程仓库的前提下不需要惊慌可以用带修正功能的提交覆盖上一次提交git add README.md git commit --amend -m feat: 初始化项目补充说明文档--amend并不是多了一条提交而是把上一次提交记录原地修改掉类似“重新存档”。需要注意的是如果这次提交已经推送到了远程仓库就要慎重使用--amend因为这会改变提交历史可能导致远程分支出现分叉影响其他协作者。2.3 用.gitignore给仓库“瘦身”新建仓库后的第二件大事就是编写.gitignore文件。这个文件告诉Git哪些文件不要跟踪。为什么需要它举个现实例子前端的node_modules依赖目录动辄几百兆后端的target编译输出每次构建都会变动登录日志、临时配置、IDE自己的设置目录比如.idea或.vscode这些内容全都塞进仓库不仅让仓库体积暴涨还会让之后的每次提交都充满噪音。一份基础的.gitignore看起来通常长这样# 依赖目录 node_modules/ vendor/ # 编译产物 dist/ build/ target/ # 日志与临时文件 *.log *.tmp # 本地配置文件可能包含敏感信息 .env.local config.local.ini # IDE配置 .idea/ .vscode/用来匹配的规则并不复杂#开头是注释/结尾代表目录*代表通配符匹配任意文件名如果想要强制忽略某类文件里的某一个例外用!取反。这里有一个新手最容易踩的坑如果某个文件已经被Git跟踪了之后再在.gitignore里添加它并不会立刻生效。Git不会主动放掉已经盯上的文件你需要先把它从跟踪列表里移出来git rm --cached .env.local--cached表示只把文件从Git跟踪列表中移除保留磁盘上的文件本身这样既不影响本机使用又能让忽略规则真正生效。3. 分支操作让历史变成平行宇宙3.1 分支的本质一个会移动的指针很多人误以为分支是复杂的数据结构其实Git里的分支本质上只是一个指向某次提交的指针。创建新分支就是创建了一个新指针切换分支就是让HEAD指向另一个指针。理解这一点你就能明白为什么Git切换分支如此轻量。因为我们切换的是指针基本不做文件拷贝操作。当然工作区文件内容会随之变化但仓库本身的数据都是共用、共享的这也是Git与老派版本控制工具相比最大的体验优势。默认分支通常是main它是整个项目的基线版本。如果想要试验新功能不想影响主流程可以创建一个独立分支改坏了也不怕直接删除分支就当一切没发生过。3.2 分支管理完整流程创建、切换、合并、删除一个非常典型的流程如下# 从当前分支创建并切换到一个新分支 git switch -c feature-login # 查看当前分支列表 git branch # 在新分支上完成一系列修改与提交 git add . git commit -m feat: 完成登录表单校验 # 切回主分支 git switch main # 把新功能分支合并回主分支 git merge feature-login创建分支为什么敢起名叫“平行宇宙”因为在main上开发的人不会因为你新分支的改动而受影响两条线就像两条平行世界线你可以在其中一条安心做实验确认稳定后再合并回主线。合并时可能遇到两种典型模式。一种是“快进模式”即你当前分支没有产生新的提交直接把指针往前移动毫无波澜地完成合并。另一种是“三方合并”两个分支都各自有新提交Git会把它们汇成一个新的合并提交。前者操作简洁后者历史更丰富。如果分支用完不再需要可以这样清理git branch -d feature-login如果这个分支的提交还没有被合并Git会拒绝删除并提示。要是你有把握确定内容不要了再用大写-D强制删除。3.3 合并冲突正面应对一次就懂合并冲突听起来吓人但它只是“同一行的代码两边各自写了不同版本”Git实在不知道该听谁的只能把人拉过来裁决。举个最简单的例子两个分支都改了conflict.txt的第一行执行git merge时Git会在文件里写下冲突标记 HEAD 这是 main 分支的内容 这是 feature-login 分支的内容 feature-login你需要做的很简单打开文件把、、这些标记删掉把最终想要保留的内容留下来然后git add conflict.txt git commit -m merge: 解决登录分支与主分支的冲突核心心态是冲突不是灾难而是Git在保护你的工作成果。它没有直接覆盖任意一方而是把选择权交到你手上。对付冲突的最好办法是平时保持提交小而清晰这样冲突波及范围就大概率限于几行。4. 连上远程仓库从单机走向协作4.1 关联远程地址与SSH连接配置本地仓库用得再顺手也解决不了换电脑、团队协作、备份到云端这些问题。连接远程仓库的第一步是把自己电脑上的公钥注册到托管平台常见的有GitHub、GitLab、Gitea等。ssh-keygen -t ed25519 -C 你的邮箱接着一路确认即可。生成的公钥默认在~/.ssh/id_ed25519.pub把文件内容复制到托管平台的SSH Keys设置里。之后使用SSH方式访问仓库就不再需要每次输入密码。关联远程仓库只需执行git remote add origin gitgithub.com:你的用户名/你的仓库名.gitorigin只是远程仓库地址的一个默认别名你可以叫他任何名字但业内约定俗成的叫法就是origin。用git remote -v查看当前关联的地址是否符合预期。这里有必要多说一句HTTPS方式的远程地址也能用但每次推送都要输账号密码或令牌SSH方式配置一次之后一路顺畅是长期开发战线里最省心的选择。4.2 推送、拉取与远程跟踪分支关联好远程仓库后就可以把本地提交推上去了git push -u origin main首次推送加上-u参数作用是把本地main分支与远程main分支建立“上游跟踪关系”。以后直接执行git push和git pullGit就自动知道该对上哪个远程分支。这里有一个很关键的概念叫远程跟踪分支比如origin/main。它跟本地main不是同一个东西它相当于你从远程同步下来的“远程仓库的最近状态”只在git fetch或git pull时更新。本地main是你在自己机器上开发的分支。如果从远程拉取最新内容git pullgit pull的本质是两步合并执行先在本地更新origin/main然后把更新合并进你当前所在分支。如果担心多出一个无意义的合并提交可以用git pull --rebase这条命令会把你在本地的提交“挪”到远程最新提交之后形成一条更线性的历史。但要注意git rebase适用于还没推送到远程的本地提交如果分支已经被别人拉了再去改写历史就容易引发混乱。5. 删除Git仓库的完整指南含误删恢复5.1 删除本地仓库一句话的清空“删除Git仓库”最粗暴的理解就是把本地仓库整个销毁。之前说过仓库一切数据都在.git目录里所以删除本地仓库只需一条命令rm -rf .git执行完毕后当前目录下的项目文件一个不少但Git历史、分支、暂存内容全部清空目录就退化成了一个普通文件夹。不过这条命令极其危险必须谨慎确认一旦删除所有分支历史、提交记录、标签全部消失而且无法从本地恢复。如果你只是想“摆脱Git但不能丢代码”可以选择删除.git目录如果你只是想“清空内容但保留历史”那绝对不是这个操作。更稳妥的方式是在真正执行前对历史记录做一次备份git bundle create backup.bundle --all生成一个backup.bundle文件以后随时可以用它恢复全部提交记录相当于给整个仓库买了一层保险。5.2 删除远程仓库不是“删个文件夹”那么简单本地删除只是影响你自己的电脑远程仓库删除则是把所有协作者同步的数据一起清掉。在GitHub这类托管平台上删除项目通常需要进入项目的Settings找到Danger Zone区域在页面最底部选择删除仓库。删除前平台会要求输入仓库名称确认这一步设计得很实用就是为了防止误操作。删除完成后仓库的提交、Issue、Pull Request、Release等全部资料永久消失绝大多数平台都不可自助恢复。所以如果你还想要里面的任何内容删除前记得先导出备份或用git clone拉一份完整仓库到自己机器上。如果只是想让本地项目不再与远程关联而不动远程数据则稳妥得多git remote remove origin记住本地仓库和远程仓库是两个独立对象本地删除不碰远程远程删除也会让本地成为“无源之水”。操作前想清楚这一点就不会慌。5.3 误删后的恢复reflog与孤儿提交抢救很多人最关心的问题是删错了仓库或者误删了分支还能救回来吗答案基本是“能”尤其是刚动手时。Git有个隐藏的救命机制叫reflog它记录的是一段时间内HEAD和分支指针的每一次移动轨迹。默认大概保留90天哪怕是像git branch -D这样的强制删除只要在周期内就有机会找回。git reflog输出会展示类似这样的关键信息d5b0f3a HEAD{0}: commit: fix: 处理登录页布局 a12c9d1 HEAD{1}: checkout: moving from main to feature-login假如你误删了一个分支而最后一次指向它的提交在reflog里还能看到只需执行分支重建git branch feature-login d5b0f3a如果是因为删除了.git目录想恢复方法更依赖备份。除非之前做过git bundle或克隆到其他位置否则几乎无法复原。这就是为什么第5.1节里我极不推荐直接rm -rf .git的原因。误删工作区文件则轻松一些只要改动已经提交过可以随时恢复git checkout -- 文件名这些操作我都在本地实验过很多次可以负责任地说Git的恢复能力比大多数人想象中强得多但它有个前提——历史记录还在且没有被彻底清理。所以真正最重要的动作是对关键仓库常态化推送远程并做好备份。6. 新手高频翻车现场问题排查笔记6.1 高频报错一览表把一些高频问题整理成一份速查表贴在这里可以收藏着随查随用。现象大概率原因处理思路推送到远程被拒绝提示non-fast-forward远程有本地没有的提交先git pull --rebase解决冲突后再推送提交信息写错已经提交但未推送git commit --amend修改信息.gitignore加了还是没效果文件已经被Git跟踪git rm --cached 文件名解除跟踪中文文件名显示成转义字符Git默认用八进制转义显示git config --global core.quotepath false切换分支后文件没变可能误以为改动已提交用git status确认未提交的改动阻止切换或跟着走误删本地分支分支被git branch -D强制删除用git reflog找到最后指向的提交并重建分支合并时冲突标记到处乱放未按规范解决冲突打开文件删除冲突标记再add和commit6.2 代码提交到了错误分支这样补救这个场景太常见了在main分支上随手做了一个修改并提交了但你想让这个改动出现在feature-login分支上。补救方法不算复杂。先用git log --oneline记下这次提交的哈希值然后切到目标分支执行git switch feature-login git cherry-pick 提交哈希cherry-pick是Git里很实用的技能它能把某个提交“摘”到当前分支相当于把改动复制一份过来。注意这里原提交仍然留在了main上需要的话再切回main用git reset --hard HEAD~1把这个提交从错误分支中移除。但这里就要提醒一句git reset --hard会把工作区里未提交的改动一起清空执行前务必确认没有未保存的内容。一旦执行想找回只能靠reflog。6.3 关于团队协作的几条个人习惯无论是使用纯命令还是图形化工具我对刚开始使用Git的新人都有几条源自实战的额外建议。第一提交之前用git diff习惯性检查一遍改动内容。肉眼看过再提交能拦住一大批愚蠢错误比如不小心提交了调试日志或本地密钥。第二尽量让每次提交只解决一个主题不要混合提交。代码评审时小提交比“一坨改动”友好十倍。第三强烈建议写一份包含分支命名规则、提交信息规范、合并流程的团队约定文档哪怕只是在仓库里放一个CONTRIBUTING.md也好。规则一旦书面化协作摩擦就会大幅度下降。我自己带新人的时候会让他们先把init、add、commit、branch、merge、push、pull这套流程在本地仓库完整过三遍再把reflog、cherry-pick这些恢复技能亲手操作一遍。次数多了那些“不小心删了东西”“提交推错了”“分支切岔了”之类的紧张场景就会从惊吓变成家常便饭最终变成条件反射。Git是个需要手感的东西光看不练永远只能停留在“会用几个命令”的层面。