一、Git 简介

Git 是一个开源的分布式版本控制系统,由 Linus Torvalds 于 2005 年为管理 Linux 内核开发而创建。它可以记录文件的每一次修改,让你随时回到历史版本,也方便多人协同开发。

1.1 什么是版本控制

版本控制用于记录文件内容随时间的变化,主要解决以下问题:

  • 修改出错后可以回退到之前的版本
  • 可以查看每一次修改的内容、时间和作者
  • 多人同时修改同一项目时,可以合并各自的工作成果

1.2 集中式与分布式

对比项集中式(如 SVN)分布式(Git)
版本库位置中央服务器每个开发者本地都有完整版本库
离线工作无法提交可以随时在本地提交
分支创建成本较高轻量,创建和切换非常快
故障影响服务器故障时影响大每个副本都是完整备份

1.3 Git 的三个区域

Git 中的文件分布在三个区域:

  • 工作区(Working Directory):你平时编辑文件的目录
  • 暂存区(Stage / Index):临时存放准备提交的修改
  • 版本库(Repository):.git 目录中保存的全部历史记录
1
2
3
工作区  --git add-->  暂存区  --git commit-->  版本库(.git)
^ |
|------------- git restore / checkout ---------|

1.4 文件的四种状态

  • 未跟踪(Untracked):新建的文件,Git 尚未记录
  • 已修改(Modified):已被跟踪的文件内容发生了变化
  • 已暂存(Staged):已通过 git add 放入暂存区
  • 已提交(Committed):已通过 git commit 保存到版本库

二、安装与配置

2.1 安装 Git

  • Windows:从 https://git-scm.com/download/win 下载安装包,安装后可使用 Git Bash 运行命令。
  • macOS:执行 brew install git,或执行 xcode-select --install 安装命令行工具。
  • Debian / Ubuntu:执行 sudo apt update && sudo apt install git。
  • CentOS / RHEL / Fedora:执行 sudo dnf install git(旧系统使用 yum)。

安装完成后检查版本:

1
git --version

2.2 配置用户信息

每次提交都会记录作者信息,首次使用前需要配置用户名和邮箱:

1
2
git config --global user.name "Your Name"
git config --global user.email "you@example.com"

Git 的配置分为三个级别:

级别参数作用范围配置文件位置
系统级--system所有用户/etc/gitconfig
用户级--global当前用户的所有仓库~/.gitconfig
仓库级--local(默认)当前仓库.git/config

查看配置:

1
2
git config --list              # 查看所有配置
git config user.name # 查看指定配置项

常用的其他配置:

1
2
3
4
git config --global core.editor "code --wait"   # 指定默认编辑器
git config --global init.defaultBranch main # 新仓库默认分支名为 main
git config --global core.autocrlf true # Windows 下处理换行符
git config --global core.autocrlf input # macOS / Linux 下处理换行符

三、Git 基本操作

3.1 创建仓库:git init

在一个空目录或已有项目目录中初始化仓库,会生成隐藏的 .git 目录:

1
2
3
mkdir runoob-git
cd runoob-git
git init

执行后会输出类似 Initialized empty Git repository in .../.git/ 的提示。

3.2 添加到暂存区:git add

1
2
3
4
5
git add README.md      # 添加指定文件
git add src/ # 添加指定目录
git add . # 添加当前目录下的所有修改(包括新文件)
git add -A # 添加整个仓库的所有修改(包括删除)
git add -p # 交互式选择文件中的部分修改后再暂存

3.3 提交修改:git commit

1
2
3
git commit -m "添加 README 文件"
git commit -am "修改登录逻辑" # 对已跟踪的文件执行 add 并提交(新文件不会被包含)
git commit --amend # 修改最近一次提交(信息或内容)

提交信息建议遵循以下习惯:

  • 第一行简要概括本次修改(建议不超过 50 个字符)
  • 必要时空一行后补充详细说明,解释“为什么这样改”
  • 每次提交只做一件相对独立的事情

3.4 查看状态:git status

1
git status

该命令会列出已暂存、已修改但未暂存以及未跟踪的文件,是使用 Git 时最常用的命令之一。

3.5 查看差异:git diff

1
2
3
4
5
git diff                     # 工作区 与 暂存区 的差异
git diff --staged # 暂存区 与 最近一次提交 的差异(--cached 同义)
git diff HEAD # 工作区 与 最近一次提交 的差异
git diff HEAD~1 HEAD # 两次提交之间的差异
git diff --stat # 只显示变更文件的统计信息

