第3章-Git基础


写代码最大的噩梦是什么?不是写不出来,而是写崩了。昨天还能跑的代码,今天一顿乱改,全废了,想回到昨天?没门。Git 就是来解决这个问题的:它是代码的时光机,可以在任意时刻给代码拍一张"存档照",崩了随时读档。

它还是备份——代码不会因为电脑坏了而丢。它更是协作工具——以后多人一起开发一个项目,Git 负责把大家各自的改动合并起来。

还有一个现实原因:招聘时,GitHub 上(一个托管 Git 仓库的网站)的代码记录就是你的求职作品集,面试官会看。所以从现在开始,每写一点代码就提交一次,养成习惯。今天不要求你背 Git 原理,只要求:会存、会取、会看历史,听得懂"冲突"这个词。

Git 和普通备份有什么区别

普通备份 = 把文件复制一份存起来,像照片打印出来收在抽屉里。它的缺点很明显:占地方、分不清哪份最新、改了什么全靠自己回忆。

Git 备份更像拍电影而不是拍照片:它记录的是"每一次改动"和"改动了什么",每个版本都认识自己的爸爸,能随时跳到任意一版。

对比 手动复制备份 Git
记录了什么 某一时刻的全部文件 每一次的改动内容
能回到任意历史版本吗 难,分不清版本 能,一键跳转
能知道谁改了什么吗 不能 能(commit 记录)
多人协作 没法合并 专门干这个

先确认电脑里装好了 Git(周四还会重点讲安装,今天先验证):

git --version

输出(版本号可能不同):

git version 2.43.0.windows.1

能输出版本号,说明 Git 可用。如果提示"不是内部或外部命令"或"command not found",先记住这个现象,周四安装环境时一起解决。

两条约定先立好:Git 命令都以 git 开头,后面跟子命令(git init、git add……),少写 git 前缀就变成普通命令,会报错;Git 只对"被它管理的文件夹"有效,第一次用必须先 git init,不然所有 git 命令都报"不是 git 仓库"。

三个区:工作区、暂存区、仓库

Git 把代码分成三个"区域",新手必懂,因为 git 命令全在三个区之间搬东西:

概念 怎么理解 命令操作
工作区 你正在编辑、看到的那堆文件,改动在这里发生 正常写代码就行
暂存区 "准备保存"的缓冲区,像购物车,先挑好要买的再一起结账 git add
仓库 已经永久存档的版本,像仓库货架,放上去就带时间戳 git commit

流程就是:工作区改动 →(git add 放进购物车)→ 暂存区 →(git commit 结账入库)→ 仓库。

为什么中间要隔一个"暂存区"?因为一次改动可能包含多个文件,你想让它们作为一个整体一起存档,购物车就是帮你"挑好了再一起结账"。

用命令感受一下三个区的存在:

git status

第一次运行、还没提交过的话,输出:

On branch main

No commits yet

nothing to commit (create/copy files and use "git add" to track)

翻译成人话:你在 main 分支上,还没有任何存档,工作区目前是干净的。git status 是最常看的命令,每次不确定状态就敲它。

两个容易误会的地方:

  • git add 只是"放进购物车",还没存档,这时后悔还来得及;真正存档是 git commit。
  • 改了文件但没 git add 就 git commit,Git 会提示"没有要提交的改动",因为购物车是空的。所以看到 nothing to commit 不代表没干活,而是"购物车空了、或改动没放进去"。

git init:把文件夹变成仓库

git init(init = initialize,初始化)= 把当前文件夹变成"受 Git 管理的仓库"。只需执行一次,以后这个文件夹里的改动都能被 Git 追踪。

cd 项目文件夹       # 先进入你的项目文件夹
git init           # 初始化仓库

输出:

Initialized empty Git repository in D:/项目文件夹/.git/

再敲 git status,提示会从"不是 git 仓库"变成正常输出,说明托管生效了。

init 之后,文件夹里会多出一个隐藏的 .git 文件夹——它就是 Git 的"账本",所有版本记录都存在里面。不要去动它、不要删它,删了 Git 历史就全没了。

三个分寸:

  • 先 cd 进到正确的文件夹再 init,别在 D 盘根目录乱 init,否则 D 盘全被你"托管"了。
  • .git 是隐藏文件夹,ls 默认看不到,用 ls -a 能看到,但只许看不许删。
  • 一个项目一个仓库:不要在 项目1 里面再对 项目1/子文件夹 init,嵌套仓库会乱套。

git add 和 git status:把改动放进"购物车"

  • git add 文件名:把指定文件放进暂存区(购物车)
  • git add .:把当前目录所有改动一次性放进暂存区(. 表示当前目录,最常用)
  • git status:查看当前状态——谁改了、谁在购物车里、谁还没进

每天的标准动作:改完代码 → git add . → git status 确认 → 准备 commit。看一遍完整的:

# 第一次:新建一个文件
touch 笔记.txt

git status
# 输出要点:
# Untracked files:
#   笔记.txt          ← 提示:有个新文件还没被 Git 跟踪

