入坑 Git,看这一篇就够了

2023年 10月 15日 47.1k 0

🎼 前言

最近在网上刷到了电视剧中「神级」伪程序员的骚操作,就是通过「秋秋」把代码发给老板,吐槽者就说:「天哪,这么弔的程序员(盲敲键盘、用 Word 写代码并且还能运行)居然不用 Git...」

现实中,没有程序员不用 Git 没错吧?但又有多少人只停留在只会 GUI 操作上?

本文将以作者对 Git 的浅薄了解,带你入坑 Git(断断续续连着写了一个月)。

TL;DR

你可以不懂 Git 背后的实现原理,你可以不了解它所有的功能,但你一定要会用命令行,一定要知道除了 addcommitpush 之外的常用命令。

主要内容

适合读者

  • 想要了解 Git 的新人
  • 只会用 GUI 工具的同学

你将获得

  • Git 的基本概念
  • Git 的安装与基本设置
  • Git 客户端(纯文本、GUI、系统集成、插件)
  • 多用命令行,以及哪些常用命令,而不要强依赖 GUI(除了 Merge 推荐 JetBrains 之外)
  • 一些小技巧
  • 编辑历史

    日期 版本说明
    2023/10/15 V1

    🧬 Git 简介

    Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的版本控制系统(Version Control System,以下简称 VCS),和传统集中式如 CVS、SVN 不同,Git 的特点是分布式,不一不必依赖远程服务器。

    三个主要功能

    作为分布式版本控制系统,Git 的主要功能包括:

  • 版本控制:跟踪和管理代码的版本,方便开发人员对代码进行版本控制和回溯
  • 分支管理:开发人员可以在不同的分支上进行开发,并在需要时合并分支
  • 分布式协作:Git 特点是分布式加云端同步,方便开发者在不同设备上进行开发和协作
  • 四个工作区

    • Working Tree(工作区):即文件目录,存放所有已跟踪、未跟踪、已提交、未提交的文件
    • Index(暂存区,又叫 Stage):存放临时(待提交)的改动,它实际上是一个二进制文件 .git/index
    • Local Repository(本地仓库):「临时」的安全数据存放区域
    • Remote Repository(远程仓库):真正安全的数据存放区域,托管代码的服务器,最著名的就是 GitHub,公司都会自建服务器(多数用 GitLab),当然我们自己也可以自建服务器玩玩

    其中「本地仓库」是 Git 作为「分布式」版本控制软件的关键。

    四种文件状态

    状态 说明 图示
    Untracked 新增的文件,仅存在于 Working Tree
    New file Untracked 文件在 git add 后的状态,待 git commit 进入本地仓库,或 git restore --staged 重回 Untracked
    Modified 已登记在案(在本地仓库中或者在暂存库中)的文件最近又发生了改动
    Staged 已暂存,尚未提交,git add 后的文件状态,git restore --staged 返回之前的状态
    Committed git commit 后的文件状态

    说明一下:

  • 图示中表示状态的颜色,绿色表示「已暂存」,红色表示「未暂存」
  • 图示中 Powerline 状态(来自 powerlevel10k),? 表示 「Untracked 文件数」,+ 表示「已暂存文件数」,! 表示「未暂文件数」
  • 需要注意的是,「Untracked」一定「未暂存」,「New file」一定「已暂存」,「Modified」则有「已暂存的 Modified」和「未暂存的 Modified」两种(其实我认为可以把 Untracked 视为一种特殊的 New file 更好理解)
  • 另外,我没有找到 git push 之后文件的状态,难道是「remotely tracked」?但由于 Git 是一个分布式的 VCS,可能这就不是很重要吧。

    三个基本操作(三板斧)

    上图同时也体现了 Git 几个工作区之间的状态流转所用到的关键命令,同集中式 CVS 的一次 commit 不同,Git 需要经历 addcommitpush 三步,也就是被戏称为 Git 三板斧的三个基本操作。

    这对当时经历 SVN 到 Git 迁移的程序员来说,闹出过不少笑话。记得当时有位老兄是这么说的:「在 Git,就像对女朋友一样,你不能只是『承诺』,那没用,必须『推到』,那才是真的有用。」

  • git add:将文件的改动添加到暂存区
  • git commit:将暂存区的改动,提交到本地仓库
  • git push:将本地仓库的当前分支,提交到远程仓库对应的分支
  • 注意,这三板斧是一个链式的线性操作。

    五种 URL 协议

    格式 例子 优点 缺点 适用场景
    HTTP/HTTPS 协议 https://github.com/user/repo.git 简单易用 传输速度较慢;私有仓库,可能需要用户名和密码 大多数场景
    SSH 协议 git@github.com:user/repo.git 安全可靠 设置相对较复杂 需要频繁进行 Git 操作的场景
    Git 协议 git://github.com/user/repo.git 速度快、安全性高(比 HTTP/HTTPS 和 SSH 协议具有更快的传输速度,并且不需要对外暴露仓库的读写权限) 不适用于排除防火墙的企业环境 -
    文件路径 /path/to/repository.git 无网络传输,速度非常快 仅适用于本地操作 开发和测试环境
    子模块 URL 任何以上格式 非常灵活(子模块 URL 可以指向任何 Git 仓库) 对于代码维护来说并不利 代码复用和模块化开发

    .git 目录

    Git 的所有信息都在 .git 目录下,我们看一下它下面有些什么:

    名称 类型 说明
    COMMIT_EDITMSG 文本文件 保存着最近一次的提交信息,作用仅仅只是给用户一个参考
    HEAD 文本文件 记录了当前分支的指向
    config 文本文件 当前仓库的本地配置,会与全局的进行合并,本地的同名配置优先
    description 文本文件 项目描述(但我看不懂它的来源和作用)
    hooks 目录 默认只有一堆钩子的 sample 文件
    index 二进制文件 暂存区
    info 目录 默认只有一个 exclude 文件
    logs 目录 操作记录,包含文件 HEAD(就是 git reflog 来源)、refs/heads 目录、refs/remotes 目录
    objects 目录 存放所有的 Git 对象,哈希值一共 40 位,前 2 位作为文件夹名称,后 38 位作为对象文件名
    tags 目录 存储 Git 各种引用,包含三个目录 heads(所有的本地分支)、tags(所有的本地 Tag)、remotes 下只有 origin/HEAD

    🕋 安装与设置

    安装

    你拿到 Mac 后,可能已经预装好了 Apple Git:

    一般比 官方版本 的要低,想要安装最新版的,用 homebrew 安装:

    brew install git
    

    但也可能没有 Apple Git,而 homebrew 自己的安装又需要用到 git,事情就变成「安装 git 需要现有 git」的尴尬场面,可以按照提示先安装命令行工具。

    安装后,重启 Terminal,即可看到安装了最新版的 git

  • git -v 看到的是 Apple 预装的 2.39.2
  • which git 显示位置为 /user/bin/git
  • brew install git 安装官方 Git
  • git -v 看到的仍是原来的 Apple Git
  • 重启 Terminal 后,git -v 看到的是新装版本
  • which git 显示位置为 /opt/homebrew/bin/git
  • 想要重新用回 Apple Git,brew uninstall git;临时用回 Apple Git,可以用全路径 /usr/bin/git

    设置

    SSH

    使用 SSH 协议进行 Git 操作,安全可靠,速度相对较快,需要配置 SSH 密钥,同时需要在 Git 服务器(GitHub 或 GitLab 等)上添加对应的公钥。

    一般在设置页面上链有说明文档,完全不必担心怎么设置。

    以 Mac + GitHub 为例:

  • gcl git@...尝试 clone SSH Url,失败
  • ssh-keygen -t ed25519 -C "em@il" 生成跟邮箱对应的 SSH Key
  • pbcopy < ~/.ssh/id_ed25519.pub 拷贝公钥内容至剪贴板,去 GitHub Settings → SSH and GPG Keys → New SSH 黏贴
  • ssh -T git@github.com 测试(可以跳过)
  • 再次 clone,成功
  • 用户名和邮箱

    Git 会在适当的时候提示你,比如当你需要提交代码的时候,它才会问你要账号信息:

    大部分公司的 Git 规范一般都会要求用公司的邮箱,因此预先设置用户名和邮箱尤为重要:

    git config --global user.name 我是谁
    git config --global user.email em@li.com
    

    主分支名

    Apple Git 的主分支名为 main,Github 也已经把主分支名改成了 main,但官方的 Git 在 git init 的时候还会有警告:

    按它的说明改一下:

    git config --global init.defaultBranch main
    

    全局 Ignore

    Git 仓库一定会有 .gitignore,但那应当仅限于操作系统自动生成的文件、构建和单侧产物等等。

    我们也可以增加「全局 Ignore」 ~/.gitignore_global,比如临时存放只有自己会用的一些文件,不想删,不想挪位置,不想提交。

    比如我有一些通用的模板代码放在某个目录下,然后软链接进来;又或者经常性地,我会将「临时」删除的文件 mv 到某个格式的目录下,最后再一起「硬」删除。

    我的习惯是一定忽略四个连续 _ 打头的文件或目录,如下操作:

    cd ~
    touch .gitignore_global
    echo "____*" > .gitignore_global
    git config --global core.excludesFile ~/.gitignore_global
    

    Config 概览

    以上所有的全局配置都存入 ~/.gitconfig,可以直接看文件内容,也可以敲命令 git config --list --global 查看。

    在 Git 本地仓库下的 .git/config 中存着当前仓库的设置信息,Git 读取配置(git config --list)会与全局的进行合并。

    🖲 Git 客户端

    这里列数了我用过的一些客户端工具,更多可以看官方的文档 [《GUI Clients》](Git 官方收录了一git-scm.com/downloads/g…

    纯文本客户端

    tig ★★★★★

    名字取得好,看 Git 信息必备,我最常用的 Git 工具。

    • URL:jonas.github.io/tig
    • 平台:Mac / Linux
    • 安装:brew install tig
    • 推荐指数:★★★★★

    lazygit ★★★★★

    Github 39K Star;全功能 Git 客户端。

    • URL:github.com/jesseduffie… 39K Star
    • 平台:Mac / Linux
    • 安装:brew install lazygit
    • 推荐指数:★★★★★

    gitui ★★☆

    注意不是 git-gui

    比较专注于待提交,不支持鼠标事件是个遗憾。

    • URL:github.com/extrawurst/… 14.1K Star
    • 免费
    • 平台:Mac / Linux
    • 安装:brew install gitui
    • 推荐指数:★★☆

    GUI 客户端

    珍爱生命,少用 GUI。

    GitKraken ★★★★☆

    号称「The world's most powerful suite of Git tools」,界面确实好看,功能也确实不错。

    免费版的不能用在企业自搭的 GitLab 或 BitBucket,会要求升级账号(要钱)。

    • URL:www.gitkraken.com
    • 收费:个人版免费;Pro 版 $4.95/月;Team ban $8.95/人月;企业版 $18.95/人月
    • 平台:Mac / Windows / Linux
    • 安装:下载安装
    • 推荐指数:★★★★☆

    SourceTree ★★★★

    免费的全功能 Git 客户端,UI 设计还不错。

    • URL:www.sourcetreeapp.com
    • 免费
    • 平台:Mac / Windows
    • 安装:下载安装
    • 推荐指数:★★★★

    Tower ★★★★

    跟 SourceTree 有点像,但个人感觉 Tower 更好看一些。

    • URL:www.git-tower.com
    • 收费:基础版 $69;Pro 版 $99;学生和教师可获取免费 License
    • 平台:Mac / Windows
    • 安装:下载安装
    • 推荐指数:★★★★

    SublimeMerge ★★★

    Sublime 作者出品,必属精品;但是一些基础的功能,比如切换主题,也需要收费。

    • URL:www.sublimemerge.com
    • 收费:$99
    • 平台:Mac / Windows / Linux
    • 安装:下载安装
    • 推荐指数:★★★

    Fork ★★☆

    和免费的 SourceTree 非常像,所以还是用 SourceTree 吧。

    • URL:git-fork.com
    • 收费:$49.99
    • 平台:Mac / Windows / Linux
    • 安装:下载安装
    • 推荐指数:★★☆

    SmartGit ★★☆

    师出名门,就是感觉性能..不能尽如人意。

    • URL:www.syntevo.com/smartgit
    • 收费:$5.90/Month
    • 平台:Mac / Windows / Linux
    • 安装:下载安装
    • 推荐指数:★★☆

    GitUp ★★★☆

    还蛮..特别的,关注在提交历史和版本计较的体验上。

    • URL:github.com/git-up/GitU…
    • 免费
    • 平台:Mac
    • 安装:下载安装
    • 推荐指数:★★★☆

    其他

    另外还有好多好多,没有一一试用,简单罗列一下:

    • git-it
    • Gitfox
    • GitBlade
    • GitX
    • Gittyup

    系统集成

    GitFinder ★★☆

    未曾试用,喜欢类似 TortoiseGit 体验的同学可以试试。

    但 Mac 系统有 .DS_Store,我并不喜欢用 Finder 开发目录。

    • URL:gitfinder.com
    • 收费:$29.95
    • 平台:Mac
    • 安装:下载安装
    • 推荐指数:★★☆

    TortoiseGit ★★★★

    用过它的两个兄长 TortoiseCVS 和 TortoiseSVN,Windows 用户值得拥有。

    • URL:tortoisegit.org
    • 免费
    • 平台:Windows
    • 安装:下载安装
    • 推荐指数:★★★★

    插件

    IDE 或编辑器的 Git 插件应该是用的最多的 Git 客户端了吧。

    JetBrains 家族 ★★★★★

    JB 的 Git 体验是我用过最棒的,直观的修改状态提示(文件树和编辑器)、方便的快捷键、丰富的菜单项、无与伦比的 Diff Merge,正是它的 Git 体验,让我毫不犹豫地弃 Eclipse 而改投 WebStorm 的怀抱。

    即使通常使用命令行进行大部分的 Git 操作,在遇到需要人肉 Merge 的时候,我一定会用 JB 的 Git。

    • URL:-
    • 收费:IDE 内置插件
    • 平台:Mac / Linux / Windows
    • 安装:IDE 内置
    • 推荐指数:★★★★★

    VSCode - GitLens ★★★★★

    VSCode 下操作 Git 不二之选。

    • URL:marketplace.visualstudio.com/items?itemN…
    • 免费
    • 平台:Mac / Linux / Windows
    • 安装:插件市场
    • 推荐指数:★★★★★

    Eclipse - EGit ★★★

    Eclipse 内置插件,喜欢 Eclipse 一定会用它,但体验真的没有 JB 家族的好。

    • URL:eclipse.dev/egit
    • 免费
    • 平台:Mac / Linux / Windows
    • 安装:IDE 内置
    • 推荐指数:★★★

    汇总

    纯文本 UI 推荐 tiglazygit;标准 GUI 客户端推荐 GitKraken 和 SourceTree;系统集成看个人喜好吧;IDE 推荐 JB 家族和 VSCode 的 GitLens。

    🚥 命令行

    珍爱生命,多用命令行。不要只用 GUI,不要只会「三板斧」。

    GUI 工具固然方便,优秀的程序员更应该对 Git 常用命令了如指掌。

    OMZ & Fig

    说到命令行,又不得不提 Oh My Zsh 和 Fig。

    OMZ 的 Git 插件 提供了近 200 个简化命令别名,能够极大地提升效率。

    Fig 能够为正在输入的命令及参数提供直观友好的提示,是了解命令及参数的最好老师:

    仓库操作

    操作 命令 OMZ 短名
    初始化 git init -
    克隆 git clone gcl
    配置(仅对当前仓库) git config -
    获取当前仓库配置信息 git config --list gcf
    添加远程 git remote add gra
    设置远程 git remote set-url grset
    移除远程 git remote rm grrm
    更名远程 git remote rename grmv

    分支操作

    操作 命令 OMZ 短名
    查看本地分支列表 git branch gb
    查看远程分支列表 git branch -r gbr
    查看本地 + 远程分支列表 git branch -a gba
    新切分支 git checkout -b gcb
    切换分支 git checkout gco
    新切分支 2 git switch -c gswc
    切换分支2 git switch gsw
    切回主分支 git checkout 需分清 main 还是 master gcm
    切回主分支 2 git switch 需分清 main 还是 master gswm
    重命名分支 git branch -m gbm
    删除分支 git branch -d gbd
    删除分支(强删) git branch -D gbD
    清除本地残留的远程分支记录(否则命令行提示会干扰 git checkout git remote prune origin -
    合并主分支 git merge master 或 main gmom

    变更操作

    | 操作 | 命令 | OMZ 短名 |
    | --- | --- | --- | --- |
    | 从远程仓库下载最新变更至本地仓库 | git fetch | gf |
    | git fetch 之前清理残留的远程分支记录 | git fetch --all --prune | gfa |
    | 从远程仓库拉取并合并 | git pull | gl |
    | 合并分支 | git merge | gm |
    | 进 Stage | git add | ga |
    | 提交 | git commit -m | gcmsg |
    | 进 State + 提交一步到位 | git commit -am | gcam |
    | 推到远程仓库(需新建远程分支) | git push -u origin | gpsup 可以自感知当前分支名 |
    | 推到远程仓库(已有远程分支) | git push | gp |

    文件操作

    操作 命令 OMZ 短名
    删除文件或目录 git rm grm
    移动文件或目录 git mv -

    查看信息

    操作 命令 OMZ 短名
    打印远程信息 git remote -v grv
    查看工作目录的状态(那些文件有改动、哪些文件未跟踪) git status gst
    查看 Commit 之间的文件内容差异 git diff gd
    查看提交日志 git log -

    OMZ 提供了一系列显示效果更好的日志查看便捷命令,推荐 glolglola,比如 glol 的效果:

    后悔药

    操作 命令 OMZ 短名
    重置 HEAD 指针 git reset grh
    回滚 git revert grev
    删除远程分支 / Tag git push -d origin git push origin : gpod

    其他

    操作 命令 OMZ 短名
    查看 Tag 列表 git tag -
    新建 Tag git tag -
    推送 Tag git push -

    📐 项目规范

    .gitignore

    护住你的节操!避免没用的文件进入版本控制被人鄙视。

    哪些需要被忽略

    哪些是「没用」的文件:

    • 下载的依赖,如 node_modules
    • 系统自动生成的文件,如 Mac 的 .DS_Store,Windows 的 Thumbs.db
    • 编码工具自动生成的项目配置
      • JB:.idea
      • VSCode:.vscode
      • Eclipse:.settings.classpath.project
      • Vim 的 .xx.swap
    • 构建产物

    提交无用文件的危害

  • 增加「代码」量,但也降低了代码提交者的「码品」,特别是下载的依赖
  • 产生没必要的冲突,尤其是系统文件、构建结果
  • 个人习惯困扰,尤其是编码工具自动生成的项目配置
  • 干扰全局搜索
  • 如何写一个好的 .gitignore

    一个好的 .gitignore 应该具有足够的包容性和前瞻性。

    也就是说,在现在和将来的时间内,对不同习惯的开发者,大多数无用的文件都会被忽略,大多数有用的文件都不会被忽略。不需要频繁调整 .gitignore,也不会经常有「无用」文件被不小心提交上来。

    我见过很多人对 .gitignore 的处理是做加法,罗列一大堆的「无用」文件,但总有漏网之鱼。

    我更建议的是 做减法,特别是 .xx,直接全忽略了,然后再添加例外。

    我的 .gitignore

    # common
    .*
    !.*ignore
    !.*.yml
    !.*.yaml
    !.*rc
    !.*rc.*
    !.husky
    *.log*
    
    # dev
    
    node_modules/
    
    # generated
    
    build/
    coverage/
    *.lock
    *-lock.json
    *-lock.yaml
    
  • .* 全部忽略了,但保留了所有 .xxignore.xxrc.xx.yml.xx.yaml 这些配置文件
  • 若有特殊的 .xxrc 文件(比如有的时候可能需要 .npmrc 因为它里边带来私人 Token)需要忽略,写到 !.*rc 下面就行了
  • 反对 lock(不要理我)
  • 以上文件能够覆盖绝大多数的前端项目,至于后端项目,也差不多,稍微改改就行了。

    .gitignore 的语法和更多说明看 官方说明。

    提交规范

    如何设置看我另一篇文章 《你的前端工程,二哈和它的朋友们都安排上了么?》 的「commitlint」一章。

    Git 从最初的设计就要求每次提交(commit)都必须带说明(commit message),以方便回顾和回滚。

    但每个人写 commit message 的习惯和风格都不一样,有的人写的很详细,有的人却图省事,一水的「update」或「提交」或「更新」。

    其实,Git message 是有规范的,那就是 Conventional Commits,规范的具体细节这里就不展开了。

    光有规范不行,还必须有工具才行 —— commitlint。

    我一直说「工具是最好的老师」,设置 commitlint 后,你再提交就会受到良好的「教育」了。

    分支规范

    我从来没有想到有人会用中文命名分支...

    事实上,Git 分支可以是任何有效字符,甚至都可以用「恶魔鸡」:

    入坑 Git,看这一篇就够了-1

    分支名书写规范

    然而「给你自由过了火」就会犯罪,所以我们需要有一定的 书写规范:

    • 统一小写字母
    • / 归类
    • 连字符用 -

    分支名组成规范

    默认 Git 只有一个常驻远程分支,也就是主分支,有些公司或个人可能会根据自己的基建定义多个常驻分支,比如 devtest 等,比如我的某个项目下有 lerna/pub 常驻分支,专门用来发包的。

    其余的分支,都应该是临时分支,它们的宿命就是被最终合并到主分支,并消失在历史的长河中。

    临时分支的命名规范,建议为 类型/版本-简述,仅允许小写字母、数字、-/,不允许其他特殊字符,不允许大写,不允许中文。

    版本和简述,不一定两个都要,但至少需要有一个,描述不要超过 32 个字符。

    根据分支处理的事务,类型可以有以下几种:

    前缀 作用 代码修改 升级版本号 举例
    feature/ 功能需求分支,新增、更新、移除某功能 视需求,代码量一般不会很少 minor feature/1.1.0-add-user-management
    fix/ Bug 修复分支,修复问题 代码量不多 patch fix/1.1.1-user-display-name
    chore/ 工程分支,与项目构建、编程规范等有关的 一般不涉及 src 下的代码 majorminor chore/1.2.0-husky-and-friends
    docs/ 文档分支,更新说明性的文档,README 和 doc/ 目录下的文件 仅 Markdown 文件 minorpatch docs/1.3.0-standards-for-dev
    test/ 测试分支,单元测试,Demo 等 不影响生产 minorpatch test/1.4.0-util-fetcher
    perf/ 性能分支,优化性能有关问题 不涉及业务逻辑 minorpatch perf/1.4.1-user-list-scroll
    refactor/ 重构分支,重构代码 不涉及业务逻辑,但容易出 Regression Bug minor refactor/1.5.0-user-as-model

    🙋 FAQ

    ❓ 挠人的 passphrase 提示

    如果生成 SSH Key 时,如果设置了 passphrase,那末使用 SSH 协议克隆远程仓库时,每次 pull / push 都会提示「Enter passphrase for key '/Users/xx/.ssh/id_rsa'」。

    入坑 Git,看这一篇就够了-2

    这个很烦。

    可以在命令行输入 sh-keygen -p进行重新设置,直接回车输入为空,就没有密码了(只是删除密码 ssh 的 pub 不会改变)。

    ❓ 挠人的 git pull 警告

    按它说的设置一下:

    git config --global pull.rebase false
    

    ❓ 如何解决冲突

    多人开发一定会合并分支,合并就难免冲突,即使一个人维护一个仓库,不同的需求根据时间先后,也难免自己冲突:

    入坑 Git,看这一篇就够了-3

    入坑 Git,看这一篇就够了-4

    注意它的提示,主要两个意思:

  • 如果不想合并了,执行 git merge --abort,将回到执行 git merge 之前的状态
  • 首先你需要改代码,把 Git 加的那些 Merge 标记清理干净,然后执行 git commit -am 声明已完成
  • 很多人看到冲突就慌了(尤其实际场景中,尤其当单个文件冲突量大的时候)。合并冲突,我只用 JB(右键菜单 Git → Resolve Conflict):

    入坑 Git,看这一篇就够了-5

    ❓ pull vs fetch

    实在想不通居然有人面试会问这种问题 1。

    入坑 Git,看这一篇就够了-6

    命令 --help 文档 理解
    pull Fetch from and integrate with another repository or a local branch fetch 要多做一步,就是把下载来的「对象」合并到当前分支
    fetch Download objects and refs from another repository 仅下载,下载到本地库,注意下载的是「对象」

    可以简单理解 pull == fetch + merge

    ❓ reset vs revert

    实在想不通居然有人面试会问这种问题 2。

    命令 --help 文档 理解
    reset 「Reset current HEAD to the specified state」 重置 HEAD 指针
    revert 「Revert some existing commits」 撤销到某个 commit 的所有操作,并生成一个新的 commit

    下图展示了 reset 对 Working Tree 的状态影响:

    入坑 Git,看这一篇就够了-7

    由于 reset 的作用只是重置了一下 HEAD 指针,对于已经 push 了的变更,这个操作是无感知的,并没有产生实际的「回滚」,pull 一下又都回来了:

    对某一个或者几个的 Commit 方向做一次它的变更,然后生成新的 Commit,对于未涉及的 Commit 不影响。

    注意:不是「回滚到」某个 Commit,而是回滚某个 Commit 的变更。

    ❓ merge vs rebase

    实在想不通居然有人面试会问这种问题 3。

    除了为写本文做的实验之外,实际场景我用 rebase 的次数一个手就可以数过来...

    从下面这张图(采自 GitKraken),可以看出两者的一些区别:

  • Merge 需要创建一个合并点,即多一条合并的 Commit,Rebase 不会添加 Commit
  • Merge 不改变时间线,而 Rebase 的当前分支会整体时间线放在被 Rebase 的分支之后(该 Commit 记录的 CommitDate 被修改)
  • 入坑 Git,看这一篇就够了-8

    更加抽象但可能更好理解的区别可以看下图,可以看出,Rebase 更像是对变更做 剪切复制 操作。

    入坑 Git,看这一篇就够了-9

    有些人认为 Rebase 好,甚至说应该禁止 merge。我却不赞成他们提出的所谓「分支多,难看懂」的原因。说句实在话,有多少人一天到晚去翻历史分支的?我反倒认为多条时间线,在特定的时刻交汇,是很真实的美的体现。

    但 Rebase 有更厉害的用处,看下面一节。

    ❓ 如何修改提交者信息?

    公司要求提交信息是公司的邮箱,但你可能有自己的开源项目,如果忘记设置 user.nameuser.email 就会产生不合场景的提交信息。

    如果公司的 Git 服务设置禁止非公司域邮箱提交,最终可能导致无法 push。

    也有可能你在自己的私人项目下以公司邮箱的身份提交了。

    修改最近一次 commit 作者

    提交一次即刻觉醒,及时止损。

    入坑 Git,看这一篇就够了-10

    修正当前项目(非全局)下的用户信息(也可以直接编辑 .git/config):

    git config user.name 
    git config user.email 
    

    然后执行 git commit --amend --reset-author 即可:

    入坑 Git,看这一篇就够了-11

    修改多次 Commit 作者

    但更多的时候可能是,已经错误提交了很多次,在最终 push 的时候被远程拒绝了。

    参考文档:dev.to/brayanarrie…

    如下,在 GitHub 上的项目,我需要把「驳是」都改成「Jianchun Wang」。

    tig 查找并拷贝之前的正确的 Commit ID:

    入坑 Git,看这一篇就够了-12

    以上,有 7 次的错误提交,并拷贝了这 7 次之前的 Commit ID。

    执行命令:

    git rebase --interactive --merge 
    
    # 或
    git rebase -i -m 
    

    在弹出的 Vim 界面中将需要修改的 Commit 前边的 pick 改成 edit(可使用 Vim 替换指令 :s/pick/edit/ 7),然后输入 :wq

    随后,会发现被切到一个奇怪 rebase-i 分支,并有相关的提示:

    入坑 Git,看这一篇就够了-13

    实际上它并不是一个真正的分支,执行 git branch 可以看到 (no brach, rebasing ...)

    入坑 Git,看这一篇就够了-14

    根据步骤提示,依次执行 git commit --amend --reset-author + git rebase --continue 一直到最后返回到原分支:

    完成后的效果:

    修改远程仓库

    本地修正好之后,git push -f

    ❓ 如何查看远程分支详情?

    通过 git branch --remote 或其对等命令 git branch -r / gb --remote / gb -r / gbr,可以看到有哪些远程分支:

    这几乎没有什么借鉴意义,尤其是在多人一起维护一个项目的时候。通常,作为项目的主负责人,我希望可以看到有多少远程分支,每个分支是谁在负责,最近提交时间是什么,以判断这些分支的健康程度。

    使用以下命令,可以做到:

    git for-each-ref refs/remotes --sort=committerdate --format="%(color:bold)%(committerdate:relative)%09%(color:blue)%(authorname)%(color:cyan)%09%(refname:lstrip=3)"
    

    入坑 Git,看这一篇就够了-15

    可以看到超过好几个月,甚至超过一年的远程分支,基本可以断定烂尾了。

    写个 Alias 到 .zshrc 方便使用:

    alias gbaa="git for-each-ref refs/remotes --sort=committerdate --format="%(color:bold)%(committerdate:relative)%09%(color:blue)%(authorname)%(color:cyan)%09%(refname:lstrip=3)""
    

    ❓ 如何批量清理远程 Tag?

    作为发包爱好者,我曾经凭一己之力把公司的 GitLab 搞卡死了..

    事情是这样的,我的项目用 Lerna 做 Monorepo 管理。处于一定的技术考量,我的包切得很碎。Lerna 在 publish 的时候可能由于一个底层的小包而连带发布几十个包。而 Lerna每发一个包就为它创建一个 Tag。即使我不是每次都用 lerna publish 全量发布,久而久之,Tag 数目也是可观的。

    当 Tag 数到达一定数量级之后,Lerna 在拉取公司 GitLab Tag 列表的时候,由于服务端的问题,导致服务端卡死。

    虽然服务端后来修复了这个问题,但我还是养成了时不时清理不必要的远程 Tag 的习惯。

    Lerna Tag 跟包名关联,通常是 @xx/...@x.y.z,跟常规分支有明显的区别,所以这个事情还是比较好处理的。

    git tag -l "@/*" | xargs -n 1 git push --delete origin
    

    写到项目 package.json 下:

    {
      ...,
      "scripts": {
        ...,
        "clean:tag:remote": "git tag -l "@xx/*" | xargs -n 1 git push --delete origin",
        "clean:tag:local": "git tag -l "@xx/*" | xargs -n 1 git tag -d"
      }
    }
    

    就可以很简单的使用了。先清理 remote 再清理 local,注意清理完后 git pull 一下,因为清理远程 Tag 有失败的几率。

    ❓ 如何解决 GitHub 访问慢,甚至无法访问的问题?

    国内经常访问不了 GitHub,或者访问速度慢。甚至有的公司也可能禁用 GitHub。

    方法 1 - ghproxy

    如果用来下载,比如某些命令行需要 curl GitHub 上的内容,报错「Failed to connect to ... port 443」,最简单的办法是在对应的 URL 前拼上 https://ghproxy.com/,支持的域名有:

    • https://ghproxy.com/https://github.com/...
    • https://ghproxy.com/https://raw.githubusercontent.com/...
    • https://ghproxy.com/https://gist.github.com/...
    • https://ghproxy.com/https://gist.githubusercontent.com/...

    更多用法可以直接访问 ghproxy.com。

    方法 2 - 改 Host

    如果需要使用 GitHub 的 OAuth 登录,比如 VSCode 登录,碰到超时连不上的问题,可以通过修改 Host 解决。

  • 在 www.ipaddress.com 查询 github.com 的 IP,快捷地址 sites.ipaddress.com/github.com/
  • 修改 Host 添加 xx.xx.xx.xx github.com
  • 重试登录
  • 注意,修改 Host 后可能需要刷新 DNS,用完记得还原 Host。

    GitHub 镜像

    • hub.njuu.cf

    📌️ 链接

    • Git 官网
    • Git Pro Book
    • Git Cheat Sheet

    🪭 写在最后

  • 这篇文章的题目,从原来设想的《我的 Git 使用记录》到《入坑 Git,看这一篇「应该」就够了》到《入坑 Git,看这一篇就够了》,感觉内容越写越多,就大放厥词吧
  • 奉劝强依赖 GUI 的同学:多用命令行,虽然我一直说 GUI 工具是「最好的老师」,但若强依赖好老师,无法出高徒
  • 相关文章

    服务器端口转发,带你了解服务器端口转发
    服务器开放端口,服务器开放端口的步骤
    产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
    如何使用 WinGet 下载 Microsoft Store 应用
    百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
    百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

    发布评论