有手就行的 Git 使用方法

零基础也能愉快使用 Git#

真正的零基础,本文默认你此前从来没有接触过 Git 相关工具链,也没有任何编程相关常识,从零开始开始学习 Git。

强烈建议 使用 Git 作为版本管理工具。
Git 只是一个工具,就算你不联网一样可以使用它进行版本管理。

只要使用得当,它可以 全程 记录你写下的 每一个字符 的修改历史。
你明白我在说什么吗?所有、全部、每个、文件里的,每一行代码的历史版本。

我有一个五年前写的项目,我去翻 Git 的提交记录,看着同一个文件各个历史版本的 diff(改动对比)我可以清楚地回忆起当初(五年前)修改的每一行代码的用意。
没有一丁点夸张,你去问任何一个会用 Git 的人,他会告诉你:
「就这?小意思,Git 的牛逼之处多了去了。」

不会用没关系,只要你愿意踏出第一步,本文将全程附上对应的 Git 命令及对应说明。

我不会加好多特效,duang 的一下,很黑,很亮,很柔,结果出来你们一定会骂我,根本没有这种 Git。没有,就是这样子。
我会给你们看到,我做完之后是这个样子,你们做完的时候也是这个样子。

速查表#

本节内容放在最前面是为了方便快速查阅。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 将当前目录初始化为 Git 管理的项目
git init

# 添加当前工作区的所有改动到暂存区
git add . # ← 看清楚结尾有个点(.) 意思是"当前目录(的所有改动)"

# 提交暂存区所有改动到本地仓库
git commit -m "别说中文和符号了 你甚至可以用 emoji 📝"
git commit -m"←左边这里没有写错 就是可以不加空格"

# 将(已提交的)本地仓库推送至远程(云端)托管
git push

# 从远程(云端)拉取最新的版本到本地仓库
git pull

# 从远程(云端)下载某个仓库
git clone [仓库远程链接] [指定保存在本地的目录名字]

背景科普#

Git 是什么#

一种版本控制系统Version Control System(VCS),也是目前为止全世界最强大最流行的 VCS。

当然世界上不是只有这一种 VCS,还有种曾经也流行过的竞品,叫 SVN。
但我想说「但凡你用过 Git 你都不会喜欢 SVN」,我认为是这样的。

Git 能做什么

它可以对你的文件进行 极致 的版本管理,粒度细到令人发指。
如果你看不懂「粒度」是什么意思,用人话说就是,你的每一行文本——
甚至 每一个 字符,都完全处于你的绝对掌控之下。

因此 Git 不止是编程好用,用来写作也是很爽的事(比如本博客)。

当然,前提需要是文本文件,像 Word 的 .docx 文件或者各种压缩包文件(.7z / .zip / .rar),硬要用 Git 管理的话确实也能做到(一律视为二进制数据)。
不过就只剩回档的功能了,Git 失去了原本对文本文件极致的控制力,那 Git 的魅力就失色了一大半。

它可以让你随心所欲地进行版本控制。只要你愿意,你可以:

个人使用

  1. 将项目恢复到 任何一个(记录过的)版本。
  2. 对比 任何一次 修改前后的变化。
  3. 查看 任何一个 版本的变化信息:提交时间、提交者,提交者的联系方式,以及具体到每个文件、每一行文本、每一个字符的提交内容。

团队协作

  1. 如果是团队协作,你可以和协同者 同时 进行各自的工作,然后用一行命令合并项目,互相同步对方的成果。
  2. 如果协作中出现文件冲突,你可以轻松找到 每一处 冲突的地方进行解决。
  3. 再也不可能扯不清谁的贡献有多少,只要你想,统计 每个人 分别汇总提交了多少个字符都能做到。谁提交了几次,每次提交是具体哪些文件的什么内容,你可以选择在任何时候回溯。

Git 仓库(repository)是什么#

「Git 仓库Repo」狭义的意思是「本地使用 Git 进行版本管理的项目目录」。

网上交流时一般指广义的「在线托管 Git 仓库服务提供商」所托管的「某个项目」。

GitHub 是什么#

世界上最著名,使用人数最多的,提供在线托管 Git 仓库服务的网站。

目前该网站已被微软收购。

Git、Git 仓库、GitHub 真的有那么难分清吗?

Git 是你的相册软件,Git 仓库是你剪完的片,GitHub 就是 PornHub,这样说能明白吧。

