Date
Nov. 21st, 2024
 
2024年 10月 21日

Post: Git Handbook

Git Handbook

Published 17:01 Jan 15, 2022.

Created by @ezra. Categorized in #Version Control, and tagged as #Cheatsheet, #Git, #Version Control.

Source format: Asciidoc

Table of Content

基本操作

工作流

workflow

常见的 Workflow 有三种情况:

单分支

一般见于个人使用场景, 所有工作内容均在主分支进行和完成。

C----C----C----C----C----  (main)

Github Flow

一般见于小型团队使用场景, 每项工作内容均在新建的针对性的短期分支 (如 feturebug-fix 等) 进行, 并最终合并到主分支。

    C---C---C       C         (feature/bugfix)
  /           \   /   \
C---------------C------C----  (main)

Git Flow

一般见于各类团队使用场景, 分支类型根据生命周期分为长期分支和短期分支:

  • 长期分支包括:

    • 主分支 (main/master), 仅包含删节的提交记录和节点

    • 开发分支 (dev/devlop/stage), 由主分支 fork 产生, 将包含项目的完整提交记录和节点, 最终合并入主分支

  • 短期分支包括:

    • 新增功能分支 (feature-*), 由开发分支 fork 产生, 最终合并入开发分支

    • 缺陷修复分支 (bugfix-/hotfix-), 由开发分支 fork 产生, 最终合并入主分支与开发分支

    • 版本发布分支 (release-*), 由开发分支 fork 产生, 可以进行缺陷修复、文档生成等发布相关变动, 完成后合并入主分支, 并合并回开发分支

    • 本地临时分支

长期分支并不用于日常维护, 仅当临时的短期分支中的变更完成后, 才会依次合并入长期分支。

        C-----C                    (feature/bugfix)
       /       \
      /         \      C           (release)
     /           \   /   \
    C--------------C      \        (dev/develop/stage)
  /                        \
C---------------------------C----  (main/master)
feature-branches.svg hotfix-branches.svg release-branches.svg

其它

当然, 实践中还有很多其它方案, 例如 Gitlab Flow 等, 此处不一一列举。

如果使用 git-flow 工具, 可以参考 git-flow-cheatsheet

如何初始化当前目录为 Git 仓库

git init

如何添加远程仓库

git add remote https:/domain.com/repo.git

如何查看远程仓库信息

git remote -v
git remote show origin

如何克隆仓库

git clone https://domain.com/repo.git
git clone https://domain.com/repo.git path/to/folder/

如何查看 Git 仓库配置信息

git config -l

如何查看全局 Git 配置信息

git config --global -l

如何配置 Git 仓库姓名和邮箱

git config user.name "Ezra"
git config user.email "[email protected]"

如何全局配置姓名和邮箱

git config --global user.name "Ezra"
git config --global user.email "[email protected]"

如何记住账号密码等认证信息

git config credential.helper cache
git config credential.helper store

如何更改默认编辑器

git config --global core.editor "nano"

如何全局配置记住认证信息

git config --globan credential.helper cache
git config --global credential.helper store

如何取消某条 Git 仓库配置

git config unset config-label

例如取消记住认证信息:

git config unset credential.helper

如何取消某条 Git 全局配置

git config --global unset config-label

如何暂存文件

git add path/to/file.ext
git add .
git add --all
git add *.txt

如何在暂存文件时进行对比选择

git add -p

如何取消暂存文件

git reset path/to/file.ext
git reset *

如何撤销修改

如何撤销未暂存文件的修改

git checkout path/to/file.ext

如何撤销已暂存文件的修改

git reset --hard path/to/file.ext
git reset --hard HEAD

如何查看 Git 仓库状态

git status

如何对比文件修改信息

git diff
git diff path/to/file.ext
git diff --staged

如何提交修改

git commit
git commit -m "commit message here"

如何设置提交信息模板

git config commit.template /absolute/path/to/template/file
git config commit.template /relative/path/from/repository/root

如何撰写一条合适的提交记录

一条合适的提交记录应该包含:

type: subject

body (optional)

footer (optional)
Type/类型
  • feat - 一个新增功能

  • fix - 一个缺陷修复

  • docs - 文档变动

  • style - 样式调整

  • refactor - 非缺陷修复和新增功能的代码变动

  • test - 测试相关变动

  • chore - 更新编译构建、项目配置等

Subject/主题

简要的描述修改内容, 一般在 50 个字符以内较好。

Body/详情

这一部分用于解释你进行的修改和修改的原因等内容。当然, 并非所有的提交都需要这一部分进行说明。

BodySubject 之间必须要留有一行空行。同时, Body 部分每一行应限制在 72 个字符以内。

Footer/脚注

