第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 initgit 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 addgit 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 switchgit switch dev),两者效果一样。今天用 checkout 即可,遇到 switch 别慌。
  • 分支名用英文小写、不带空格:devfeature-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 devgit checkout dev → 新建文件 dev.py 并 commit → git checkout main → 用 lsdev.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 statusgit reset --hard HEAD~1 是"读档键",能回到上一版,但会丢掉未提交的改动,慎用。冲突就是同一行被两个人改了,删掉标记、保留想要的内容即可。

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