GitLab 又 TM 是什么#

类似 GitHub,但是由各种第三方自行建立的,提供在线托管 Git 仓库服务的网站。

是的,你可以(用自己的服务器)自建一个私有的云端 Git 仓库托管服务。

还有别的托管网站吗,一起来吧#

那可就多了:

  • gitee
  • coding
  • 各种基于 GitLab 自行搭建,并向公众开放的托管服务(比如 gitgud.io
  • 等等

严正警告:你的 era 游戏请避免托管到中国大陆的 Git 仓库服务提供商。

点名 OSCHINA 的 gitee 和腾讯的 coding。

仓库被封都是小事,当你写得正 high 的时候,你不会希望听到门铃突然响起的。
「学习」「开发」「使用」确实不犯法,「传播」犯法(《刑法》第三百六十四条)。

基础教程#

安装 Git 应用程序#

安装完成后桌面会多一个 GUI 客户端,不过应该没人用那个吧。

右键点击空白区域,在弹出的右键菜单中选择「Git Bash Here」打开 Git 自带的命令行。

配置 Git#

1
2
3
4
5
git config --global user.name "用户名"
git config --global user.email "电子邮箱"

# 启用彩色的命令行输出 不是为了好看 是为了防止你眼瞎
git config --global color.ui auto

注意:你这里设置的 用户名电子邮箱 都会 暴露在仓库附带的提交信息 中。

本地使用 Git 仓库#

1
2
3
mkdir practice-git
cd practice-git
git init

你已经成功创建一个项目目录,并用 Git 管理起来了。

1
2
3
touch readme.txt
ls -l
pwd

编辑刚刚创建的 readme.txt(位于 pwd 打印的路径),随便写点什么,回到命令行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
git status

# 回显
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        readme.txt

nothing added to commit but untracked files present (use "git add" to track)

提示让你 git add readme.txt 来添加这个文件到版本管理。

1
2
git add . # ← 看清楚结尾有个点(.) 意思是"当前目录(的所有改动)"
git status

文件绿了,意为「该文件的改动已被添加(add)到暂存区」。

1
2
3
4
5
6
git commit -m "好耶 ✌️ 是第一次提交"
git status

# 回显
On branch master
nothing to commit, working tree clean

绿色的文件不见了,代表「改动已经被提交到(本地)仓库」。

现在假如我们误操作,把 readme.txt 的内容搞砸了。
编辑 readme.txt,删除之前的内容,随便敲点乱码,保存后退出编辑器。
接下来,我们甚至完全没注意到已经出错,不小心把这个文件提交了。

1
2
3
git add .
git commit -m "这是一个错误版本"
git status

如果没用版本管理系统(Git),这种情况已经 GG 了。
但我们用了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 查看提交历史
git log

# 查看最近一次改动的内容差异
git show
# 现在知道管理每一个字符是什么意思了吧

# 强制回退 HEAD是当前的指针 ^是HEAD的前面 也就是上一个版本
git reset --hard HEAD^

git log
# 第二次提交的错误版本已经消失了

打开 readme.txt,乱码滚蛋了。世界真是美好。

当然 git reset --hard 这种粗暴的方式原则上不提倡使用。

如果使用了远程仓库托管服务(下一节就讲),本地回退到了上一个版本,但远程(如果 push 过)还停留在错误版本。
此时可以 git push -f 强制覆写远程仓库,用本地的版本暴力覆盖远程的版本。

然而这个操作是「破坏性」的,违背了 Git 优雅合理地进行版本管理的初衷。
如果仓库持有者注重安全性(比如公司的内部仓库),将当前分支(分支是啥之后再说)设置为保护(protected),那么远程仓库会拒绝你的强制提交(push -f)。

这个机制是为了防止有人搞破坏,比如删光整个仓库然后强制覆盖远程仓库。
或者并不熟练的新手的误操作,比如我司有个新人(行政)就干过用她几个文件的本地仓库强制覆盖上千个文件的内部仓库的傻事。(她吓坏了,沿着工位挨个道歉,挺可爱的。)
最后解决方式当然也很简单,远程仓库只是为了方便大家进行协作,那必然有人电脑上留有最新版本的完整仓库。我用我的工作机再 push -f 一遍最新版本就完事了。

没错,我没有跑题,我在吹 Git 无敌的协同开发能力(什么是真正的去中心化啊)。
不过这仅限于有人一起协作的情况,如果只有你一个人独立开发,此时假设你本地 reset 这一步本来就是误操作,然后你还 push -f 了,那 GG,没人救得了你。
所以一句话:reset --hard + push -f 这套操作可行,但它是「不可逆」的。
因此原则上能不用尽量不用。

那应该怎么办?

我们来玩点更狠的,直接 Shift + Delete 删掉 readme.txt 这个文件。

1
2
3
git add .
git commit -m "前略,代号穿山甲,我滴任务完成辣哈哈哈哈"
git show # 看不到 diff 了 只知道 deleted file

此时当然可以故技重施(reset),不过这次我们不用这招,整点新活。

1
2
3
4
5
6
7
8
9
# 查看简略的提交历史 开头的 7 个字符就是 commit id (的前几个字符)
git reflog

# 使用不破坏提交历史记录的回退功能
git revert [穿山甲的 commit id]
# 本质是又新建了一个 commit 来 *抵消* 指定 commit 的改动,妙啊

# 这时会进入 vim 的编辑窗口界面,不要惊慌
# Esc 然后输入 :q 按回车退出 vim

接着 readme.txt 就恢复了原样,而且 git log 可以看到穿山甲被永远钉在了耻辱柱上。

大概就这样,日常使用已经很够了,如果还有不懂的可以 PM 我催更,联系方式在文末。

把本地仓库推送到云端保存#

注册 Git 仓库在线托管服务提供商,并将你本地的 Git 仓库绑定并上传,你就永远不用再怕自己电脑上的源码意外丢失了。

那是真的不怕意外。

GitHub 2020 年有个讨好程序员的项目「GitHub 归档计划Archive Program」,他们号称「Preserving open source software for future generations」(为后代保护有价值的源码),并声称:

The world is powered by open source software. It is a hidden cornerstone of modern civilization, and the shared heritage of all humanity.
这个世界是由开源软件驱动的。它(所有开源项目)是现代文明的隐性基石之一,也是全人类的共同遗产。

我浪,肉麻是真的肉麻。但爽也是真的爽,只要你是开源项目作者,真的是搔到痒处。
青史留名,大部分理工男 男人 人类的浪漫。

如果你的项目 star 数够多,他们会把你的源码仓库打包备份,送到北极坚冰之下和种子库放在一起保存,好像还有别的基因库什么的吧不太清楚具体细节,总之就是诺亚方舟那味,然后他们会给你的 GitHub 帐号一个「Arctic Code Vault Contributor」的称号。

我有个实现巨丑陋的仓库也被逮到了,给我尴尬坏了,心想「GG,整不好丢人丢到一万年以后了」。
一聊身边有个货(他的水平我心里门清)有个几十星的仓库也入选了,哦原来门槛这么低啊,那没事了。总有比我写得烂的,慌什么。

扯远了,不吹了,话说回来——

除了安全性得到无后顾之忧的保障以外,利用 Git 仓库的在线托管服务,你还可以轻易做到异地同步开发。家里写完上班继续写,公司写完回家接着写。
当然我没有这样(我是复古怀旧风,我用的 U 盘)。

如果你不知道的话:你的 Wi-Fi 提供者 可以 监测你 所有 的流量去向。

一般来说,他们解不开 HTTPS(SSL/TLS)加密后的数据包。
但他们可以看到你的数据包去了哪里,这个没有任何门槛。

「不好意思,我在家,而且我从不连外面的 Wi-Fi,只用自己的流量。」
你的网络运营商:「?你对网络拓扑结构是不是有什么误解。」

在墙内,如果没有使用足够科学的上网方式的话,约等于「裸奔」。

我没有说墙外就安全哦,2022 年了,公知 NGO 们把棱镜门PRISM洗白了没?
但凡你玩过带社会学元素的战略运营 SLG 就会意识到,你可以不主动控制舆论。
不过一旦舆情发生问题时,如果你没有反制手段,你自己说是不是纯傻逼。

扯远了,只要你没有非法牟利或者造谣传谣什么的,就是纯看()的话是没问题的。

至于隐私问题,网安的兄弟们也很忙,还有业绩要冲呢。
你没杀人放火谁 TM 有空盯着你不放。
凌晨一点前往奇怪网站的流量来源海了去了,真的不差你一个。

SSH 上手

这里以 GitHub 为例说明。

首先建立 GitHub 帐号,来到 帐号设置,切换到 SSH and GPG keys 标签页。
点击「New SSH key」按钮,新建一个 SSH 密钥对。
提示让你输入 Key(这里的 Key 指公钥),你有吗?

没有就建一个,还记得上一节讲的「Git Bash Here」吗?让我们打开命令行:

1
2
3
4
5
6
7
8
# 切换到用户主目录
cd ~

# 看看这个目录到底在哪
pwd

# 看看你有没有 .ssh 目录
ls -al .ssh

如果有正常回显,说明你曾经用过 SSH,直接跳到下一步。

不要怪我讲的巨细无比,本文开头就说了「在读者完全没有任何基础的前提下」展开。
不耐烦的话直接往下拖或者右上角叉掉就是了,这也要我教吗。

如果回显 ls: cannot access '.ssh': No such file or directory 说明你从来没有用过 SSH。没关系,我们可以从头开始新建:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 创建目录
mkdir -p ~/.ssh
# -p 是自动递归创建路径 这里其实不需要
# 假如 "mkdir ./a/b" 如果 a 不存在就会报错
# "mkdir -p ./a/b" 则会自动先创建 a 再创建 b

# 查看创建的目录
ls -al | grep ".ssh"
# 以.开头的目录在 Linux 文件系统(Git Bash 也是)中视为隐藏目录
# -a 显示全部(包含隐藏文件) -l 以列表模式显示
# grep 筛选带有指定关键字的条目

cd ~/.ssh

按理说安装 Git 应该会自动创建这个目录,我也不知道,我手上找不到没有装过 Git 也没用过 SSH 的电脑测试。总之我说的这个流程是没问题的,我们继续。

生成 SSH 密钥对

生成一对 SSH 密钥对:

1
2
3
4
ssh-keygen -b 4096 -C '推荐填你的邮箱' -f ~/.ssh/github_ssh_key_your_username
# -b 生成大质数的bit位(RSA核心算法需要) 默认为2048我记得 越高越安全 但加/解密越慢
# -C 评论 会记录在公钥里 随便写什么 最好写相关的内容 方便日后辨认
# -f 指定生成密钥对的文件名 不指定则默认为 id_rsa

然后会提示你输入加密「密钥对」的口令,也就是二次加密(用来加密密钥的密码)。
这个完全是为了给有安全焦虑的人准备的,我们搞的不是军工级项目,用不上这个。
直接两次回车跳过。

这样就成功生成了一对 SSH 密钥对,很简单吧?
最后显示的 SHA256 指纹图不用在意,我们用不到。

导出公钥

私钥自己妥善保管好,公钥则要交出去以证明「你是你」:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 查看当前目录(~/.ssh)的文件列表
ls -l

# 打印公钥内容
cat github_ssh_key_your_username.pub
# 选中公钥信息(不能多也不能少 最后的评论可以少 最好也不要少)
# 右键菜单选择 Copy (Ctrl+C没用 在Linux里这个组合键是终止当前进程)

# 或者你也可以查看绝对路径
pwd
# 然后用 Windows 的文件资源管理器找到该文件,用任意文本编辑器打开该文件

注意带 .pub 后缀的以 ssh-rsa 开头、且只有一行的,才是可以公布出去的公钥。
没带 .pub 后缀的以 -----BEGIN OPENSSH PRIVATE KEY----- 开头、整齐地排了 n 行的是你的私钥,不要交给任何人。

把公钥粘贴到之前打开的 GitHub 的 SSH and GPG keys 页面,Title 随便取。

1
2
3
4
5
# 测试连通性
ssh -T git@github.com

# 回显
Hi [你的 GitHub 名字]! You've successfully authenticated, but GitHub does not provide shell access.

GitHub 已经可以通过公钥来识别你是谁了。

如果连接失败,也许是因为你在墙内吧?有时候就是会抽,有条件可以使用代理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 配置全局 HTTP 代理
git config --global http.proxy http://127.0.0.1:10809 && git config --global https.proxy https://127.0.0.1:10809
# IP 和端口自己改好

# 配置全局 SOCK5 代理
git config --global http.proxy sock5://127.0.0.1:10808 && git config --global https.proxy sock5://127.0.0.1:10808
# IP 和端口自己改好

# 上面两种代理方式 二选一就行了
# HTTP 走 443 端口 对应 https://github.com/username/repo_name.git 这种连接形式
# SOCK5 走 22 端口 对应 git@github.com:username/repo_name.git 这种连接形式

# 取消所有全局代理
git config --global --unset http.proxy && git config --global --unset https.proxy

既然已经连通 GitHub 了,该上传仓库了。
新建一个仓库,具体配置看你自己,建好之后回到本地的命令行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 与远程仓库建立链接
git remote add origin git@github.com:username/repo_name.git
# 注意修改成自己的仓库地址
# GitHub 新建仓库后的网页之后会提示对应的命令

# 修改主分支名字为 main
git branch -M main
# GitHub 推荐的 原因下面说

# 将本地仓库推送(push)到远程(origin)
# 哪个分支? main
git push -u origin main

GitHub 的主分支叫 main(本来是 master)这个改动是当初搞政治正确的时候改的。
因为「master」这个词暗示了有「slave」。 草生。
真要说的话其实挺几把离谱的,现代文字狱了属于是。
另外,如果搞大数据的话,其实真的有 master / slave 的概念(Hadoop / Spark 的节点),这个才是直球 www。毕业后没怎么了解过分布式了,不清楚有没有被波及到。

恭喜你,你的仓库基本算是永久保存了。
(说句不好听的,我死了一百年,我仓库里的内容都还好好的。)

从云端拉取仓库到本地#

拉取一个新的远程仓库(clone)

1
2
3
4
5
6
7
8
# 重新拉取一个新的远程托管仓库
git clone git@github.com:username/repo_name.git

# 拉取指定仓库的指定分支
git clone git@github.com:username/repo_name.git -b master

# 连带子模块一起递归拉取
git clone git@github.com:username/repo_name.git --recursive

在已链接的仓库中同步远程的更新改动(pull)

1
git pull

单独拉取子模块

如果目标仓库包含子模块submodule,直接 clone(仅 clone)并不会连同子模块一起下载。
(直接 clone 子模块的位置只有一个空文件夹。)
除非 clone --recursive 才会连同子模块一起下载。

不过万一出现这种情况也不用删掉重新 git clone --recursive,可以执行

1
git submodule update --init --recursive

来单独拉取子模块。

进阶教程#

使用忽略清单文件(.gitignore#

有时候 大部分时候我们都需要 Git 忽略 / 排除一些 不应该 上传到远程仓库的东西,比如:

  • 鉴权凭证(各种形式的密码、密钥、token 等包含敏感信息的文件)
  • 开发过程中用到的文本编辑器 / IDE(集成开发环境)的配置信息 1
  • 编译过程中自动生成的各种中间 / 临时文件

这时候怎么办呢,难道要 每次 commit 的时候都拖走这堆东西,提交完了再移回来吗?

当然不可能这么傻,Git 支持使用一个「忽略清单文件(.gitignore)」来将「指定的文件或文件夹」排除在版本管理系统之外。

用法也很简单,在 Git 进行版本管理的项目根目录 2 新建一个名为 .gitignore 的文本文件,然后在里面写下排除的文件名或目录名就行了,每行一个。

另外,支持 * 作为通配符进行 模糊匹配# 开头的行视为注释。

如果不想自己逐条慢慢写可以在远程仓库托管网站 3 建立时 根据自己使用的编程语言 自动初始化,已经创建好的仓库想要新加忽略清单可以参考官方仓库 github/gitignore,或使用相关在线工具(如 gitignore.io)自动生成。

最后附上一个解决使用 gitignore 中可能会碰到的问题的小 tip。

如果你 已经 对某部份文件进行了版本管理(add 过),之后想要排除这部分内容。
此时光是在 .gitignore 里写下文件名 / 目录名你会发现并不能直接排除。
因为这也算「破坏性操作」,违背了安全原则。

所以你可以先 git rm ... 再自行恢复那部分文件。
或者使用 最简单的方式:把这部分文件移出项目文件夹,git add . 添加改动,再把这些文件移动回来,然后 gitignore 就能正常工作了。

使用分支#

有时候 大部分时候我们都会有这样一种需求:
「探索性开发」,或者说非常前沿(当然也非常激进)的技术验证。

我们希望 在原有仓库的基础之上 进行非常激进的开发,这种开发不是经过长期经验事实验证的稳扎稳打,而是一种结果未知的 冒险,比如:

  • 新增功能,尤其是那些你很难甚至根本就没办法保证兼容性的全新功能
  • 修复漏洞,尤其是那些原理不明 / 涉及到方方面面的大量已存在部分的奇怪 bug

此时我们当然也希望将对原来的既定内容进行最大程度的保障,同时又想对新开发内容进行版本管理,难道要另外新建一个仓库(与原仓库内容大部分雷同)吗?

于是分支(branch)功能重磅登场。分支的作用顾名思义,就是在原来的主干道之外新开辟了一条路(可以随时并线回去),这保证了主干道(主分支)的绝对安全。

查看分支

1
2
3
4
5
6
7
8
# 查看本地分支
git branch

# 查看远程分支
git branch -r

# 查看所有分支(包括远程和本地)
git branch -a

创建分支

1
2
3
4
5
6
# 创建分支(且留在当前分支)
git branch [new-branch]

# 创建并切换到新的分支
git switch -c [new-branch]    # -c 是 --create 的缩写
git checkout -b [new-branch]  # -b 是 branch 的缩写 (没有 --branch 是因为还有 -B)

本来 switch 才是「切换」分支。
这里也可以用 checkout 切换分支,但实际上 checkout 移动的是指针,下同。

其他分支操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 切换到指定的分支
git switch [the-branch]
git checkout [the-branch]

# 将本地分支关联到远程分支
git branch [loacl-branch] -u [remote-branch]

# 修改指定的分支名称
git branch -m [old-branch] [new-branch]
# 省略 [old-branch] 则默认为当前分支

# 将指定分支合并到 **当前分支**
git merge [the-branch]

# 删除指定分支
git branch -d [useless-branch]

同时使用多个账号(提交身份)#

用人话说就是开马甲(小号)。(我现在就是)

还记得上一章「基础教程」第 2 节 说的配置用户名和邮箱吗?

当你 git commit 提交版本改动的时候,Git 就用的这组信息来记录你的身份。
所以你可以通过修改这组配置来化身另一个人。

同时使用多个身份难道要每次提交都设置一下?
当然不用,之前配置的是全局(--global),你可以为每个项目单独设置特定的配置项。
Git 会优先使用项目配置 > 全局配置。

1
2
3
4
5
# 切换到你的项目根目录下
cd your-project

# 为此项目单独设置的配置项
git config user.name "Anonymous" && git config user.email "anonymous@gitlab.localhost"

同时绑定(链接)多个远程仓库#

运行 touch ~/.ssh/config 或者手动在对应目录下新建名为 config 的文本文件。
输入以下内容并保存文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# GitHub 大号
User username1@email.com
Host github.com
HostName ssh.github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/github_ssh_key_user1
Port 443

# GitHub 小号
User username2@email.com
Host github.com-2
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/github_ssh_key_user2

# gitgud.io
User username2@email.com
Host ssh.gitgud.io
HostName gitgud.io
PreferredAuthentications publickey
IdentityFile ~/.ssh/gitgud_ssh_key

需要注意的是 Host 这一项才是你输入指令时实际使用的配置。
gitgud.io 默认的链接地址就是 git@ssh.gitgud.io:username/repo_name.git挺怪的
我上面写成这样是为了配合 gitgud.io 官方自带的提示指令。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# GitHub 大号
git clone git@github.com:username1/repo_name.git
# 注意 github.com 的位置

# GitHub 大号
git clone git@github.com-2:username2/repo_name.git
# 注意 github.com-2 的位置

# gitgud.io
git clone git@ssh.gitgud.io:username/repo_name.git
# 注意 ssh.gitgud.io 的位置

注意:最好要搭配上一节说的小号一起用。
也就是说,如果使用第二身份,clone 下来还要进去设置 git config user.*
否则提交的仍然是默认(全局)的用户名和邮箱。

如果同一仓库要同时上传到多个远程托管网站:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 正常链接远程仓库
git remote add origin git@github.com-2:username2/repo_name.git

# 链接第二个远程仓库
git remote add [remote-name] [remote-repository-url]
# 比如
git remote add gitgud git@ssh.gitgud.io:username/repo_name.git

# 然后实际使用分别 push 就行了
git push -u [remote-name] [branch-name]
# 比如
git push -u origin master  # 可以省略为 git push (默认为 origin 的当前分支)
git push -u gitgud master

使用子模块#

在特定的少数情况下,一个项目需要直接依赖另一个项目,比如各种框架、主题、公共库。

不是说常见的那种工具链,这些库更常用的方式是各种编程语言自行实现的依赖管理,比如 PHP 的 Composer、Python 的 pip、Node.js 的 npm/yarn、Go 的 go.mod 等。

我是指那些用途没那么广泛的、比较「客制化」的、基于某个开源项目「二创」的 特定 的库(比如最典型的「主题」这种事物)。

在这种情况下,想要获得及时且稳定的依赖管理可以直接使用 Git 的子模块submodule功能实现。

1
2
3
4
5
6
7
# 将另一个仓库添加为本仓库的子模块
git submodule add [repository-url] [local-path]
# 比如
git submodule add https://github.com/username/dependency.git library/dependency

# 更新子模块
git submodule update --rebase --remote

比较极端的情况就是对方(你依赖的)的仓库已经包含了一个或多个子模块(套娃),此时需要使用递归选项 --recursive,详见「基础教程」最后部分

临时存贮栈(真正的「暂存」区)#

在当前分支写得正 high 的时候突然需要切到另一个分支。
又不想草率提交(commit)当前正写到一半的草稿,怎么办?

存贮 stash

1
2
3
4
5
git add .
git stash save "临时保存一下草稿"

git status # 干干净净
git checkout another-branch # 可以切换到另一个分支了

注意:stashadd 没有 必然联系。
只不过 stash 只能保存「已经 被 Git 进行版本管理的文件」,仅此而已。
因此「新增的文件」直接 stash 没办法被存贮,必须先 add 才行。
如果是已经被管理(曾经 add 过)的文件,本次 不需要 add 直接 stash 也可以。

查看已存贮的 stash

1
2
3
4
5
6
git stash list
git stash show # 显示最近一次的改动
git stash show -p # 显示最近一次的详细(diff)改动

git stash show stash@{$index} # 注意 $index 是从 0 开始计数的 下同
git stash show stash@{$index} -p

取出已存贮的 stash

1
2
3
4
git stash pop # 恢复最近一次的 stash 并删除已存贮的 stash

git stash apply # 恢复最近一次的 stash 不会删除已存贮的 stash
git stash apply stash@{$index}

丢弃已存贮的 stash(慎用)

1
2
3
git stash drop stash@{$index}

git stash clear # 清除所有已存贮的 stash

只存贮部分文件技巧

其实我只是听说过有这个技巧,我自己没有用过。(我测试过,没有复现成功。)
因为我本地测试并没有发现 stash --keep-indexstash --no-keep-index 有什么区别。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
echo "初始的 a.txt" >> a.txt
echo "初始的 b.txt" >> b.txt
git add .
git commit -m "初始化"

echo "改动后的 a.txt" >> a.txt
echo "改动后的 b.txt" >> b.txt
git status
git add a.txt
git status

git stash save "keep index" --keep-index
#git stash save "NO keep index" --no-keep-index
git stash show -p
# a.txt 和 b.txt 的改动都被存贮了啊 你在逗我吗

git status
git reset
git status
git stash pop

# 报错
error: Your local changes to the following files would be overwritten by merge:
        a.txt
Please commit your changes or stash them before you merge.
Aborting
The stash entry is kept in case you need it again.
# 删除 a.txt

git stash pop
git show

两个文件的改动都是同步的啊,stash -k 只是会把已经 add 的文件留在暂存区而已。
add 与否「对 stash 自己来说」并没有区别。


  1. 这个也有例外,比如「模板类的仓库」就很可能自带作者已经提前配置好的、并推荐大家使用的一系列相关编辑器配置。
    但我们原则上还是不想让 IDE 配置加入版本管理扰乱仓库内容。
    因为大部分时候你用什么 环境 来进行开发,对项目本身来说是一件根本无所谓也没区别的事,即所谓的「场外」内容。 ↩︎

  2. 并不是只有把 .gitignore 放在项目根目录才有效,放在子目录下会对子目录同一级(及以内)的内容生效。
    但大部分时候我们还是把「要忽略的清单」放在根目录写在一起,看起来一目了然。 ↩︎

  3. 如 GitHub 以及别的代码托管网站,每次创建新仓库都会让你选择「是否自动生成 README.md」「是否自动生成 .gitignore」「是否自动生成可选的协议文件」。 ↩︎

lackbfun © 2021 - 2024