本节将引导读者学会使用最基础的 git 版本控制功能。
12¾.1 ハロー ギット #
就算代码库被
rm -rf,
我也想,
使用git reset --hard回见到你——
这种让一切“重来”的底气,源于我们手中的强大工具:Git。
Git 是一个免费且开源的分布式版本控制系统,旨在快速高效地处理从小到大的所有项目。(引自 Git 官网 和 中文官网)
通过 Git,我们不仅可以轻松维护代码的无数个历史版本,还能从清晰的提交记录中追溯每一次修改的意图。这极大地方便了复杂项目的开发与维护。
Git “分布式”的特性更是团队协作的利器。它没有绝对的中心服务器,每个开发者的本地仓库都是完整的副本。基于内容差异和哈希校验的同步机制,让分支、合并与代码评审变得高效可靠。程序员可以轻松的在每一个 IF线 之间跳跃,还能对它们进行分支、合并等操作。全球最大的代码托管平台 GitHub 正是得名于 Git。
Git 还像是“神之手”。误删文件、改出 Bug、甚至整个项目目录被清空?别慌。只要曾经提交过,你就可以从容地从某个历史节点恢复如初。
从个人实验到企业级项目,Git 已成为现代软件开发不可或缺的基础设施。接下来,我们将从零开始,掌握 Git 最核心的操作,掌握穿越时间、修改现实的力量! 仅限于你的仓库内
12¾.2 准备工作 #
我们首先需要正确地下载和安装 Git。
前往 Git - Install 下载正确的安装包
然后打开安装包,UAC 弹窗记得通过一下。基本上保持默认就好,但是,选择编辑器(Choosing the default editor used by git)的时候记得改成自己习惯的!!!
这样就完成了安装
安装之后… #
这时,打开任意终端(开始菜单图标右键 - “终端”(或者会是命令提示符/Powershell))
输入:
git --version
你会立刻看到版本信息,说明安装成功。
这之后,输入下面命令进行其它必要配置:
# 设置你的身份信息(提交时会用到)
git config --global user.name "你的名字"
git config --global user.email "你的邮箱@example.com"
# 关闭文件名转义
git config --global core.quotepath false
12¾.3 驾驭“神之手” #
Git 的工作看起来就是将历史线的节点进行记录、分支、合并和移动的操作。不过我们这里先只学习“书写历史”的部分。熟练之后,读者可以自行查阅、探索分支和合并操作。
12¾.3.1 初始化:美好 我看未必 故事的起点
#
找到一个目录,实在不行新建一个,右键(找不到就 Shift + 右键)- 在终端中打开(或者 Git Bash,Powershell、命令提示符都可以),打开 git 的终端。
输入并回车
git init
这样就完成了仓库初始化(我安装了 oh-my-posh,提示符也同步显示了仓库的信息)
12¾.3.2 第一次提交 #
随便创建一点文件
然后输入:
git add .
# 由于你现在在仓库根目录下,所以递归向下搜就能扫到所有文件
# 如果你不在根目录,可以使用
git add -A
完成后什么都不会输出,但这个时候,git 已经完成了对 . 目录(也就是当前目录)的扫描,将文件从工作区(就是仓库根目录,你做编辑的区域)放入暂存区,准备好了提交。
碎碎念之一:增加特定文件 #
如果你只想增加特定的文件或者目录,那么就将 “.” 替换为对应的东西。
# 只添加某个文件
git add filename.txt
# 批量添加某个文件夹下所有东西
git add folder/
# 添加所有修改的文件
# ** 只添加已跟踪的文件,也就是如果新增了文件,新文件不会被跟踪
# 这一点与 -A 有所不同
git add -u
碎碎念之二:忽略文件 #
如果需要在批量操作的时候忽略特定的文件,那么,创建一个名为 .gitignore 的文件,用记事本打开它,将需要忽略的文件/文件夹以相对路径的形式添加进去即可
比如要忽略 𝙿𝚑𝚒𝙻𝚒𝚊𝟶𝟿𝟹,那么,输入:
./𝙿𝚑𝚒𝙻𝚒𝚊𝟶𝟿𝟹
即可。由于 𝙿𝚑𝚒𝙻𝚒𝚊𝟶𝟿𝟹 就在仓库根目录,所以是 ./𝙿𝚑𝚒𝙻𝚒𝚊𝟶𝟿𝟹,如果有文件:位于仓库下的 DATA - ChrysosHeirs - PhiLia093 - memory_pb6b5z3qsns33pxm,那么就是:DATA/ChrysosHeirs/PhiLia093/memory——pb6b5z3qsns33pxm
如果是目录,建议加上 / 结尾
更多的例子:
# 忽略所有 .log 文件
*.log
# 忽略 build/ 目录
build/
# 忽略所有 .tmp 文件,但保留 important.tmp
*.tmp
!important.tmp
你还可以在 GitHub 找到更多参考。
要查看 git 扫出了什么,输入:
git status
git 展示了距离上一次提交的更改。
如果你的文件名有 Unicode 字符,界面可能会长这样(比如 𝙿𝚑𝚒𝙻𝚒𝚊 的字符不是标准英文字符,是为数学公式设计的 Unicode 版本):
请输入:
git config --global core.quotepath false
关闭转义序列,然后重试。
这时你可以确认是否正确(git diff HEAD,q 退出),如果正确,那么就可以提交了(没有的话就回去改然后重新 add)
输入:
git commit
这时会打开之前说的默认编辑器(我是 vim),你可以编辑提交信息,# 是注释行会被忽略。提交信息是你回顾历史时最直接的媒介,请务必认真填写。注意如果没有任何提交信息,git 会直接拒绝你的提交。
现在文件从暂存区存入了提交。
你也可以使用
git commit -m "提交信息"来避免打开编辑器
现在,你已经学会了“记录”的操作。
那么,就去书写属于你的浪漫故事吧~
小结 依旧增删改查 #
git 的操作也算得是一种对数据的”增删改查“操作了。我们刚才主要学习了”增“的操作
初始化:
git init
添加:
# 文件或目录
git add <path>
# 全加
git add -A
# 更新而不增加文件
git add -u
检查状态:
git status
提交:
git commit [-m MSG]
12¾.3.3 回滚:终末的力量 #
如果写错了代码、不小心删除了内容,我们就需要从「过去」寻找答案:
# 检查所有提交
git log
显示有两个提交。
回滚有几种方式,分别有不同的强度,从“软”到“硬”分别如下:
1. 软回滚(保留修改) #
如果只是想撤销提交但保留修改内容:
# 撤销最近一次提交,但保留修改内容在暂存区
git reset --soft HEAD~1
# 如果只是恢复特定文件,那么加上文件名即可
# 此时HEAD的头部不会发生移动
git reset --soft HEAD~1 a.txt CMakeList.txt
--soft 不会更改工作区,会将提交撤销,提交的文件放入暂存区,此时你就可以为这个提交补充文件,免得额外增加一次提交
注意,如果只是像第二个命令一样恢复一个或几个文件,HEAD的头部不会发生移动,后面的回滚同理。
2. 混合回滚 #
# 撤销最近一次提交,完全放弃暂存区
git reset HEAD~1
# 等效于
# git reset --mixed HEAD~1
# 如果只是恢复特定文件,那么加上文件名即可
git reset HEAD~1 a.txt CMakeList.txt
--mixed 会直接将提交放回工作区,所以,如果你最初提交了文件 file1.txt、file2.txt,提交后又修改了 file1.txt,这回导致工作区的 file1.txt 与提交的 file1.txt 冲突。此时不能使用 --mixed
3. 硬回滚(放弃修改) #
如果想彻底丢弃当前所有修改,回到某个过去的提交状态:
# 回滚到指定的提交
# hash只需要前几位就好
# 比如 620073a
# 当然,HEAD始终指代最新的提交的哈希
# HEAD~1,HEAD~2分别代表HEAD的前一次、前两次提交
git reset --hard <commit-hash>
# 如果只是恢复特定文件,那么加上文件名即可
git reset --hard <commit-hash> a.txt CMakeList.txt
警告:这是最强的回滚形式,会清除所有工作区的内容!谨慎使用。
4. 使用 revert 安全回滚 #
如果是在公共分支上,推荐使用 revert 创建一个新的提交来撤销之前的修改:
# 撤销指定的提交
git revert <commit-hash>
这会生成一个新的提交,正好复制了 <commit-hash> 中的内容。
小结 #
总结一下,git 的回滚主要语法有:
# 真·回滚
git reset <--soft|--mixed|--hard> <hash> [filename1] [filename2] ...
# 复刻一份
git revert <commit-hash>
12¾.3.4 对比:选哪边? #
对比清晰,确实够清楚wwwwww
git diff 命令可以对比一对提交或者文件。基本语法是:
git diff <obj1> <obj2>
比如 git diff [branch1] [branch2] 可以对比两个 branch 的差异;git diff [file1] [file2] 可以对比文件的差异。(你可能意识到了分支名和文件名可能冲突,我们稍后讨论)
另外,
git diff --staged [file]
可以将工作区的 [file] 与暂存区中的 [file] 进行对比。(也可以使用别名 --cached)
如果不指定文件 [file],那么 git 就会展示所有文件的对比。如果不想吵到眼睛,那么可以使用 --stat 只显示摘要。
将 --staged 替换为提交的哈希值,那么就会改为对比工作区与具体提交,语法与 --staged 几乎一样,只是往往会插入一个分隔符避免将文件名与分支名混淆。举个例子,你 恰好 有一对文件 CAFEBABE 和 CAFEDEAD。然后 恰好 也产生了提交号 CAFEBABE和 CAFEDEAD。那么:
git diff CAFEBABE CAFEDEAD
就会优先对比分支 CAFEBABE和 CAFEDEAD,而不是文件对比。那么,如何进行区分呢?
可以使用 .. 连接两个提交:
git diff CAFEBABE..CAFEDEAD
使用 -- 强制指定文件
# 将指定提交 <CAFEBABE> 的文件与工作区的文件 CAFEDEAD 进行对比
git diff CAFEBABE -- CAFEDEAD
# 单纯对比俩文件
git diff CAFEBABE -- CAFEDEAD --
但这种语法过于诡异,占位符 -- 的位置不 那么 便于理解,我们不多讲。如果要更清晰地体现语义,其实可以加一个 ./(或者具体路径):
git diff CAFEBABE ./CAFEDEAD
git diff ./CAFEBABE ./CAFEDEAD
这样就保持了语法的一致性,好记又好用了。
最后,其实 diff 还有三个点连接提交的语法:
git diff CAFEBABE...CAFEDEAD
它的含义是,寻找这两个提交最近的共同祖先 X,然后将 X 与 CAFEDEAD 进行对比。主要用于对比带有分支结构的提交。
但这完全可以使用等效且更清晰的语法替代:
git diff --merge-base CAFEBABE CAFEDEAD
碎碎念:不会有杂鱼退不出对比界面吧~♡
使用方法几乎和 vim 一样,无需多言。
不会还有人不会 Vim 吧~不会吧不会吧
小结 #
总结一下 git diff 的语法:
git diff [--merge-base] [--stat] <object1> <object2>
其中:
-
[--merge-base]只能用于分支对比,将<object2>与两分支的共同祖先X进行对比; -
<object>具体可是<commit-hash | filename | --staged>- 可以对比:提交与提交、提交与暂存区、提交与文件、暂存区与文件、文件与文件。
-
[--stat]用于显示摘要而不显示具体内容Git 常见的工作流是: 0.
git init初始化仓库- 编辑文件
git add <file>(可以在根目录add .) 将修改添加到暂存区 - 可以使用git status检查状态,git diff检查修改内容git commit将暂存区的修改提交到本地仓库 - 可以使用git log检查提交历史- (如果需要)使用
git reset或git revert回滚到之前的提交
12¾.2147483647 结语 #
掌握了这些“增删改查”的命令,你就已经拥有了操控仓库历史的基本神力。无论是记录当下的灵感(commit),还是回溯过去的错误(reset/revert),亦或是审视变迁的细节(diff),你都能得心应手。
但这只是“神之手”的冰山一角。当你需要同时推进多个实验性功能,或是与他人协作时,分支与合并的力量将真正展现出 Git 的魔力。在未来的章节,我们将跳出线性的历史,探索平行世界的奥秘。