3.6 查看提交历史:git log

1
2
3
4
5
6
7
git log                              # 显示完整提交历史
git log --oneline # 每个提交显示一行
git log -3 # 只显示最近 3 次提交
git log -p # 显示每次提交的具体改动
git log --author="Your Name" # 按作者过滤
git log --graph --oneline --all # 以图形方式显示所有分支
git show <commit-id> # 查看某次提交的详细内容

--oneline 的输出示例:

1
2
3
9e8f7a1 (HEAD -> main) 修改登录逻辑
3628164 添加 README 文件
7c2d9b0 初始化项目

四、版本回退

4.1 HEAD 与提交编号

  • HEAD 指向当前所在的提交(即当前分支的最新位置)
  • HEAD^ 表示上一个版本,HEAD^^ 表示上上个版本
  • HEAD~3 表示往前数第 3 个版本
  • 每个提交都有一个 40 位的 SHA-1 编号,通常只需使用前 7 位即可

4.2 git reset:回退版本

1
2
3
4
git reset --soft HEAD~1      # 回退到上一版本,修改保留在暂存区
git reset --mixed HEAD~1 # 回退到上一版本,修改保留在工作区(默认模式)
git reset --hard HEAD~1 # 回退到上一版本,修改全部丢弃
git reset --hard 3628164 # 回退到指定的版本
注意:--hard 会丢弃工作区和暂存区中所有未提交的修改,执行前请确认没有需要保留的内容。

4.3 git reflog:找回“丢失”的版本

回退后想再回到较新的版本,但找不到提交编号时,可以通过 reflog 查看 HEAD 的移动记录:

1
2
3
4
5
6
git reflog
# 输出示例:
# 3628164 HEAD@{0}: reset: moving to HEAD^
# 9e8f7a1 HEAD@{1}: commit: 修改登录逻辑

git reset --hard 9e8f7a1 # 恢复到该版本

4.4 git revert:安全地撤销某次提交

git reset 会改写历史,不适合用于已经推送到远程的提交。git revert 会新建一个提交来抵消指定提交的改动,历史记录保持完整:

1
git revert 9e8f7a1

五、撤销修改与删除文件

5.1 撤销工作区的修改

文件被修改但还没有 git add 时,可以用以下命令恢复为最近一次提交的内容:

1
2
git restore README.md          # 新版写法
git checkout -- README.md # 旧版写法,效果相同

5.2 撤销暂存区的修改

已经 git add 但还没有提交时,先把文件移出暂存区,再撤销工作区的修改:

1
2
git restore --staged README.md   # 新版写法:仅取消暂存
git reset HEAD README.md # 旧版写法:仅取消暂存

5.3 删除与移动文件

1
2
3
git rm old.txt                 # 删除文件并将删除操作加入暂存区
git rm --cached secret.txt # 只从 Git 中移除跟踪,保留本地文件
git mv old.txt new.txt # 重命名或移动文件

删除文件后需要执行 git commit 才会真正记录到历史中。如果误删了已提交的文件,可以用 git restore --source=HEAD~1 文件名 或 git checkout <commit-id> -- 文件名 找回。

六、远程仓库

6.1 配置 SSH 密钥

使用 SSH 方式连接 GitHub、Gitee 等平台时,需要先配置公钥:

1
2
3
ssh-keygen -t ed25519 -C "you@example.com"
cat ~/.ssh/id_ed25519.pub # 复制输出内容,添加到代码托管平台的 SSH Keys 设置中
ssh -T git@github.com # 测试连接

6.2 添加远程仓库:git remote

1
2
3
4
5
git remote add origin git@github.com:username/runoob-git.git
git remote -v # 查看远程仓库地址
git remote show origin # 查看远程仓库的详细信息
git remote rename origin upstream
git remote remove upstream

origin 是默认的远程仓库名称,可以自定义。

6.3 推送:git push

1
2
3
4
git push -u origin main        # 首次推送,并建立本地 main 与远程 main 的关联
git push # 关联后直接推送
git push origin dev # 推送指定分支
git push --force-with-lease # 安全的强制推送(会覆盖远程历史,谨慎使用)

6.4 克隆:git clone

1
2
3
git clone git@github.com:username/runoob-git.git
git clone https://github.com/username/runoob-git.git
git clone -b dev git@github.com:username/runoob-git.git # 克隆并直接切换到 dev 分支

