生成式软件工程 · 领学讲义
搜索

Git:给项目存档

会按 Ctrl+Z 撤销一步,就够理解 Git 为什么存在。

这一页讲什么
  • 从「我想回到昨天能跑的版本」这个需求,推出为什么需要版本管理;
  • Git 里的仓库、提交、分支分别对应什么;
  • 五个够用的命令,配上完整输出;
  • 哪些文件不该提交,以及误把密码传上去之后为什么删文件没用。

一、从一个真实场景开始

假设你在写一个程序,昨天能正常运行。今天改了一上午,现在一运行就报错。你想回到昨天那份, 于是打开回收站、翻历史记录、找备份……都找不到。

多数人的第一反应是复制一份目录,改名叫「作业-v2」「作业-最终版」「作业-最终版-2」。 一两个版本还行,做到第五个版本你会遇到三个问题:

版本管理(version control)就是专门解决这三个问题的一类工具,Git 是其中用得最广的一个。

生活类比 写论文时你会按 Ctrl+S 保存,但保存只会覆盖当前内容。Git 相当于给整个文件夹拍快照: 每拍一张,之前那张还留着,你可以随时翻回任何一张,也能看到两张之间具体改了哪几行。

二、Git 的核心想法:存快照

Git 不管「第几个版本」,它存的是快照(snapshot):某个时刻,纳入管理的所有文件分别是什么内容、放在哪个位置。

每拍一张快照叫一次提交(commit)。一次提交会记下四样东西: 这一版所有文件的内容、作者、一句说明、以及它是从哪一版改过来的。 最后这项把一次次提交串成一条线,这条线就是历史。

为什么以整个项目为单位存?因为代码、配置、说明经常要一起改,分开存容易拼不出能跑的版本。 只恢复旧代码却留下新配置,可能拼出一个从来没有正常运行过的版本。

flowchart LR
  A["提交 1
能跑通"] --> B["提交 2
加了输入检查"] B --> C["提交 3
改坏了"] C -->|"git checkout 回到提交 2"| B

三、五个基本命令

先装 Git。Windows 到 git-scm.com 下载安装包,一路下一步;macOS 在终端执行 xcode-select --install,系统自带的 Git 通常已经可用。装完打开终端,输入下面这行验证:

PS> git --version
git version 2.51.0.windows.1
% git --version
git version 2.51.0 (Apple Git-155)

看到版本号就说明装好了。接下来五步,在一个练习目录里完整走一遍。

下面几段命令前面那个 $ 表示「这是该你输入的内容」,不用照着打。 Windows 的 PowerShell 里提示符实际显示成 PS C:\Users\你的用户名>,macOS 里显示成 你的用户名@MacBook ~ %,提示符后面敲的命令两套系统完全一样。

第一步:把目录变成仓库

$ cd ~/gse-practice
$ git init
Initialized empty Git repository in .../gse-practice/.git/

这一步会在目录里多出一个隐藏的 .git 目录。仓库(repository,简称 repo) 指的就是这个带 .git 的目录。所有历史都存在里面,删掉 .git 等于删掉全部历史, 但当前文件还在。

第一次用之前还要告诉 Git 你是谁,否则提交会失败:

$ git config --global user.name "你的名字"
$ git config --global user.email "你的邮箱"

第二步:看看现在是什么状态

$ git status
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        note.txt

nothing added to commit but untracked files present

Untracked 的意思是「这个文件存在,但 Git 还没开始管它」。

第三步:把要存档的文件加进来

$ git add note.txt

这一步叫暂存,意思是你挑好了这次要存哪些文件。它可以分开挑: 改了三个文件,这次只想存两个,就只 add 那两个。

第四步:拍照

$ git commit -m "第一次提交:写下练习目标"
[main (root-commit) a1b2c3d] 第一次提交:写下练习目标
 1 file changed, 2 insertions(+)

-m 后面那串是这次提交的说明。这句话写给三个月后的自己看, 「改了一下」这种说明等于没写,「修正课程表里周三的课错排到周四」才有用。

第五步:看历史

$ git log --oneline
a1b2c3d (HEAD -> main) 第一次提交:写下练习目标

每次提交前面那一串 a1b2c3d 是提交的编号。想回到某一次,就用这个编号。

四、改坏了怎么退回去

改了文件但还没提交,想放弃修改:

$ git restore note.txt

已经提交了一个坏版本,想退回上一个提交,同时把文件也恢复:

$ git reset --hard HEAD~1

HEAD~1 表示「当前提交的上一个」。这条命令会丢掉未提交的改动, 执行前先用 git status 确认没有舍不得的内容。

关键结论 Git 让「回到某个能用的状态」变成一条命令。它的价值不在于省事,而在于你敢大胆改: 知道随时能退回,才会愿意尝试更好的写法。

五、哪些文件不该提交

并非目录里所有文件都该进仓库。三条实用规则:

把这些规则写成 .gitignore 文件放在仓库根目录,Git 就会自动忽略:

# .gitignore 的内容示例
*.log
dist/
.env
常见坑 误把密码提交上去之后,只删掉当前文件再提交一次没有用。 旧的那次提交里密码还在,任何人翻历史都能看到。正确做法是立刻去服务商那里把这个密码作废、换新的, 再考虑清理历史。删文件和作废密码是两件事。

六、分支:并行的几条线

分支(branch)可以理解成历史线上的一个书签。默认那条叫 main。 你可以在某次提交上开一个新分支,在上面试验新想法,失败了就丢开,成功了再合回主线。

$ git branch try-new-ui
$ git switch try-new-ui
$ git switch main

零基础阶段不必急着用分支。先把 add、commit、log、 status、restore 这五个用熟,已经能覆盖日常九成的需求。 第 7 章与第 8 章把分支、合并与并行协作讲透了,那时你已经有了手感。

现在就做 在 gse-practice 目录里完整走一遍:
① git init;② 配置用户名邮箱;③ git add note.txt;
④ git commit -m "练习:第一次提交";⑤ git log --oneline 看到那条记录;
⑥ 打开 note.txt 随便改一句,执行 git status 看 Git 怎么提示;
⑦ git restore note.txt,再打开文件确认改动没了。
做完这七步,你已经掌握了后面所有课程都要用到的基础操作。

自测

1. 我把项目目录里的 .git 文件夹删了,代码还在吗?历史还在吗?
代码还在,历史没了。所有提交记录都存在 .git 里,删掉它等于把这个目录变回普通文件夹。反过来说,只要有 .git,即使把工作文件删光,也能用 git restore 找回来。
2. 一次提交能不能包含多个文件?
能,而且通常应该这样。一次提交表达一件事,这件事可能涉及好几个文件,比如同时改代码和对应的测试。判断标准是「这次改动能不能用一句话说清楚」,不是文件个数。
3. 提交说明写「update」有什么问题?
三个月后你和其他人都无法从中知道这次改了什么、为什么改。好的说明写清楚行为变化,例如「修正周三课程被错排到周四的问题」。提交说明是给未来的自己和协作者看的。
4. 为什么密码误提交后删掉文件仍然不安全?
历史里的旧提交仍然完整保留着那份内容,git log 翻回去就能看到。别人也可能已经拉取过。可靠的处理是立刻在服务商处作废该密码并换新的,把损失控制在「已作废的凭据」范围内。