git add 笔记.txt     # 或 git add . (把所有改动都放进购物车)
git status
# 输出要点:
# Changes to be committed:
#   新文件: 笔记.txt  ← 提示:已经在购物车里了,等着 commit

Untracked(未被跟踪)是新手最常见的词,意思是"这个文件 Git 还不知道它的存在"。git add 之后它就变成"已暂存"(staged)了。

几个坑:

  • git add . 的 . 别漏,git add(不跟任何东西)是另一种用法,会报错。
  • 忘记 add 直接 commit,会提示 changes not staged for commit,翻译过来是"有改动还没放进购物车",先 git add . 再 commit。
  • 想看购物车里具体有什么,git status 会明明白白列出来,养成"add 后必看 status"的习惯。

git commit 和 git log:存档、翻历史

  • git commit -m "说明文字":把暂存区的改动永久保存成一个版本。-m 是 message(留言),后面那句说明文字就是"这个版本改了什么"的日记
  • git log:查看所有存档记录(每次 commit 的作者、时间、留言)

写 commit 留言的好习惯:用一句话说清"这次干了啥",比如 第一次提交、修复了路径报错。以后回看历史,靠留言就能找到对应版本。

git add .
git commit -m "第一次提交:新建笔记文件"

输出:

[main (root-commit) 1a2b3c4d] 第一次提交:新建笔记文件
 1 file changed, 0 insertions(+)

再改一次文件、再提交一次,然后看历史:

# 修改笔记.txt 后
git add .
git commit -m "第二次提交:补充路径笔记"

git log

输出(节选):

commit 5f6g7h8i (HEAD -> main)
Author: 小明 <xiaoming@example.com>
Date:   ...

    第二次提交:补充路径笔记

commit 1a2b3c4d
Author: 小明 <xiaoming@example.com>
Date:   ...

    第一次提交:新建笔记文件

最新的版本在最上面,HEAD 表示"当前所在版本"。

这里有三处容易栽跟头:

  • -m 后面的说明文字必须加引号:git commit -m "第一次" 对,git commit -m 第一次 可能报错。
  • 不写 -m 会进入一个编辑界面(vim),新手进去出不来很慌——永远记得带 -m。
  • 忘记 add 直接 commit 会提示 nothing to commit,先 git add . 再 commit,顺序别乱。

分支:另开一条平行线

分支(branch)是 Git 最强大的功能:在主线之外另开一条平行线路,在分支上随便折腾,不影响主线;折腾好了再合并回主线。

打个比方:主线是"正式出版的书",分支是"草稿本"。你在草稿本上随便写、写坏了扔掉,正式出版的版本一点不受影响。团队开发时,每个人在各自的分支上干活,最后合并。

今天只需要会用三个命令:

命令 作用
git branch dev 新建一个叫 dev 的分支
git checkout dev 切换到 dev 分支(两条分支各自独立)
git checkout main 切回主分支(默认主分支叫 main)
git branch dev          # 新建 dev 分支
git branch              # 列出所有分支,* 号标在当前所在分支
git checkout dev        # 切到 dev 分支
# 输出:Switched to branch 'dev'
git checkout main       # 切回主分支

完整感受一下分支的独立性:

git checkout dev
touch dev版本.py          # 在 dev 分支上新建文件
git add .
git commit -m "dev 分支上的新文件"
git checkout main        # 切回主线
ls                       # 会发现 dev版本.py 不见了!
git checkout dev         # 切回 dev 又出现了

这正是分支的威力:两边互不干扰,各存各的版本。

注意三点:

  • 切分支前最好先 commit 或确认改动已处理,未提交的改动会跟着你"串门"到别的分支。
  • 新旧版 Git 命令不同:老教材教 git checkout,新 Git 推荐 git switch(git switch dev),两者效果一样。今天用 checkout 即可,遇到 switch 别慌。
  • 分支名用英文小写、不带空格:dev、feature-login,别用中文。

回滚:git reset 读档

回滚 = 把代码恢复到之前某个版本。这是 Git 最让人有安全感的功能:写崩了?读档!

最常用的回滚命令:

git reset --hard HEAD~1

拆开看:

  • reset:重置/回滚
  • --hard:干脆利落型,直接把工作区的文件也一起变回旧版(慎用,当前未提交的改动会丢掉)
  • HEAD~1:往回退 1 个版本;HEAD~2 退 2 个版本

--hard 是"彻底读档":连正在编辑的、还没保存的改动也会被覆盖。练习时用测试文件玩,别拿重要代码练手。

git log                 # 记下当前有哪些版本
git reset --hard HEAD~1 # 回到上一个版本
git log                 # 刚才最新的那条 commit 没了
git reset --hard HEAD~2 # 再退一个版本

"后悔药"场景:现在代码是坏的?没关系,回到刚才的正常版本:

git reset --hard HEAD~1

