Git 教程
Git 是一个免费开源的分布式版本控制系统,旨在快速高效地处理从小型项目到大型项目的所有事务。
Git 常用命令
初始化及配置
初始化 git 本地仓库
git init
显示仓库的配置
git config -l
配置颜色
git config --global color.ui true
配置编辑器为 vim
git config --global core.editor vim
给远程仓库地址添加别名
git remote add origin git@github.com:Rasic2/gvasp.git
其中 origin 代表给远程仓库添加的别名, git@github* 代表远程仓库的地址。
提交相关
显示工作目录和暂存区的状态
git status
将文件添加到暂存区
git add .
将暂存区的内容添加到本地仓库
git commit -m "comment"
其中 comment 表示对本次版本更新的标注。
修改 commit 的最后一条注释
git commit --amend
git commit --amend --author "qwer <qwer@qq.com>" --no-edit
将本地仓库的内容同步到远程
git push origin master
其中 origin 代表远程仓库的别名, master 代表一个分支。
将远程仓库的内容同步到本地
git pull origin master
将工作区的内容缓存
git stash
显示缓存区记录
git stash list
将缓存区的第一条记录恢复到工作区
git stash pop
分支相关
基于分支新建分支
git checkout -b new_branch # 基于当下分支
git checkout -b new_branch old_branch # 基于已有分支
删除本地分支
git branch -d branch_name
删除远程分支
git push origin --delete branch_name
同步远程分支
git fetch --prune # 当远程仓库上已经删除了某个分支,但本地的 `git branch -r` 仍然显示该分支
重命名分支
git branch -m new_branch_name
合并分支
git merge branch_name
git rebase branch_name # 变基
撤销操作
git revert -n OLDER_COMMIT^...NEWER_COMMIT # 撤销从 OLDER_COMMIT 到 NEWER_COMMIT 的所有 COMMIT,并且保存为一次 COMMIT
tag 相关
查看已有的 tag
git tag
为当前 commit 增加 tag(如 v0.0.3)
git tag v0.0.3
后期为某个版本号增加 tag(如 v0.1.1)
git tag -a v0.1.1 COMMIT_ID -m "message"
删除已有的 tag(如 v0.0.1)
git tag -d v0.0.1 # 删除本地 tag
git push origin :refs/tags/v0.0.1 # 删除远程 tag
将本地 tag 提交到远程仓库
git push origin --tags
从远程仓库获取 tag
git fetch origin --tags
冲突相关
对于冲突的文件,使用合并进来的分支(如
main)覆盖当前分支(如test)确保你正处于合并冲突的状态中,并且当前分支是 test。
对于每一个冲突文件,使用 –theirs 选项来检出 main 的版本。
Note
在合并操作中,–theirs 代表要合并进来的分支(即 main)的版本。
–ours 代表当前所在的分支(即 test)的版本。
git checkout --theirs -- path/to/conflicted_file1.js git checkout --theirs -- path/to/conflicted_file2.css git checkout --theirs -- path/to/conflicted_file3.html
提交版本
git add . git commit
历史相关
比较差异
git diff zyguo..main # 比较两个分支的差异
git diff abc123 def456 -- app.py # 比较某文件在两次提交中的差异
显示历史日志
git log
git log --stat # 查看所有提交记录的修改文件信息
git log -p file # 查看某个文件的修改历史
git log COMMIT1..COMMIT2 -- file # 查看某个文件从 COMMIT1 到COMMIT2 所有的修改情况(概略信息)
git log COMMIT1..COMMIT2 -p file # 查看某个文件从 COMMIT1 到COMMIT2 所有的修改情况(详细信息)
git log -n 4 --pretty=format:"%s" # 查看最近4条提交记录并只显示提交信息
git log --pretty=format:"%s" main..dev/zhouh # 查看 dev/zhouh 分支相比 main 分支的差异
合并分支(变基)
git rebase branch # 区别于 git merge,该命令会删除原有 commit,新建 commit
梳理提交历史
git rebase -i HEAD~num # 进入交互模式
- pick
重新安排 pick 命令的顺序会更改提交的顺序。如果选择不包括提交,则应删除整行。
- reword
使用后会提供更改提交信息的机会,提交所做的任何更改均不受影响。
- edit
添加或修改提交,可以将大型提交拆分为较小的提交,或者删除在提交中所做的错误更改。
- squash
该命令可以将两个或多个提交合并为一个提交。提交被压缩到其上方的提交中,有机会重新编写描述这两个更改的新提交消息。
- fixup
与 squash 类似,但提交仅合并到其上方的提交中,并且较早提交的消息用于描述所有更改。
- exec
可以对提交运行任意的 Shell 命令。
Note
重新推送到远程仓库时,需要加上 --force-with-lease,不建议直接用 --force,详情可看这里。
将一个 commit 拆分成多个
依次执行如下步骤:
git checkout -b new_branch_name commit_id # 想拆分 commit 的版本号
git reset HEAD^ # 撤销上一个 commit 到工作区
git add some_files && git commit -m "" # 新建多个 commit
git checkout branch # 切换到想更改历史的分支
git rebase new_branch_name # 变基到拆分 commit 的分支即可自动将某个 commit 拆分成多个
版本回退
git reset --hard commit_id # 移动HEAD指针,并清空工作区和暂存区(git add)
git reset --soft commit_id # 移动HEAD指针,并把版本差异放进暂存区
git reset --mixed commit_id # 移动HEAD指针,并把版本差异放进工作区
git reset # 默认 mixed 模式
将一个文件的多处变更拆分为多个 commit
依次执行如下步骤:
确保你的工作区是干净的,并且已经将想要拆分的文件修改完毕。
执行交互式暂存命令:
git add -p <文件名>
或者,如果你想对所有修改过的文件进行操作,可以省略文件名:
git add -p
Git 会逐块(hunk)地显示你的更改,并询问你如何处理它。你会看到类似下面的提示:
Stage this hunk [y,n,q,a,d,s,e,?]?
y:暂存此区块。
n:不暂存此区块。
q:退出;不暂存这个区块和后面所有的区块。
a:暂存这个区块和这个文件后面所有的区块。
d:不暂存这个区块和这个文件后面所有的区块。
s:分割这个区块。如果这个区块比较大,且包含多个逻辑变更,可以用这个选项将它拆分成更小的区块,然后再决定。
e:手动编辑这个区块。这是最强大的选项,可以精确地选择要暂存哪些行。
循环操作:对于第一个你想提交的功能,对所有相关的区块选择 y(或者 s 分割后选择 y)。对于不属于这个功能的区块,选择 n。
完成第一次提交:
git commit -m “提交信息:功能 A”
重复步骤 2-5:再次运行 git add -p,这次选择与第二个功能相关的区块进行暂存,然后完成第二次提交。
git add -p <文件名>
git commit -m “提交信息:功能 B”
检查结果:使用 git log –oneline 查看是否成功创建了多个 commit。
规范 Git 提交说明
使用 git cz 命令可以自动生成规范的 Git 提交,要使用该命令,需要以下几个步骤:
安装
nodejs和npm(Ubuntu 系统)
sudo apt-get update
sudo apt-get install nodejs npm
安装适配器
npm install -g cz-conventional-changelog
在项目目录下初始化
cz-conventional-changelog适配器
commitizen init cz-conventional-changelog --save --save-exact
在 Git 提交时使用
git cz命令
git add .
git cz
Git hook 相关
强制检查提交
首先,进入你的 Git 仓库并创建或编辑
.git/hooks/commit-msg文件:
#!/bin/bash
# 提交消息文件
COMMIT_MSG_FILE=$1
# 允许的前缀列表
PREFIXES="feat|fix|docs|style|refactor|build|chore|perf|test|ci|revert"
# 读取提交消息的第一行
COMMIT_MSG=$(head -n 1 "$COMMIT_MSG_FILE")
# 检查提交消息是否以允许的前缀开头
if ! echo "$COMMIT_MSG" | grep -qE "^($PREFIXES)"; then
echo "错误:提交消息必须以以下前缀之一开头:$PREFIXES" >&2
exit 1
fi
将这个钩子脚本设置为可执行:
chmod +x .git/hooks/commit-msg
Commit 前操作
创建
.git/hooks/pre-commit文件:
#!/bin/bash
# 获取当前分支名称
current_branch=$(git rev-parse --abbrev-ref HEAD)
# 获取远程分支的最新更新
git fetch origin $current_branch
# 比较本地和远程分支
local_commit=$(git rev-parse $current_branch)
remote_commit=$(git rev-parse origin/$current_branch)
if [ "$local_commit" != "$remote_commit" ]; then
echo "Your local branch is not up to date with origin/$current_branch."
echo "Please pull the latest changes before committing."
exit 1
fi
# 如果一致,则允许提交
exit 0
将这个钩子脚本设置为可执行:
chmod +x .git/hooks/pre-commit
本地保护 main 或 master 分支
创建
.git/hooks/pre-push
#!/bin/bash
protected_branch="main"
current_branch=$(git symbolic-ref HEAD 2>/dev/null | sed -e 's,.*/\(.*\),\1,')
# 如果没有当前分支(比如在 detached HEAD 状态),直接退出
if [ -z "$current_branch" ]; then
exit 0
fi
# 检查是否是推送到受保护分支
if [ "$current_branch" = "$protected_branch" ]; then
# 获取推送的引用(分支或标签)
while read local_ref local_sha remote_ref remote_sha
do
# 如果是标签推送,允许
if [[ "$remote_ref" == refs/tags/* ]]; then
continue
fi
# 如果是删除分支操作,允许
if [ "$remote_sha" = "0000000000000000000000000000000000000000" ]; then
continue
fi
# 如果是推送到受保护分支的提交,拒绝
if [ "$remote_ref" = "refs/heads/$protected_branch" ]; then
echo "ERROR: You are not allowed to push commits directly to the $protected_branch branch."
echo "Use a feature branch and create a Pull Request."
exit 1
fi
done
fi
exit 0
将这个钩子脚本设置为可执行:
chmod +x .git/hooks/pre-push
使用 Git 模版来初始化 hook 钩子
创建一个模板目录:
mkdir -p ~/.git-templates/hooks
添加
commit-msg钩子:vim ~/.git-templates/hooks/commit-msg在钩子文件中添加代码:
#!/bin/bash # 提交消息文件 COMMIT_MSG_FILE=$1 # 允许的前缀列表 PREFIXES="feat|fix|docs|style|refactor|chore|perf|test|ci|revert" # 读取提交消息的第一行 COMMIT_MSG=$(head -n 1 "$COMMIT_MSG_FILE") # 检查提交消息是否以允许的前缀开头 if ! echo "$COMMIT_MSG" | grep -qE "^($PREFIXES)"; then echo "错误:提交消息必须以以下前缀之一开头:$PREFIXES" >&2 exit 1 fi
确保钩子文件是可执行的:
chmod +x ~/.git-templates/hooks/commit-msg
配置 Git 使用模板目录:
git config --global init.templateDir '~/.git-templates'
初始化新的 Git 仓库:
git init my-new-repo
自动生成 CHANGELOG
conventional-changelog
使用 conventional-changelog 命令可以自动生成 CHANGELOG,要使用该命令,需要以下几个步骤:
安装或者更新
nodejs和npm(Ubuntu 系统默认apt安装的版本较低,需要使用n升级到较高的版本)
sudo apt-get update
sudo apt-get install nodejs npm
sudo npm install -g n
sudo n stable # 安装 nodejs stable
sudo n latest # 或安装 nodejs latest
sudo n ls # 查看已下载的安装版本
sudo n 18.21.1 # 切换版本
安装
conventional-changelog-cli
npm install -g conventional-changelog-cli
更新
CHANGELOG.md
touch CHANGELOG.md # 首次需手动创建
npx conventional-changelog -p angular -i CHANGELOG.md -s -r 0
github_changelog_generator
安装
Ruby和github_changelog_generator
brew install ruby
gem install github_changelog_generator
生成
CHANGELOG.md
github_changelog_generator -u github_project_namespace -p github_project
Git 删除大文件历史
当使用 Git 进行版本管理时,可能会不慎将一些库文件、配置文件或特大文件加入版本管理而使得 .git 文件夹占用空间过大。因此特别有必要对一些 git 历史进行重写,删除不想被 git 追踪的文件,官方推荐使用的工具是 git-filter-repo(可使用 pip 或 conda 进行安装),下面将对该工具的一些具体用法进行说明。
对 git 历史进行分析
git filter-repo --analyze
首次运行该命令会生成 .git/filter-repo 目录,可查看该目录获取文件的大小和历史名字,如 .git/filter-repo/analysis/path-all-sizes.txt:
=== All paths by reverse accumulated size ===
Format: unpacked size, packed size, date deleted, path name
35149 12121 <present> LICENSE
291 209 <present> README.md
Note
后续分析时覆盖该目录需加入参数 --force。
删除文件
git filter-repo --path foo.zip --invert-paths
该命令表示将 foo.zip 从 git 历史中移除。
使用 opencommit 自动生成 commit
使用 npm 安装 opencommit:
nvm use 18
npm install -g opencommit
配置 OPENAI
oco config set OCO_API_URL=https://xxxx.openai.azure.com
oco config set OCO_AI_PROVIDER='azure' OCO_MODEL='gpt-4o'
oco config set OCO_API_KEY=xxx
【可选配置】
oco config set OCO_GITPUSH=false # 关闭自动 push
oco config set OCO_ONE_LINE_COMMIT=true # 单行 commit
oco config set OCO_OMIT_SCOPE=true # 省略 scope
oco config set OCO_TOKENS_MAX_INPUT=40960 # 修改输入最大 Token 数
使用 oco 命令生成 commit
git add .
oco
更多参考资源
4. 细读 Git | 让你弄懂 origin、HEAD、FETCH_HEAD 相关内容
7. 【git 整理提交】git rebase -i 命令详解