Skip to content

Git 随记

警告:本文使用机翻且尚未经过审核,可能包含拼写错误或概念性错误。

本文假设读者理解基本的 Git 操作,包括 git init/clone/status/add/rm/stash/loggit reset 最好了解。

参考教程:

使用 Git - Github 文档

新建 repository 🥶

可以通过 git initgit clone <git-url> 创建仓库。

例如,如果通过从 GitHub 克隆创建了本地 Git 仓库,那么 git remote --verbose 将显示类似以下内容:

shell
(HTTPS)

 git remote -v
origin  https://<url>/<owner>/<repo> (fetch)
origin  https://<url>/<owner>/<repo> (push)

(SSH)

 git remote -v
origin  git@<url>:<owner>/<repo>.git (fetch)
origin  git@<url>:<owner>/<repo>.git (push)

(派生(fork)仓库)
 git remote -v
origin  git@<url>:<fork-owner>/<repo>.git (fetch)
origin  git@<url>:<fork-owner>/<repo>.git (push)
upstream  git@<url>:<owner>/<repo>.git (fetch)
upstream  git@<url>:<owner>/<repo>.git (push)

如何手动配置远程仓库?

假设我们有一个含有分支 master 的仓库,并且记录了远程仓库 origin

shell
git push -u origin master

此命令将本地分支 master 推送到远程分支 origin/master,并记录上游跟踪 master -> origin/master(可以通过 git status 检查跟踪状态)。

分支/仓库名称没有特殊含义。一般按照约定采用默认分支名称 master main,开发分支 dev;远程主仓库名称 upstream,个人仓库名称 origin

以下是一个工作流程示例。(偷自 nixpkgs/CONTRIBUTING.md)

shell
# 添加一个名为 `upstream` 的远程仓库跟踪
git remote add upstream <git-url>

# 确保你有来自 `upstream` 的最新更改
# 附加 `--depth 1` 如果只需要 `upstream` 的最新提交
git fetch upstream

# 基于 `upstream` 的 master 分支创建并切换到一个新分支 `update-hello`
# 等同于 `git checkout --branch/-b`
git switch -c update-hello upstream/master

# 或基于特定提交
git switch -c update-hello <the desired base commit>

然后可以开始在这个分支上添加你的更改到暂存区,并通过 git commit 保存。

shell
# 如果设置了 `EDITOR` 环境变量,这将触发相应的编辑器可执行文件并显示当前更改状态。
# 然后你应该在编辑器中编辑提交消息。
# 附加 `--message/-m "<提交消息>"` 可以直接提交更改而不打开编辑器
git commit

合并分支 / 解决合并冲突 / 拉取请求

TODO

标签

参考 https://git-scm.com/book/en/v2/Git-Basics-Tagging

shell
# Lightweight tag(不含说明信息)
git tag <tag> <commit>

# Annotated tag(类似 `git commit` 编辑标签说明)
git tag -a <tag> 
# 或者直接附加标签说明
git tag -a <tag> -m "<message>"

# 默认下 `git push` 不会推送标签到远程仓库
# 推送指定标签
git push <remote-repo> <tag> 
# 推送全部标签
git push <remote-repo> --tags

重写历史

重写历史过程中可以使用 git stash 贮藏当前工作区状态,使用非常简单,请自行 RTFM。

重写当前提交:

shell
# 重写当前提交(假设你不在 HEAD 分离模式下)。
# 如果你不想编辑提交消息,请附加 `--no-edit`。
git commit --amend

如果你想重写多个提交或远离 HEAD 的提交,可以使用交互式变基 (git rebase --interactive/-i)。这是一个强大的工具,允许你修改历史中的多个提交。以下是具体步骤:

shell
# 在历史中交互式地变基到某个提交
# 这应该是你想要编辑的最早提交的**父**提交。
# 使用 <TAB> 按钮来利用 shell 补全。
git rebase --interactive <commit>
# 重写包括根提交的全部历史
git rebase -i --root

eg.

shell
git rebase -i HEAD~4

这将打开一个编辑器,显示类似以下内容:

shell
pick f7f3f6d 修改配置文件
pick 310154e 更新用户界面
pick a5f4a0d 修复bug
pick 368fab6 添加新功能

# 变基 710f0f8..a5f4a0d 到 710f0f8
#
# 命令:
# p, pick <提交> = 使用提交
# r, reword <提交> = 使用提交,但修改提交信息
# e, edit <提交> = 使用提交,但停止以便修改
# s, squash <提交> = 使用提交,但融合到前一个提交
# f, fixup <提交> = 类似于 "squash",但丢弃提交信息
# x, exec <命令> = 使用 shell 运行命令(此行剩余部分)
# b, break = 在此处停止(使用 'git rebase --continue' 继续变基)
# d, drop <提交> = 删除提交
# l, label <label> = 为当前 HEAD 打上标记
# t, reset <label> = 重置 HEAD 到该标记
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       创建一个合并提交,使用原始的合并提交信息(如果没有指定
# .       原始提交,使用 oneline 作为提交信息)
#
# 可以对这些行重新排序;它们会从上至下被执行。
#
# 如果你在这里删除一行,对应的提交将会丢失。
#
# 然而,如果你删除全部内容,变基操作将会终止。
#

如果要修改提交内容,将想要修改的提交前的 "pick" 改为 "edit"(或简写 "e")。例如:

shell
edit f7f3f6d 修改配置文件
edit 310154e 更新用户界面
edit a5f4a0d 修复bug
pick 368fab6 添加新功能

然后 Git 将变基到第一个要修改的提交。

完成后,运行 git rebase --continue,Git 将应用所有更改,重写历史并变基到下一个要修改的提交。

你可以通过 git rebase --skip 跳过更改,或通过 git rebase --abort 退出变基模式。

注意:这个过程会改变提交的 SHA-1 值,因此要谨慎使用,特别是在已经推送到共享仓库的提交上。使用前确保与其他人沟通,并使用 git push --force-with-lease 推送。

切忌无脑 git push --force/-f !!!!!!(伤身而且影响你的仓库!这样会让你一年一篇顶会都没有!)

补充阅读:Is git push --force-with-lease always safe?

Git 提交的不可变性(immutable) / 重写历史的基本原理

在 Git 版本控制中,即使重写历史的相关操作看起来修改了一些提交,其实并不是覆写了这些提交,而是创建了新的提交替换到树上。

Git 中的每个提交(commit)都是不可变的(immutable),每个提交在创建时,根据提交时间/内容等属性,计算并记录不可变的散列值(hash value),作为唯一标识(补充:hash 前缀索引)。 当我们执行 git rebase、git commit --amend 等"重写历史"的操作时,实际上是创建了新的提交对象,这些新提交有: 新的散列值 新的提交时间 可能有新的父提交关系

eg.

原始提交

A -> B -> C (master)

执行 git commit --amend 修改最后一个提交

A -> B -> C'  (master)
        \-> C  (原提交仍然存在,但不可见)
  1. 保证数据完整性和安全性

  2. 便于分布式协作

  3. 支持版本回溯

  4. 防止历史被真正篡改

这就是为什么在进行这类操作时,Git 会警告我们要小心,特别是在已经推送到远程仓库的提交上执行这些操作时,因为这实际上会创建不同的提交历史线。

git diff - Myers 差分算法 / diff3 OR zdiff3

TODO

更多

Pro Git 书籍 高级 Git 用法