前言
https://missing.csail.mit.edu/2020/version-control/
https://docs.github.com/zh/get-started/git-basics/set-up-git
https://www.runoob.com/git/git-tutorial.html
https://cloud.tencent.com/developer/article/2194434
https://juejin.cn/post/7272276908379799563
https://segmentfault.com/a/1190000017030384
引入
以 Minecraft 为例,我们设想一下这样一个场景:
- 首先,本地游玩 Minecraft 一定需要创建一个存档,这个存档会记录下你的游戏状态(Repository)
- 当你正在盖一个建筑,你想使用“结构方块”把这个建筑框起来,告诉系统我要把这部分存一下(Add)
- 当你正常退出游戏,系统保存了你的存档,你还复制了这份存档生成了一个快照副本,方便以后的某个时刻不满意想直接回退使用它,然后你意识到你得在某个地方写一个注释用于记录这个快照副本此时的进度,方便下次打开这个快照副本的时候知道从哪开始干(Commit)
- 你想大搞红石科技,但是你怕把原来的存档卡爆,于是你复制了一份同样的存档,原先的存档继续作为一个普通的建筑档,新复制的这个专门用于搞红石科技,这样即使新复制的这个炸档了也不会影响到原始的存档(Branch)
- 你在这个用于“实验”的存档中成功造好了红石科技,想把它搬回原始存档(Merge)
- 你玩的本地存档是单人模式,而你想玩服务器了!(Remote)
- 你把本地存档的建筑照搬到了服务器里(Push)
- 你把服务器的建筑照搬到了本地存档(Pull)
- 你在本地存档里坐标 (X:100, Y:64, Z:100) 的地方放了一块钻石块,但是服务器里同一坐标 (X:100, Y:64, Z:100) 放了一块岩浆。你想把服务器这个坐标的内容照搬到本地,就会发生冲突,需要你自己手动介入(Conflict)
安装配置
Git 不仅仅是个版本控制系统,它也是个内容管理系统(CMS),工作管理系统等。
安装 git: https://git-scm.com/download/
然后 Windows 下就能使用 git bash 了
git config 命令:配置或读取相应的工作环境变量,这些环境变量,决定了 Git 在各个环节的具体工作方式和行为
这些变量可以存放在以下三个地方:
/etc/gitconfig文件:系统中对所有用户都普遍适用的配置。若使用git config时用--system选项,读写的就是这个文件。~/.gitconfig文件:用户目录下的配置文件只适用于该用户。若使用git config时用--global选项,读写的就是这个文件。- 当前项目的 Git 目录中的配置文件(也就是工作目录中的
.git/config文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以.git/config里的配置会覆盖/etc/gitconfig中的同名变量。
配置个人的用户名称和电子邮件地址,用于每次提交代码时记录提交者的信息
git config --global user.name "C1oudfL0w0"
git config --global user.email test@cloud.com
检查已有的配置信息:
git config --list
有时候会看到重复的变量名,那就说明它们来自不同的配置文件(比如 /etc/gitconfig 和 ~/.gitconfig),不过最终 Git 实际采用的是最后一个
查看某个环境变量的配置:
git config user.name
工作流程

克隆仓库:
git clone https://github.com/username/repo.git
创建新分支:
git checkout -b new-feature
暂存文件:
git add filename
# 或者添加所有修改的文件
git add .
提交更改:
git commit -m "Add new feature"
拉取最新更改:
git pull origin main
# 或者如果在新的分支上工作
git pull origin new-feature
推送更改:
git push origin new-feature
合并更改:
git checkout main
git pull origin main
git merge new-feature
删除分支:
git branch -d new-feature
# 从远程仓库删除分支
git push origin --delete new-feature
工作区 Working Directory
工作区是你在本地计算机上的项目目录,你在这里进行文件的创建、修改和删除操作。工作区包含了当前项目的所有文件和子目录
- 显示项目的当前状态。
- 文件的修改在工作区中进行,但这些修改还没有被记录到版本控制中。
暂存区 Staging Area
暂存区是一个临时存储区域,它包含了即将被提交到版本库中的文件快照,在提交之前,你可以选择性地将工作区中的修改添加到暂存区。
- 暂存区保存了将被包括在下一个提交中的更改。
- 你可以多次使用
git add命令来将文件添加到暂存区,直到你准备好提交所有更改。
git add filename # 将单个文件添加到暂存区
git add . # 将工作区中的所有修改添加到暂存区
git status # 查看哪些文件在暂存区中
版本库 Repository
版本库包含项目的所有版本历史记录。
每次提交都会在版本库中创建一个新的快照,这些快照是不可变的,确保了项目的完整历史记录。
- 版本库分为本地版本库和远程版本库。这里主要指本地版本库。
- 本地版本库存储在
.git目录中,它包含了所有提交的对象和引用。
git commit -m "Commit message" # 将暂存区的更改提交到本地版本库
git log # 查看提交历史
git diff # 查看工作区和暂存区之间的差异
git diff --cached # 查看暂存区和最后一次提交之间的差异
完整的操作:
# 本地修改文件后
git add file.txt
git commit -m "Update file.txt"
git push origin main # 推送到远程仓库
创建仓库
git init <repo_path> # 默认为当前目录,执行后创建 .git 目录
git add 。
git commit -m '初始化项目版本'
注: 在 Linux 系统中,commit 信息使用单引号 ',Windows 系统,commit 信息使用双引号 "。所以在 git bash 中 git commit -m '提交说明' 这样是可以的,在 Windows 命令行中就要使用双引号 git commit -m "提交说明"。
.git 目录
./git
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── index
├── info
│ └── exclude
├── logs
│ └── HEAD
├── objects
│ ├── 00
│ │ └── fa871f7c774c8a2f3c8204e3546e80daabe365
│ ├── 1d
│ │ └── 6f7af91f6b8b8ac903bd77ea9ee7531ab59a9f
│ ├── 46
│ │ └── fc026da73f9cdb62f72506f0146f5ad22ca4c8
│ ├── 5b
│ │ └── 58bd121fd2c29b36d2f5dd7dfd450bde15f4c1
│ ├── 8c
│ │ └── bb1da21438cf0907cda9bb85ddcf065fa43bf3
│ ├── af
│ │ └── 74f3306302ad59fc71670c229f16a1fa606849
│ ├── b4
│ │ └── cfa0242cf57a9373c7c7c73397414ff7b8f530
│ ├── bc
│ │ └── 7b276cb5dee2c2fcb62641047d7b79c74783b9
│ ├── ce
│ │ └── a0b8cbf9737b4f438e56fb23b7b12e03ecde96
│ ├── dc
│ │ └── dbd0b338ad13627f452ab48bdbc5df67ab576c
│ ├── e6
│ │ └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
│ └── ff
│ └── 13a25b35fda25c5fc977cb4e2009a97d50f4d1
└── refs
└── heads
└── master
COMMIT_EDITMSG:保存着最近一次的提交信息,git 不会用到这个文件,只是给用户一个参考
flag commit
config:存储了项目级别的 Git 配置信息,包括用户名、邮箱、远程仓库等。
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
description:对于空的 Git 仓库,此文件内容为空。对于非空的 Git 仓库,内容为描述该项目的文本。
Unnamed repository; edit this file 'description' to name the repository.
HEAD:指向当前所在的分支(或者是一个特定的提交)
ref: refs/heads/master
index:包含了暂存区(stage)的内容,记录了即将提交的文件和相关元数据
❯ hexdump -C index
00000000 44 49 52 43 00 00 00 02 00 00 00 01 67 b6 df ac |DIRC........g...|
00000010 06 6f f3 00 67 b6 df ac 06 6f f3 00 00 00 fd 00 |.o..g....o......|
00000020 06 34 25 e8 00 00 81 a4 00 00 00 00 00 00 00 00 |.4%.............|
00000030 00 00 00 09 dc db d0 b3 38 ad 13 62 7f 45 2a b4 |........8..b.E*.|
00000040 8b db c5 df 67 ab 57 6c 00 08 66 6c 61 67 2e 70 |....g.Wl..flag.p|
00000050 68 70 00 00 a8 d5 ca b5 11 ba 3b d8 66 2b 79 6c |hp........;.f+yl|
00000060 ee 90 25 78 0e e7 c0 17 |..%x....|
00000068
info:包含一些辅助性的信息
logs:存储了每个引用(分支、标签等)的修改历史
❯ cat logs/HEAD
0000000000000000000000000000000000000000 ff13a25b35fda25c5fc977cb4e2009a97d50f4d1 ryan <ryan@work.com> 1740033342 +0800 commit (initial): first file
ff13a25b35fda25c5fc977cb4e2009a97d50f4d1 af74f3306302ad59fc71670c229f16a1fa606849 ryan <ryan@work.com> 1740033420 +0800 commit: second file
af74f3306302ad59fc71670c229f16a1fa606849 af74f3306302ad59fc71670c229f16a1fa606849 ryan <ryan@work.com> 1740033512 +0800 checkout: moving from master to flag
af74f3306302ad59fc71670c229f16a1fa606849 1d6f7af91f6b8b8ac903bd77ea9ee7531ab59a9f ryan <ryan@work.com> 1740033731 +0800 commit: flag file
1d6f7af91f6b8b8ac903bd77ea9ee7531ab59a9f af74f3306302ad59fc71670c229f16a1fa606849 ryan <ryan@work.com> 1740034654 +0800 checkout: moving from flag to master
af74f3306302ad59fc71670c229f16a1fa606849 1d6f7af91f6b8b8ac903bd77ea9ee7531ab59a9f ryan <ryan@work.com> 1740036538 +0800 checkout: moving from master to flag
1d6f7af91f6b8b8ac903bd77ea9ee7531ab59a9f 5b58bd121fd2c29b36d2f5dd7dfd450bde15f4c1 ryan <ryan@work.com> 1740038030 +0800 commit: flag commit
5b58bd121fd2c29b36d2f5dd7dfd450bde15f4c1 af74f3306302ad59fc71670c229f16a1fa606849 ryan <ryan@work.com> 1740038060 +0800 checkout: moving from flag to master
objects:存储了Git仓库的对象(commits、trees和blobs),其中的命名规则为 refs 前两位作为文件夹,refs 剩下的部分作为文件名,如 ff13a25b35fda25c5fc977cb4e2009a97d50f4d1 的结构为 ff/13a25b35fda25c5fc977cb4e2009a97d50f4d1,文件中的内容是 zlib 压缩后的 commit,解压后内容如下:
commit 151\x00 tree cea0b8cbf9737b4f438e56fb23b7b12e03ecde96
author ryan <ryan@work.com> 1740033342 +0800
committer ryan <ryan@work.com> 1740033342 +0800
first file
refs:存储了所有的引用(分支、标签等)
❯ cat refs/heads/master
af74f3306302ad59fc71670c229f16a1fa606849
基本操作
git init
git clone # 拷贝一份远程仓库,包括被clone仓库的版本变化
git add
git status
git diff # 比较文件的不同,即暂存区和工作区的差异
git difftool
git range-diff # 比较两个提交范围之间的差异
git commit
git reset # 回退版本
git rm # 将文件从暂存区和工作区中删除
git mv # 移动或重命名工作区文件
git notes # 添加注释
git checkout # 分支切换
git switch # 更清晰的分支切换
git restore # 恢复或撤销文件的更改
git show # 显示git对象的详细信息
git log
git blame <file>
git shortlog
git describe
git remote
git fetch # 从远处获取代码库,将本地已有的远程仓库更新到最新状态,仅拉取更改不会合并分支
git pull # 拉取远程分支更新到本地仓库,自动合并与更改
git push
git submodule # 管理包含其他 Git 仓库的项目
冲突解决
当我们尝试 git pull 更新本地仓库时,有时会遇到 error: Your local changes to the following files would be overwritten by merge... Please commit your changes or stash them before you merge.
这是因为本地修改了文件,而远程仓库更新的代码恰好也要覆盖这些文件
解决方法,分多种情况:
- 放弃本地修改,直接用远程最新代码
git reset --hard HEAD
git pull
- 保留本地修改,与最新代码合并
# 1. 将本地修改暂时“藏”起来
git stash
# 2. 重新拉取最新代码(此时会成功)
git pull
# 3. 将刚刚“藏”起来的修改恢复到代码中,如果你的修改和原作者的修改冲突了,Git 会提示你手动解决冲突(Merge Conflict)
git stash pop
- 保存本地修改
# 1. 提交你的本地修改
git add .
git commit -m "保存我的本地修改"
# 2. 重新拉取并合并
git pull
分支管理
提交历史
标签
Git 标签(Tag)用于给仓库中的特定提交点加上标记,通常用于发布版本(如 v1.0, v2.0)
注意事项
- Windows (^M)和 Linux/Mac 的换行符不一样,如果一个仓库的代码在 Windows 下打开过,系统会自动把文件的换行符替换成 Windows 格式。此时在 Mac 下
git pull会导致冲突,可以强制拉取最新代码,也可以使用此命令让 git 自动处理换行符问题
git config --global core.autocrlf true
(设置完之后,Git 在拉取时会自动转换换行符,提交时也会自动转回去,就不会再报这种错误了)
Git 服务
GitHub
GitLab
Gogs
Gogs(Go Git Service)是一个用 Go 语言编写的轻量级、自托管的 Git 服务解决方案