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

    1. 确保你正处于合并冲突的状态中,并且当前分支是 test。

    2. 对于每一个冲突文件,使用 –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
    
    1. 提交版本

    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

依次执行如下步骤:

  1. 确保你的工作区是干净的,并且已经将想要拆分的文件修改完毕。

  2. 执行交互式暂存命令:

git add -p <文件名>

或者,如果你想对所有修改过的文件进行操作,可以省略文件名:

git add -p
  1. Git 会逐块(hunk)地显示你的更改,并询问你如何处理它。你会看到类似下面的提示:

Stage this hunk [y,n,q,a,d,s,e,?]?
  • y:暂存此区块。

  • n:不暂存此区块。

  • q:退出;不暂存这个区块和后面所有的区块。

  • a:暂存这个区块和这个文件后面所有的区块。

  • d:不暂存这个区块和这个文件后面所有的区块。

  • s:分割这个区块。如果这个区块比较大,且包含多个逻辑变更,可以用这个选项将它拆分成更小的区块,然后再决定。

  • e:手动编辑这个区块。这是最强大的选项,可以精确地选择要暂存哪些行。

  1. 循环操作:对于第一个你想提交的功能,对所有相关的区块选择 y(或者 s 分割后选择 y)。对于不属于这个功能的区块,选择 n。

  2. 完成第一次提交:

git commit -m “提交信息:功能 A”
  1. 重复步骤 2-5:再次运行 git add -p,这次选择与第二个功能相关的区块进行暂存,然后完成第二次提交。

git add -p <文件名>
git commit -m “提交信息:功能 B”
  1. 检查结果:使用 git log –oneline 查看是否成功创建了多个 commit。

规范 Git 提交说明

使用 git cz 命令可以自动生成规范的 Git 提交,要使用该命令,需要以下几个步骤:

  1. 安装 nodejsnpm(Ubuntu 系统)

sudo apt-get update
sudo apt-get install nodejs npm
  1. 安装适配器

npm install -g cz-conventional-changelog
  1. 在项目目录下初始化 cz-conventional-changelog 适配器

commitizen init cz-conventional-changelog --save --save-exact
  1. 在 Git 提交时使用 git cz 命令

git add .
git cz

Git hook 相关

强制检查提交

  1. 首先,进入你的 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
  1. 将这个钩子脚本设置为可执行:

chmod +x .git/hooks/commit-msg

Commit 前操作

  1. 创建 .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
  1. 将这个钩子脚本设置为可执行:

chmod +x .git/hooks/pre-commit

本地保护 main 或 master 分支

  1. 创建 .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
  1. 将这个钩子脚本设置为可执行:

chmod +x .git/hooks/pre-push

使用 Git 模版来初始化 hook 钩子

  1. 创建一个模板目录:

    mkdir -p ~/.git-templates/hooks
    
  2. 添加 commit-msg 钩子:

    vim ~/.git-templates/hooks/commit-msg
    
  3. 在钩子文件中添加代码:

    #!/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
    
  4. 确保钩子文件是可执行的:

    chmod +x ~/.git-templates/hooks/commit-msg
    
  5. 配置 Git 使用模板目录:

    git config --global init.templateDir '~/.git-templates'
    
  6. 初始化新的 Git 仓库:

    git init my-new-repo
    

自动生成 CHANGELOG

conventional-changelog

使用 conventional-changelog 命令可以自动生成 CHANGELOG,要使用该命令,需要以下几个步骤:

  1. 安装或者更新 nodejsnpm(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 # 切换版本
  1. 安装 conventional-changelog-cli

npm install -g conventional-changelog-cli
  1. 更新 CHANGELOG.md

touch CHANGELOG.md  # 首次需手动创建
npx conventional-changelog -p angular -i CHANGELOG.md -s -r 0

github_changelog_generator

  1. 安装 Rubygithub_changelog_generator

brew install ruby
gem install github_changelog_generator
  1. 生成 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

  1. 使用 npm 安装 opencommit:

nvm use 18
npm install -g opencommit
  1. 配置 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
  1. 【可选配置】

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 数
  1. 使用 oco 命令生成 commit

git add .
oco

更多参考资源

1. 常用 Git 命令总结

2. Git 教程

3. Git reset 命令的使用

4. 细读 Git | 让你弄懂 origin、HEAD、FETCH_HEAD 相关内容

5. GitHub fork 与 pull request

6. git rebase,看这一篇就够了

7. 【git 整理提交】git rebase -i 命令详解

8. Git Reset 三种模式

9. Cz 工具集使用介绍 - 规范 Git 提交说明

10. git commit 、CHANGELOG 和版本发布的标准自动化

11. Ubuntu 安装最新版本 NodeJs 和 Npm 的方法