6.5 拉取:git fetch 与 git pull

  • git fetch:只下载远程的新提交,不修改当前工作内容
  • git pull:相当于 git fetch 加上 git merge
  • git pull --rebase:下载后使用变基的方式整合,保持历史更整洁
1
2
3
git fetch origin
git pull origin main
git pull --rebase origin main

七、分支管理

7.1 分支的概念

分支本质上是一个指向某个提交的可移动指针。HEAD 指向当前所在的分支。创建分支只是新增一个指针,因此速度很快。在分支上的新提交会让该分支指针向前移动,不影响其他分支。

7.2 创建、切换与删除分支

1
2
3
4
5
6
7
8
9
10
git branch                     # 查看本地分支(当前分支前有 * 标记)
git branch -a # 查看所有分支(包括远程分支)
git branch dev # 创建 dev 分支
git switch dev # 切换到 dev 分支(新版写法)
git checkout dev # 切换到 dev 分支(旧版写法)
git switch -c feature/login # 创建并切换到新分支(新版写法)
git checkout -b feature/login # 创建并切换到新分支(旧版写法)
git branch -d dev # 删除已合并的分支
git branch -D dev # 强制删除未合并的分支
git branch -m old new # 重命名分支

7.3 合并分支:git merge

1
2
git switch main
git merge dev
  • 快进合并(Fast-forward):目标分支没有新提交时,直接移动指针,不产生新的合并提交。
  • 非快进合并(--no-ff):始终生成一个合并提交,历史中可以看出分支合并的过程。
1
git merge --no-ff -m "合并 dev 分支的新功能" dev

7.4 解决冲突

当两个分支修改了同一文件的同一位置时,合并会产生冲突,Git 会在文件中标记出差异:

1
2
3
4
5
<<<<<<< HEAD
这是当前分支的内容
=======
这是 dev 分支的内容
>>>>>>> dev

解决冲突的步骤:

  1. 打开冲突文件,手动保留正确的内容并删除 <<<<<<<、=======、>>>>>>> 标记
  2. 执行 git add 文件名 标记冲突已解决
  3. 执行 git commit 完成合并

可以使用 git status 查看所有冲突文件,使用 git merge --abort 放弃本次合并。

7.5 分支管理策略

