简介:一份面向Web开发入门者及需要掌握版本控制技能的开发者的Git学习笔记,聚焦Git仓库初始化与本地常用操作,系统讲解Git分布式架构、快速与完整性特点、历史记录与回滚作用,并与SVN等集中式版本控制系统作了命令级对比。文档结合git init、git add、git commit、git config等命令,详细演示了在空目录中创建新仓库、将现有项目目录转换为Git仓库、配置全局与局部用户信息的具体操作,并针对工作区、暂存区与提交的关系给出了实践示例,便于在本地项目起步阶段快速建立规范版本管理流程。资源为docx格式,共1个文件,压缩包总大小约26KB,轻量便携可直接阅读。已有114人学习浏览,适合想从零入门Git、理顺本地仓库操作链的读者。使用后可以独立完成仓库搭建、文件跟踪、状态检查与提交归档等操作,为后续分支合并、远程协作打下基础。
1. Git 本地仓库:不是“存文件”,是给改代码留后悔药
写代码最难受的不是报错,而是改了一下午发现思路错了,想退回上午那个能跑的版本,却找不到任何痕迹。Git 的本地仓库就是干这个的:在你电脑上维护一份完整版本历史,让每次提交、每次分支都有据可查。标题里的“Git 初始化与本地仓库操作”,指的就是从安装 Git、git init 建立仓库,到提交文件、查看历史、管理分支这一整套不依赖服务器的动作。它适合一个人开发、还没上团队协作的初学者,也是把版本管理跑通的最小闭环。把这套动作练熟,你就有了写代码的后悔药——随便折腾,总能找回上一个能用的版本。
2. 从零装 Git 到第一次 git init:环境、身份与仓库骨架
2.1 三端 git 安装与第一道验证:装完先看 git --version
很多人拿到 Git 第一步不是装环境,而是先去找可视化工具。我建议反过来:先装好命令行 Git,把 git 命令跑通,再决定要不要套图形界面。因为无论你之后用 VS Code 的源代码管理、SourceTree 还是 JetBrains 系列,底层调用的都是这套命令行 Git。命令行能跑通,图形界面才能不踩“库初始化失败”“项目初始化错误”这类环境问题。常见做法是按操作系统分开装:
| 平台 | 安装方式 | 日常操作终端 |
|---|---|---|
| Windows | 下载 Git for Windows 安装包,一路默认选项即可 | Git Bash |
| macOS | brew install git,或官网 pkg 包 | Terminal |
| Linux | sudo apt install git(Debian/Ubuntu)、yum install git(CentOS) | Terminal |
Windows 上我一般默认选 Git for Windows,装完开始菜单里会出现 Git Bash。它模拟了一套 Linux 风格 shell,补全、路径、换行处理都更顺手。不建议在 cmd 或 PowerShell 里硬用 git,Windows 原生命令行对单引号、通配符的处理和 Git 期望的行为不一致,遇到奇怪报错很难排查。
装完第一道验证不是打开界面,而是在终端里执行:
git --version如果输出git version 2.xx.x这样的信息,说明安装成功。这一步能过滤掉大部分“装完不知道怎么进环境”的困惑。如果提示command not found,多半是安装时没把 git 加进 PATH,或者装完没有重开终端。重开终端再看一次,不行就检查安装选项里“Add to PATH”是否勾选。
2.2 git init:仓库是怎么“长”出来的
环境就绪后,初始化本地仓库只需要一条命令。我一般会先建一个专门的项目目录,再进去执行初始化,避免把仓库建在乱七八糟的目录里。演示完整过程:
mkdir my-project # 建项目目录 cd my-project # 进入目录 git init # 初始化本地仓库 ls -la .git # 查看仓库骨架git init会在当前目录生成一个.git子目录,这就是整个本地仓库的本体:里面存着提交对象、分支指针、配置信息和钩子脚本。HEAD记录当前所在分支,config存仓库级配置,objects存提交和文件内容对象,refs存分支和标签指针。初学者不需要逐项理解,但要知道一条原则:.git目录坏了,版本历史就悬了,日常别去手动删里面的文件。
注意,git init 只负责“建仓库”,不会自动提交任何文件,也不会把当前目录已有的文件纳入版本管理。如果你在一个写了一半的项目目录里执行 git init,git 只会安静地把仓库建好,然后等你用 git add 和 git commit 把文件装进去。这跟很多人印象里“初始化就是建好完整项目”不一样,是新手常见的理解偏差。这一步不产生任何系统级初始化动作,纯粹是给当前目录挂上一套版本管理外壳。
想确认当前目录是否已经是仓库,可以用git rev-parse --is-inside-work-tree,输出true说明在仓库内。这个命令在排查“明明执行了 git 却提示 not a git repository”时很好用,一般是目录进错了,或者仓库建在了上级目录。
2.3 提交前必须配置身份:user.name 与 user.email 的作用范围
仓库建好,先别急着提交。Git 要求每次提交带作者身份,没配置就提交会直接报错,提示Please tell me who you are。常见做法是用全局配置一次性设好,后续所有仓库默认继承:
git config --global user.name "你的名字" git config --global user.email "you@example.com" git config --listgit config --list会列出当前生效的全部配置,用来验证是否写进去了。如果看到user.name和user.email,说明身份配置完成。这里有个细节:user.email 不一定要用真实邮箱,但要填一个长期能用的地址;将来接远程托管平台时,这个邮箱最好和平台账号一致,否则提交记录里的头像和账号关联不上,看着很别扭。
配置的作用范围分三档:--system作用整台机器所有用户,--global作用当前系统用户的所有仓库,--local只作用当前仓库。默认不写参数时,git config 操作的是--local。我一般习惯把姓名、邮箱这种通用信息放--global,把某个仓库特有的配置放--local,这样换项目时不会被上一份工作的名字污染提交记录。这就是一套最精简的 git 安装及配置流程,后面所有仓库都会继承这套身份配置。
排查配置被“覆盖”时,git config --list --show-origin很有用:它会列出每条配置来自哪个配置文件,直接看出是全局配置生效还是仓库配置把全局值盖掉了。这个命令在处理“我明明设置了用户名,提交时却显示别人的名字”这类玄学问题时,比挨个文件翻快得多。
身份配置完成后,初始化本地仓库的最小闭环就是装 Git、git init、配身份。到这里还没涉及远程仓库,git clone、git remote 这些命令都属于“连远端”阶段,等真正需要和远程协作时再学也不迟。本地这套动作跑顺,后面接远程仓库时会自然得多。
3. 本地仓库的日常循环:status、add、commit 与 log
3.1 先拆黑匣子:工作区、暂存区、仓库三块地怎么协作
Git 本地仓库的文件状态不是“改了/没改”二元制,而是三块区域之间的流转:工作区、暂存区、仓库。工作区就是你磁盘上看到的真实文件;暂存区是一个中间缓冲区,git add 把工作区的改动装进去;仓库是.git目录里存储的已提交版本。commit 的动作,是把暂存区的内容固化成一次版本快照写进仓库。
把这个模型想成“草稿箱”最好懂:文件先在工作区改,改完用 git add 放进草稿箱,确认没问题后 git commit 正式发出去。很多人第一次用 Git 会直接跳步,改完文件就 commit,结果发现 commit 什么都没记录,就是因为没先 add。这不是 Git 的缺陷,而是设计上故意把“改”和“提交”分开,让你在提交前有检查的机会。
三块区域的边界状态直接影响后续命令的输出。文件可能处于未跟踪、已暂存、已修改、已提交四种状态。搞不清这些状态,你只会在 git status 的输出里来回猜。这也是我把 status 放在所有操作前面讲的原因:它是一面镜子,能直接告诉你文件现在站在哪块区域。
3.2 git status:读不懂状态就动手,早晚翻车
在项目里新建一个 README 文件,然后跑一次 status,是最直观的入门方式:
echo "# demo" > README.md git status输出里 README.md 会出现在 Untracked files 段落,意思是这个文件还没被 Git 跟踪。此时它只在工作区,git 知道它的存在,但不知道它属于版本历史。执行git status -s得到紧凑格式:?? README.md,??表示未跟踪。紧凑模式在文件多的时候特别好用,不会刷满屏。
如果文件已经被跟踪且修改过,status 会显示 Changes not staged for commit,意思是工作区有改动,但这改动还没进暂存区。此时git diff能看具体改了哪些行。如果文件已暂存但还没提交,会显示 Changes to be committed,此时git diff --cached能看暂存区里将要提交的内容。
我见过不少新手每次提交前不看 status,直接 add 全部文件然后 commit,结果把临时文件、日志文件一起提交进去,仓库越来越脏。养成提交前先git status看一眼的习惯,能避免绝大多数“提交了不该提交的东西”的后悔场景。这不是技巧,是纪律。
3.3 git add 的三种写法与撤销暂存
git add 是决定“哪些改动进入这次提交”的动作,写法不同,口径不同:
git add README.md # 精确加一个文件 git add . # 加当前目录下所有改动 git add *.md # 加所有匹配 .md 后缀的改动git add .最常见,也最容易出事:它会一次性把所有改动都装进暂存区,包括你不想提交的调试输出文件。我一般建议在文件数量少或明确知道改动范围时,用精确文件路径;改动多时先git status扫一遍,确认没有可疑文件后再git add .。git add *.md这种通配符写法适合批量操作同一类型文件,注意它遵循 shell 的通配规则,不是 git 自己的过滤规则。
如果 add 加错了,撤销暂存用:
git restore --staged README.mdgit restore --staged把文件从暂存区移回工作区,文件内容保持不变。注意不要用老教程里的git reset HEAD <file>来干这件事,功能类似,但 restore 语义更清晰,也更符合现代 Git 的操作习惯。撤销暂存后,git status 会把它重新归到未暂存或未跟踪的段落,这时候再重新 add 正确文件就行。
3.4 git commit 与 git log:提交历史的“记账本”
暂存区准备好后,提交操作本身很简单:
git commit -m "docs: 添加项目说明" git log --oneline --graph-m后面跟提交信息。提交信息是给未来的自己看的,我习惯用动词开头,比如 fix: 修复登录按钮失效、feat: 新增导出功能、docs: 补充部署说明。前缀用来区分改动类型,在 log 里扫一眼就能看出项目演进脉络。如果提交时忘写-m,Git 会打开一个编辑器让你输入信息;在 Git Bash 里默认可能是 Vim,新手容易卡在“不知道怎么保存退出”。要么记住 Vim 的:wq退出,要么老老实实每次都带-m。
git log --oneline --graph是查看提交历史的常用姿势:--oneline把每次提交压成一行哈希加信息,--graph用字符画出分支走向。对一个本地仓库来说,看到整洁的提交历史,说明前面 add 和 commit 的节奏是对的。想看某次提交具体改了什么,用git diff HEAD~1对比上一次提交,用git diff HEAD --stat只看改了哪些文件。这些都是本地操作,不会影响任何远端,完全可以放心试。
这个章的节奏很关键:status 看状态,add 选内容,diff 检查,commit 固化,log 回顾。按这个循环走,本地仓库的基本盘就稳了。
4. 分支与本地仓库管理:一切操作先开分支,心里不慌
4.1 分支是平行世界:创建、切换、查看
本地仓库操作里,分支是最能体现 Git 优势的部分。分支可以理解为平行世界:在 feature 分支上随便折腾,不影响主分支的稳定版本。创建并切换分支:
git branch feature-login # 创建分支 git checkout -b feature-login # 创建并切换分支 git branch -v # 查看本地所有分支和最近提交git checkout -b是创建分支最顺手的写法,一步完成“创建+切换”。git branch -v会列出本地分支和各自指向的提交,方便确认当前在哪个分支上。当前分支用git branch输出里的*号标识,也可以用git status第一行查看。有个习惯值得从一开始就养成:改代码之前先确认自己在哪个分支,不要在 main 分支上直接写新功能。
创建分支的默认基准是当前 HEAD。新仓库第一次提交后,Git 默认分支名在较新版本里是 main,早期版本是 master;如果你本地的默认分支名和团队不一致,可以用git branch -m main master改名。这里不展开,只是提醒你看到分支名不同时别觉得是出错了。现代 Git 也推荐用git switch替代checkout做分支切换,git switch -c等价于git checkout -b。老教程多用 checkout,两个都能用;我习惯按老命令讲,因为网上存量资料里 checkout 出现频率高,你看到旧命令能看懂就行。
分支本质上是一个指向提交的指针,创建分支就是新建一个指针,成本极低,几乎不占空间。所以“开分支”这件事在本地仓库里应该被当成零成本操作来用:每做一个独立需求、独立修复,都可以先开一个分支,做完再合并回去。等你需要处理多个并行改动时,这个习惯能帮你避免“改到一半发现另一个需求也要改同一个文件”的狼狈。
4.2 git merge:合并结果和一次经典冲突处理
合并分支用git merge。常见做法是切回接收方分支,再合并进来:
git checkout main git merge feature-login如果两个分支改动互不干扰,merge 会直接成功,Git 自动生成一次合并提交,log 里能看到分支汇入的痕迹。如果两个分支改了同一个文件的同一区域,就会产生冲突。冲突时 Git 不会自动帮你定夺,而是把冲突标记写进文件:
<<<<<<< HEAD 当前分支的内容 ======= feature-login 分支的内容 >>>>>>> feature-login看到这种标记不用慌,手动编辑文件,决定留下哪部分,删掉冲突标记,然后:
git add index.html git commitmerge 后提交的是“合并结果”,不是冲突文件本身。冲突体验第一次总是有点吓人,但处理几次就熟:Git 把两边的差异原样摆出来,你当裁判,决定谁留下或都保留。处理冲突的关键是别慌,也别用git checkout -- .把整个目录重置,那样会把另一边的改动一起丢掉。如果 merge 到一半发现合错了,或者冲突太多不想手动处理,可以用git merge --abort取消合并,回到 merge 之前的状态。这是一个很实用的后悔药,别硬着头皮合完才后悔。
合并前我会习惯先确认当前分支和工作区状态:git status显示工作区干净再 merge。否则带着未提交的改动去 merge,容易把状态搅乱,出现“合并完我改动不见了”的错觉。这个习惯能避开大多数本地仓库里的灰色问题。
4.3 本地仓库的后悔药:reset、restore、stash 怎么选
本地仓库操作里,最常被问“我操作错了怎么回退”的,就是 reset 和 stash 这套。reset 三个模式有明确差异:
| 命令 | 作用 | 工作区 | 暂存区 |
|---|---|---|---|
| git reset --soft HEAD~1 | 撤销提交 | 保留 | 保留 |
| git reset --mixed HEAD~1 | 撤销提交(默认) | 保留 | 清空 |
| git reset --hard HEAD~1 | 撤销提交 | 清空 | 清空 |
HEAD~1表示当前提交的上一次。--soft只是撤销了提交动作,改动都还在暂存区里,适合“提交后发现漏了东西”。--mixed是默认行为,提交被撤销,改动退回工作区,适合“提交早了,还想再改改”。--hard会把提交、暂存区、工作区一起恢复到指定版本,工作区里未提交的改动会永久丢失。这条命令是真正意义上的翻车点,用之前一定要确认工作区没有要保留的东西。
注意:reset --hard 会清空工作区未提交的改动,执行前先 git status 确认。
restore 系列则更细粒度:git restore --staged撤销暂存,git restore <file>把工作区文件恢复到暂存区或 HEAD 的版本。它不会移动 HEAD,只针对单个文件,比 reset 安全得多。日常“改错了一个文件想还原”,第一反应应该是 restore,而不是 reset。restore 和 reset 的分工可以用一句话记:restore 是“改了某文件要还原”,reset 是“提交历史的回退”。restore 操作的是工作区和暂存区的文件内容,reset 操作的是 HEAD 指针和提交历史。改坏一个文件,第一反应应该是 restore;提交错了要撤销,才轮到 reset。
临时要切换分支但手头改动还没做完,就用 stash 把工作区暂时存起来:
git stash push -m "登录功能做到一半" git stash list git stash popgit stash push把工作区改动打包存进 stash 列表,git stash pop再把最近一次 stash 弹回工作区。存的时候带-m写清备注,否则 stash list 里全是匿名条目,过几天根本分不清哪条是哪条。stash 的定位是暂存,不是长期保存;放太久不处理,和分支混在一起容易造成混乱。
5. 本地仓库操作避坑:5 个真实踩过的“玄学”
这一章整理 5 个在本地仓库操作里高频出现的疑难杂症,每一个都是我实际踩过或帮人排过的。它们看起来像代码问题,本质上都是状态、配置或环境问题。
5.1 现象:.gitignore 明明写了规则,文件还是被提交
原因:.gitignore 只对“未被跟踪”的文件生效。如果文件已经 git add 过,它已经在 Git 的跟踪列表里,之后在 .gitignore 里补规则不会把已跟踪文件踢出去。这是整个 gitignore 问题里最常见的误解,很多人以为写进去就自动被忽略,结果提交后文件还是进了仓库。
解决:把已跟踪文件从索引移除,保留工作区文件:
git rm --cached <file> git add .gitignore git commit -m "chore: 停止跟踪不需要的文件"git rm --cached只删除索引记录,不删磁盘文件。处理后文件进入未跟踪状态,.gitignore 规则才开始生效。.gitignore 的规则本身不难:build/忽略整个目录,*.log忽略后缀,!important.log表示例外保留。注意例外规则只有在父目录没被忽略时才有效。建议规则从简单开始,按需补充,不要一开始抄一大份通用的,容易把该提交的文件也忽略掉。新项目一开始就把该忽略的目录写进 .gitignore,比如临时文件、构建产物、IDE 配置,比事后补救省心得多。
5.2 现象:Windows 下只改一行代码,git status 显示整个文件都变了
原因:换行符。Windows 用 CRLF,Linux/macOS 用 LF。Git 默认会把仓库里的 LF 在检出时转成 CRLF,提交时再转回 LF。如果同一文件里既有 CRLF 又有 LF,或 core.autocrlf 配置不一致,git 会把整个文件的换行符差异当成内容差异,于是只加了一行却显示“整个文件被改”。这是我在 Windows 上踩过的血泪经验,属于典型的跨平台换行符翻车。
解决:Windows 上统一设置core.autocrlf true,仓库内部统一存 LF:
git config --global core.autocrlf truemacOS/Linux 一般设input:检出时转 LF,提交时不转。更彻底的做法是在仓库根目录加.gitattributes文件,写上* text=auto,让 Git 自动按文本类型处理换行。验证某个文件到底是内容变了还是换行符变了,可以用git diff --ignore-space-at-eol看排除行尾差异后的结果;如果排除后没有实际内容差异,那基本就是换行符的锅,调整 autocrlf 配置后重新提交即可。注意,历史文件已经混入 CRLF 的仓库不要一次性做全量转换,否则一次提交会带上大量无关改动;要转换就单独安排一次格式化提交,并在提交信息里写清楚,避免污染历史。
5.3 现象:commit 提交后才想改注释,或者发现漏了一个文件
原因:提交已经生成,但还没推送到远程,本地仓库范围内其实完全有后悔余地。犯错的不是“写错注释”,而是不知道本地提交可以安全改写。
解决:用 git commit --amend 修正上一次提交:
git commit --amend -m "修正后的注释" git add forgotten.txt && git commit --amend --no-edit-m会替换原提交信息;--no-edit表示保持原注释不变,只补充文件。amend 的本质是生成一次新提交替换旧的提交,所以提交的哈希会变。在本地仓库里这样做没有任何风险;但如果提交已经推送到远程,就不要用 amend 去改公共历史,否则别人 pull 下来会看到分叉历史。这个原则要记住:本地随便改,远程需谨慎。另外 amend 只能修补最近一次提交;如果想改更早的提交,就需要 rebase 这类会重写历史的操作,本地仓库里能做,但那是一个更大的话题。先把 amend 用熟,它覆盖了八成的“刚提交就后悔”场景。
5.4 现象:git stash list 是空的,但明明记得自己存过东西
原因:stash 是跟着仓库走的,不存在跨仓库的 stash。换了一个目录执行 git stash list,自然什么都看不到;还有一种可能是 stash pop 时被弹出后,又因为后续操作被覆盖或丢弃。stash 列表清空了不代表数据立刻消失,但抢救窗口比较短。
解决:先确认在正确的仓库目录下执行git stash list。如果确实弹丢了,可以尝试用git fsck --no-reflogs找悬空提交:
git fsck --no-reflogs | grep commitgit fsck会列出仓库里没有被引用但仍存在于对象库里的提交对象,配合git log查看这些悬空提交的内容,找回 stash 或误删分支的场景都能救回来。这个命令有点冷门,但它是 Git 本地仓库在数据层面最后的兜底,值得记下来。为了避免 stash 找不到,我一般随手就 pop,不在 stash 里囤东西。stash 的用途是“临时避让”,不是“第二份工作区”;长期不用的改动,应该开一个分支放进去,而不是一直压在 stash 里。
5.5 现象:Git Bash 突然报 git open /dev/null or dup failed
原因:在 Windows 上长时间开着大量编辑器、终端和 Git 进程时,文件句柄耗尽会出现这个错误;另一种常见情况是当前终端所在的目录已被删除,Git 无法创建临时文件。这个报错信息看着像代码问题,实际上跟代码没有任何关系,属于运行环境层面的句柄问题。
解决:先关掉不用的编辑器、多余的 Git Bash 窗口,cd到一个真实存在的目录里,再重开终端执行 git 命令。这个报错在 Git Bash 里偶发,在 cmd 里一般不出现;遇到先看是不是终端卡死,重开一个干净的 Git Bash 窗口往往直接解决。频繁出现就考虑升级 Git for Windows 版本,新版对 Windows 句柄管理更稳。别浪费时间去查代码或改仓库配置,这个坑和运行状态有关,不是仓库数据问题。
6. 用 git reflog 找回“以为删掉的提交”:本地仓库的底线验证
前面讲的 reset、restore、stash 说到底都是“还没彻底丢”的操作。真正让人心里没底的场景是:reset --hard 之后连 log 里都看不到了,是不是就没了?答案是不一定。Git 还有一个记录所有 HEAD 移动痕迹的机制,叫 reflog。它记录的是 HEAD 在本地仓库里的每一次变动,即使提交被 reset 掉,reflog 里还留着那个提交的哈希。
演示一下完整找回流程:
echo "v1" > app.txt git add app.txt git commit -m "app v1" git reset --hard HEAD~1 # 模拟误操作,app.txt 没了 git reflog # 找到 app v1 对应的哈希 git cherry-pick <hash> # 把那次提交恢复到当前分支git reflog输出里能看到HEAD@{0}、HEAD@{1}这样的条目,每条对应一次 HEAD 移动。“app v1”这次提交虽然从 log 里消失了,但 reflog 里还留着它的哈希,用git cherry-pick把这个提交的改动在当前分支上重新生成一次,文件就回来了。cherry-pick 的作用是把指定提交的改动应用到当前分支,相当于“把丢掉的改动捡回来”。
注意:reflog 条目默认保留 90 天,执行 git gc 会按配置清理。
所以发现误删之后,越早动手找回成功率越高;隔几个月再想来处理,数据可能真的被回收了。我不建议把 reflog 当常规操作天天用,但它存在的意义是让你敢在一个本地仓库里放手实验:只要没跑 git gc,多数误操作都有后悔药。
我现在养成的习惯,是每天下班前在项目目录里跑一次git status,看一眼有没有改到一半没提交的文件;提交前再用git diff HEAD快速检查一遍改了什么。这个习惯帮我省掉了大量“我昨天改的呢”的找补时间。Git 本地仓库这套东西,真正的价值不在于命令多熟,而在于养成一种“改动有记录、回退有依据”的工作方式。希望帮到你。
本文还有配套的精品资源,点击获取