这一部分也是可选的, 一般在你使用了缺陷追踪工具时会在这里添加和引用缺陷 ID 等信息。

示例
fix: crash when click on the home button

Every time when user click on the home button, the app crash. This bug no longer exists.

Resolves: #123
See also: #456, #789

如何查看提交记录

git log
git log --oneline
git log --graph

如何查看所有分支的提交记录

git log --all

如何查看远程仓库的提交记录

git log origin/main

如何修正最近一次的提交信息

git commit --amend

如何修正最近一次的提交信息并添加新文件

git commit --amend -a

如何回滚提交

git revert HEAD
git revert commit-id-here

如何在 Git 中重命名文件

git mv oldname.ext newname.ext

如何在 Git 中删除文件

git rm path/to/file/or/folder
git rm --cached path/to/file/or/folder

如何推送到远程仓库

git push

如何强制推送

git push -f

如何从远程仓库拉取并自动与本地合并

git pull

如何配置偏离分支合并策略

git config pull.rebase false
git config pull.ff only

如何从远程仓库拉取但不自动与本地合并

git fetch

如何查看分支列表

git branch
git branch -r

如何创建新本地分支

git branch branch-name-here

如何重命名分支

git branch -m old-branch-name new-branch-name

如何切换分支

git checkout branch-name-here

如何创建并切换到新本地分支

git checkout -b branch-name-here

如何将本地新分支推送到远程仓库

git push -u origin branch-name-here

如何删除分支

如何删除本地分支

git branch -d branch-name-here

如何删除远程分支

git push --delete origin branch-name-here

如何合并其它分支到当前分支

git merge branch-name-here

如何终止正在进行的合并

git merge --abort

如何新建标签

git tag 1.0.0

如何将标签推送到远程仓库

git push --tags

如何删除标签

如何删除本地标签

git tag -d 1.0.0

如何删除远程仓库的标签

git push --delete tag 1.0.0

如何对提交记录进行变基: 遴选、压平、编辑等操作

选择要操作的提交记录的前一条记录进行变基:

git rebase -i previous-commit-id

# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit

此操作会将指定 commit-id 后 (不包括该 commit-id 所对应的提交记录) 所有提交记录提供给用户进行遴选、压平、编辑等操作。

Warning

请注意, 除非个人使用, 否则不要用于操作公开和远程的分支、提交记录。

例如, 我们当前有如下提交记录:

git log --oneline

612f2f7 This commit should not be squashed
a3fb05d This commit should be squashed
d84b05d This commit should be updated
36d15de Rebase from here
17692d1 Did some more stuff
e647334 Another Commit
2e30df6 Initial commit

此时假设我们希望的需求是:

  • 保留最新的 612f2f7 This commit should not be squashed 记录

  • 但将 a3fb05d This commit should be squashed 压平后合并入 d84b05d This commit should be updated

  • 并更新为新的日志信息

那么我们应当选取其前一条记录, 也即 36d15de Rebase from here, 进行变基操作:

git rebase -i 36d15de

在出现的编辑器中, 提交记录的顺序与 git log 中的顺序是相反的。

此时根据我们的需求, 我们应当:

  • 保持最新的 612f2f7 This commit should not be squashed 记录为 pick 状态

  • 修改要被合并的 a3fb05d This commit should be squashedsquash/s 状态

  • 修改其合并入的 d84b05d This commit should be updatededit/e 状态

    • (如果不需要更新此处的日志信息, 则修改状态为 pick/p 即可)

然后保存。

edit d84b05d This commit should be updated
squash a3fb05d This commit should be squashed
pick 612f2f7 This commit should not be squashed

此处由于我们标记了 edit 状态, 保存后会要求执行 git commit --amend 命令编辑日志信息, 我们将根据需求修改日志信息, 例如 A updated commit message, 并保存。

再次查看日志, 会发现记录 a3fb05d This commit should be squashed 已经被压平合并到其前一条记录中, 且该记录的日志信息也已经变更:

git log --oneline

612f2f7 This commit should not be squashed
d84b05d A updated commit message
ac60234 Yet another commit
36d15de Rebase from here
17692d1 Did some more stuff
e647334 Another Commit
2e30df6 Initial commit

如何对分支进行变基

git rebase base-branch-name-here

如何终止变基操作

git rebase --abort

如何继续变基操作

git rebase --continue

如何区分合并与变基

merging rebasing

如何进行打包发布

git archive --format=tar --prefix=proj-1.2.3/ HEAD

如何同时操作多个 Git 仓库

例如使所有子目录下的 Git 仓库统一执行拉取操作:

find . -type d -exec git --git-dir={}/.git --work-tree=$PWD/{} pull origin master \;