在团队中,通常会约定如下分支模型:

  • main:随时可以发布的稳定版本,只接受合并,不直接开发
  • dev:日常开发的集成分支
  • feature/*:新功能分支,完成后合并回 dev
  • bugfix/*:普通缺陷修复分支
  • hotfix/*:紧急修复线上问题的分支,从 main 创建,修复后同时合并到 main 和 dev
  • release/*:发布准备分支

建议合并时使用 --no-ff,以保留分支的开发历史。

7.6 临时保存现场:git stash

正在开发时需要切换分支处理紧急问题,但当前修改还没完成,可以使用 stash 临时保存:

1
2
3
4
5
6
git stash                      # 保存当前未提交的修改
git stash list # 查看保存的列表
git stash pop # 恢复最近一次保存的内容,并从列表中删除
git stash apply stash@{1} # 恢复指定的保存内容,但保留在列表中
git stash drop stash@{0} # 删除指定的保存内容
git stash -u # 同时保存未跟踪的新文件

7.7 多人协作流程

一个典型的协作流程如下:

1
2
3
4
5
6
7
8
git clone git@github.com:team/project.git
git switch -c feature/payment origin/dev # 基于远程 dev 创建功能分支
# ...编写代码...
git add . && git commit -m "实现支付接口"
git push -u origin feature/payment # 推送功能分支
# 在代码托管平台发起 Pull Request / Merge Request,审核通过后合并
git switch dev
git pull # 同步团队最新代码

如果推送时被拒绝(提示 non-fast-forward),通常是远程有新的提交,先执行 git pull 整合后再推送即可。

7.8 变基:git rebase

rebase 可以把当前分支的提交“搬到”另一个分支的最新提交之后,使历史呈线性:

1
2
git switch feature/login
git rebase main
注意:不要对已经推送到公共远程并被他人使用的分支执行 rebase,否则会导致他人的历史冲突。

八、标签管理

标签(Tag)通常用于标记重要的版本,例如 v1.0.0。

1
2
3
4
5
6
7
8
9
git tag                                  # 查看所有标签
git tag v1.0 # 创建轻量标签(只记录提交编号)
git tag -a v1.0 -m "正式版本 1.0" # 创建附注标签(包含标签者、日期和说明)
git tag -a v0.9 -m "测试版" 3628164 # 为指定提交打标签
git show v1.0 # 查看标签信息
git tag -d v1.0 # 删除本地标签
git push origin v1.0 # 推送单个标签到远程
git push origin --tags # 推送所有标签到远程
git push origin --delete v1.0 # 删除远程标签

九、忽略文件:.gitignore

项目中有些文件(如编译产物、日志、依赖目录、密码配置)不应该被提交,可以在仓库根目录创建 .gitignore 文件进行配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 忽略所有 .log 文件
*.log

# 忽略 node_modules 目录
node_modules/

# 忽略根目录下的 build 目录
/build

# 忽略环境变量文件
.env

# 但保留 logs/important.log 文件
!logs/important.log

说明:

  • 以 # 开头的行为注释
  • 以 / 结尾表示目录
  • 以 ! 开头表示取消忽略
  • 已经被 Git 跟踪的文件不会因为加入 .gitignore 而被忽略,需要先执行 git rm --cached 文件名

检查某个文件为什么被忽略:

1
git check-ignore -v build/app.js

GitHub 官方仓库中提供了各种语言和框架的 .gitignore 模板,可以直接参考。

十、配置别名

可以为常用命令设置简短的别名,提高效率:

1
2
3
4
5
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.lg "log --graph --oneline --all --decorate"

设置后即可使用 git st、git lg 等命令。

十一、完整示例

下面演示从创建项目到推送远程的完整流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 1. 创建并初始化项目
mkdir hello-git && cd hello-git
git init

# 2. 创建文件并提交
echo "# Hello Git" > README.md
git add README.md
git commit -m "初始化项目"

# 3. 创建功能分支并开发
git switch -c feature/greet
echo "print('Hello, Git!')" > hello.py
git add hello.py
git commit -m "添加问候脚本"

# 4. 切回主分支并合并
git switch main
git merge --no-ff -m "合并 feature/greet" feature/greet

# 5. 删除已合并的分支
git branch -d feature/greet

# 6. 添加远程仓库并推送
git remote add origin git@github.com:username/hello-git.git
git push -u origin main

# 7. 打标签发布
git tag -a v1.0 -m "第一个版本"
git push origin v1.0

十二、常用命令速查表

类别命令说明
配置git config --global user.name "名字"设置用户名
创建git init初始化仓库
创建git clone <url>克隆远程仓库
查看git status查看工作区状态
查看git diff查看未暂存的差异
查看git log --oneline查看提交历史
暂存git add <file>添加到暂存区
提交git commit -m "说明"提交到版本库
回退git reset --hard <commit>回退到指定版本
回退git revert <commit>生成一个撤销提交
撤销git restore <file>撤销工作区修改
撤销git restore --staged <file>取消暂存
分支git switch -c <branch>创建并切换分支
分支git merge <branch>合并指定分支到当前分支
分支git branch -d <branch>删除分支
临时git stash / git stash pop临时保存与恢复修改
远程git remote add origin <url>添加远程仓库
远程git push -u origin <branch>推送并关联分支
远程git pull拉取并合并远程更新
标签git tag -a <tag> -m "说明"创建附注标签

十三、常见问题

1. 提交信息写错了怎么办?

如果是最近一次提交且尚未推送,可以执行 git commit --amend -m "新的说明"。

2. 提交了不该提交的文件怎么办?

如果尚未推送,可以执行 git reset --soft HEAD~1 撤销提交,再用 git rm --cached 移出该文件,并将其加入 .gitignore 后重新提交。

3. 推送时提示 non-fast-forward 怎么办?

说明远程有本地没有的提交。先执行 git pull(或 git pull --rebase)整合远程代码,解决冲突后再推送。

4. 误删了分支怎么办?

可以通过 git reflog 找到该分支最后的提交编号,然后执行 git branch 分支名 提交编号 恢复。

5. 提示 LF will be replaced by CRLF 怎么办?

这是 Windows 与 macOS / Linux 换行符不同导致的提示,可以通过设置 core.autocrlf 统一处理(参见第二章)。

6. 不小心把密码提交到了仓库怎么办?

仅删除文件并提交是不够的,历史中仍然保留着该内容。应立即修改密码,并使用 git filter-repo 等工具清理历史,然后强制推送。


以上内容覆盖了 Git 的核心用法。建议结合实际项目多加练习,熟练掌握 add、commit、branch、merge 和 push 这几个最常用的命令。