回滚前想清楚这三条:

  • --hard 会丢掉当前未提交的改动,重要代码前先把改动 commit 掉再回滚。
  • 回滚后 git log 里最新的 commit 会消失,这是正常的,不是出 bug。
  • 今天只学"回退"。"跳到指定版本"(reset 到具体版本号)以后用到再学,原理一样。

冲突:Git 的"吵架现场"

冲突(conflict)发生的场景:两个人改了同一个文件的同一行,Git 不知道该听谁的,就把决定权交给你,在文件里标出矛盾。

出现冲突时,文件里会出现这样的标记:

<<<<<<< HEAD
这是你的版本(当前分支的内容)
=======
这是别人的版本(要合并进来的内容)
>>>>>>> dev

<<<<<<<、=======、>>>>>>> 是 Git 留下的"吵架现场",你的任务就是当裁判:

  1. 打开文件
  2. 把 <<<<<<< 到 >>>>>>> 之间多余的内容删掉,只保留你想要的
  3. 把三行标记(<<<<<<<、=======、>>>>>>>)也删干净
  4. 再 git add + git commit 一次,冲突就解决了

冲突标记长这样(示例,不用真的去制造):

def 打招呼():
<<<<<<< HEAD
    print("你好")
=======
    print("Hello")
>>>>>>> dev

解决思路是二选一或自己改,最终文件长这样:

def 打招呼():
    print("你好")    # 保留想要的,删掉标记行

今天只要求听懂概念、会认标记,不要求独立解决。几个要点:

  • 报错里出现 CONFLICT 字样就是冲突,先别慌,这是 Git 的常规操作,不是数据丢了。
  • 解决冲突时必须删掉标记行,只删内容留标记,代码会语法报错。
  • 单人练习时几乎遇不到冲突,遇到也别怕:把报错复制给 AI,让 AI 帮你分析。

随堂练习

新建一个空文件夹 git练习,全程在 Cursor 终端里完成。

  1. 练习一(跑通完整流程):git init → 新建文件 hello.py 并随便写一行 print("hi") → git add . → git commit -m "第一次提交" → git log。 提示:每步后用 git status 看状态变化;log 里能看到你刚才的 commit。

  2. 练习二(感受时光机):修改 hello.py 里的内容 → 再次 add + commit → 数数 log 里现在有几个版本 → 执行 git reset --hard HEAD~1 → 再用 cat hello.py 看文件内容变回哪一版了。 提示:回滚后 log 少了一条、文件内容变旧,这就是"时光机";再用 git reset --hard HEAD~1 回到最开始那版。

  3. 练习三(分支切换):git branch dev → git checkout dev → 新建文件 dev.py 并 commit → git checkout main → 用 ls 看 dev.py 消失了 → 切回 dev 又回来了。 提示:观察"文件出现/消失"的变化,这就是分支隔离;最后切回 main 收尾。

  4. ★ 进阶题(乱改自救):把 hello.py 改成完全跑不了的一堆乱码,commit 一次;然后用一条命令回到还能跑的那个版本,并解释你用了什么命令、为什么有效。

AI 协作提示词

场景:Git 报错看不懂,复制报错问 AI

提示词:
我是 Git 初学者,在 Windows 上执行 Git 命令时遇到问题,报错内容是:
(把报错原文粘贴到这里)
请用大白话告诉我:这个报错是什么意思?我该怎么解决?请给完整的命令。
场景:想加深理解,让 AI 出 Git 练习题

提示词:
我是初学者,刚学完 git init、add、commit、log、branch、checkout、reset 这些命令。
请给我出 5 道操作练习题,难度从简单到进阶,每道题给出"目标"和"提示",
不要直接给答案,让我自己先做。

常见报错速查

报错信息 什么意思 怎么办
not a git repository 这个文件夹还没 git init 先 git init 再执行其他命令
nothing to commit, working tree clean 购物车是空的(没 add 或没改动) 改完文件先 git add .,再 commit
changes not staged for commit 有改动但还没放进暂存区 先 git add .,再 commit
Please tell me who you are / 报错要配置用户名邮箱 Git 不知道你是谁,commit 需要作者信息 运行 git config --global user.name "你的名字" 和 git config --global user.email "你的邮箱"
进入了奇怪的编辑界面(vim) 没带 -m 直接 commit,进入了文本编辑器 按 Esc → 输入 :q 回车退出;以后 commit 一定带 -m "说明"

收个尾

今天的内容串起来就一条线:工作区(改动)→ 暂存区(购物车,add)→ 仓库(存档,commit),顺序不能乱。标准流程是 git init(一次)→ 改代码 → git add . → git commit -m "说明" → git log 看历史,迷路了就 git status。git reset --hard HEAD~1 是"读档键",能回到上一版,但会丢掉未提交的改动,慎用。冲突就是同一行被两个人改了,删掉标记、保留想要的内容即可。

明天装开发环境:Python + VSCode + Cursor,然后跑通你人生第一个 Python 程序。今天 Git 练的 cd、ls 明天全用得上,而且从明天起,每次写代码都要养成 add + commit 的习惯。