操作实例

一般流程

准备工作

接手新项目后, 首先要在本地安装 git 工具、客户端等工具, 例如:

sudo apt install git
sudo yum install git
sudo dnf install git
sudo pacman -Sy git

克隆项目仓库到本地:

git clone https://domain.com/path/to/repo.git

然后进入克隆好的本地仓库:

cd repo
初始化

如果是全新构建的项目, 先创建开发分支 stage 并推送到服务器:

git checkout -b stage

此时我们将处于 stage 分支中。

git push -u origin stage

分支切换

进入新克隆的项目时我们默认处于主分支中, 一般为 mastermain 分支。

根据团队要求的不同, 我们可能需要进行分支切换和创建。通常来说, 我们会切换到 dev/devlop/stage 分支, 并基于该分支创建新的短期工作分支:

git checkout stage
git checkout -b feature-0206

此时可以在 feature-0206 分支开始工作了。

提交变动

待工作告一段落时, 首先需要对修改的文件进行添加暂存, 最简单的是全部暂存:

git add --all

如果需要暂存特定文件:

git add path/to/file.txt
git add path/*.txt

如果修改的内容较多, 可以进行对比:

git diff path/to/file.txt

并选择其中部分进行暂存:

git add -p path/to/file.txt

如果意外暂存了错误的文件, 可以对其状态进行重置:

git reset path/to/file.txt

如果不需要暂存某文件, 可以通过增加 .gitignore 文件进行忽略, 或者:

git rm path/to/file.txt

接下来, 需要本地提交这些暂存:

git commit -m "some commit message here"

通过反复暂存和提交后, 所有修改均已在本地提交, 此时应检查日志:

git log

你将看到类似下面的结果:

commit 655acf96e613aa38a25c62a969cee56d2ccf9cdd (HEAD -> master)
Author: Ezra <[email protected]>
Date:   Tue Feb 6 14:16:47 2022 +0800

    API doc improvements

commit e4b480e2446570d2aa32543c930bd37121ae9dc9
Author: Ezra <[email protected]>
Date:   Tue Feb 6 14:16:17 2022 +0800

    change api document

commit 85d81525fc07db28de1304b4743f8bea1a300db8
Author: Ezra <[email protected]>
Date:   Tue Feb 6 13:51:18 2022 +0800

    API

commit ccf8ad5c9758c9d07212ae7cf11aa2bc5ffc4c8d
Author: Ezra <[email protected]>
Date:   Tue Feb 6 13:50:38 2022 +0800

    dev files

commit 71d35b059bfd1951e9a2310eadda12c4f26a3d61
Author: Ezra <[email protected]>
Date:   Tue Feb 6 13:50:07 2022 +0800

    README

commit 18031d7cf833118200f9aae247680bdb91a10f87
Author: Ezra <[email protected]>
Date:   Tue Feb 6 13:47:57 2022 +0800

    init
遴选压平

在此示例中, 最近的三条提交记录均为对 API.md 文件的同一类型的修改, 因此我们希望将如下三条记录合并为一条:

  • 655acf96e613aa38a25c62a969cee56d2ccf9cdd API doc improvements

  • e4b480e2446570d2aa32543c930bd37121ae9dc9 change api document

  • 85d81525fc07db28de1304b4743f8bea1a300db8 API

这时我们需要找到前一条记录, 假设为 ccf8ad5c9758c9d07212ae7cf11aa2bc5ffc4c8d dev files, 执行变基操作:

git rebase -i ccf8ad5c9758c9d07212ae7cf11aa2bc5ffc4c8d

执行上述指令后会出现编辑器:

  1 pick 85d8152 API
  2 pick e4b480e change api document
  3 pick 655acf9 API doc improvements
  4
  5 # Rebase ccf8ad5..655acf9 onto ccf8ad5 (3 commands)
  6 #
  7 # Commands:
  8 # p, pick <commit> = use commit
  9 # r, reword <commit> = use commit, but edit the commit message
 10 # e, edit <commit> = use commit, but stop for amending
 11 # s, squash <commit> = use commit, but meld into previous commit
 12 # f, fixup <commit> = like "squash", but discard this commit's log message
 13 # x, exec <command> = run command (the rest of the line) using shell
 14 # b, break = stop here (continue rebase later with 'git rebase --continue')
 15 # d, drop <commit> = remove commit
 16 # l, label <label> = label current HEAD with a name
 17 # t, reset <label> = reset HEAD to a label
 18 # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
 19 # .       create a merge commit using the original merge commit's
 20 # .       message (or the oneline, if no original merge commit was
 21 # .       specified). Use -c <commit> to reword the commit message.
 22 #
 23 # These lines can be re-ordered; they are executed from top to bottom.
 24 #
 25 # If you remove a line here THAT COMMIT WILL BE LOST.
 26 #
 27 # However, if you remove everything, the rebase will be aborted.
 28 #

由于我们要进行三条记录的合并, 因此将编辑器中第二行和第三行的 pick 改为 squashs 后保存:

  1 pick 85d8152 API
  2 s e4b480e change api document
  3 s 655acf9 API doc improvements

这一操作意味着, 我们将遴选第一条记录, 然后将第二条和第三条记录压平, 以便合并为同一条。

保存后, git 还会弹出新的编辑器, 要求我们对提交信息进行编辑:

  1 # This is a combination of 3 commits.
  2 # This is the 1st commit message:
  3
  4 API
  5
  6 # This is the commit message #2:
  7
  8 change api document
  9
 10 # This is the commit message #3:
 11
 12 API doc improvements
 13
 14 # Please enter the commit message for your changes. Lines starting
 15 # with '#' will be ignored, and an empty message aborts the commit.
 16 #
 17 # Date:      Tue Feb 8 13:51:18 2022 +0800
 18 #
 19 # interactive rebase in progress; onto ccf8ad5
 20 # Last commands done (3 commands done):
 21 #    squash e4b480e change api document
 22 #    squash 655acf9 API doc improvements
 23 # No commands remaining.
 24 # You are currently rebasing branch 'master' on 'ccf8ad5'.
 25 #
 26 # Changes to be committed:
 27 #   new file:   API.md
 28 #

此处我们根据实际情况对提交记录的信息进行编辑并保存, 当然, 也可以直接保存:

[detached HEAD 7a713af] API doc improvements
 Date: Tue Feb 8 13:51:18 2022 +0800
 1 file changed, 7 insertions(+)
 create mode 100644 API.md
Successfully rebased and updated refs/heads/master.

看到上面的信息时, 我们的变基操作已经成功, 接下来, 再次查看日志进行确认:

git log
commit 7a713af4d153bd5cb1a74f83161e496c5d4442a6 (HEAD -> master)
Author: Ezra <[email protected]>
Date:   Tue Feb 8 13:51:18 2022 +0800

    API doc improvements

commit ccf8ad5c9758c9d07212ae7cf11aa2bc5ffc4c8d
Author: Ezra <[email protected]>
Date:   Tue Feb 8 13:50:38 2022 +0800

    dev files

commit 71d35b059bfd1951e9a2310eadda12c4f26a3d61
Author: Ezra <[email protected]>
Date:   Tue Feb 8 13:50:07 2022 +0800

    README

commit 18031d7cf833118200f9aae247680bdb91a10f87
Author: Ezra <[email protected]>
Date:   Tue Feb 8 13:47:57 2022 +0800

    init

可以看到, 我们已经将最近的三条记录合并为同一条。

Warning

注意: 此压平变基操作仅适用于还未推送到服务器的本地提交记录, 如果你的操作涉及到远程服务器和其他项目成员, 可能会造成非常严重的不良后果!

拉取变更

现在, 我们可以拉取远程仓库的变更到本地:

git pull

依照 git 提示, 解决出现的冲突 (如果有)。

合并推送

接下来, 你可能需要根据实际请进行以下某一项操作:

  • 合并到 dev/devlop/stage 分支后推送到远程分支

  • 直接推送修改到远程服务器

第一种情况下, 首先需要切换到 dev/devlop/stage 分支, 然后进行合并:

git checkout stage
git merge feature-0206

你需要根据提示解决冲突 (如果有), 之后便可以进行推送了:

git push

第二种情况下, 直接推送当前的 feature-0206 分支到远程仓库:

git push -u origin feature-0206
Note

当然, 你也可以选择使用 git rebase branch_name 的方式进行分之合并, 以使记录保持线性, 但由于 dev/devlop/stage 分支通常都由多人同时使用, 因此并不建议使用这种方式合并。

下一次

在下次一次开始工作时, 切记先进行拉取操作后再根据情况进行新建分支、切换分支等工作。

强制推送

在某些特殊情况下 (例如你在使用属于你个人的私有仓库), 如果你真的需要对已经推送到服务器的提交记录进行遴选、压平和变基操作, 你可以强制推送你的变更到服务器:

git push -f

当然, 很多仓库服务器的默认设置是不允许强制推送的, 你提前需要在管理端开启。

Pinned Message
HOTODOGO
The Founder and CEO of Infeca Technology.
Developer, Designer, Blogger.
Big fan of Apple, Love of colour.
Feel free to contact me.
反曲点科技创始人和首席执行官。
开发、设计与写作皆为所长。
热爱苹果、钟情色彩。
随时恭候 垂询