2023-05-25    2024-12-13    4040 字  9 分钟
OLs

不可或缺的工具!!!

本文旨在记录个人使用过程中遇到的相关 Git 命令,非教程式的,详细学习请参阅 《Pro Git》 的中文文档

入门篇

![[assets/Pasted image 20230525223448.png]]

安装设置

去 Git 官网 下载对应版本的 Git 安装即可,此处不再赘述。

安装完成后,需要设置当前用户的名字和 Email 地址:

1
2
git config --global user.name "Your Name"
git config --global user.email "email@example.com"

你总是可以通过以下方式获取帮助:

1
2
3
git help <verb>
git <verb> -h                   # --help 最常用,如 git config --help
man git-<verb>

也许,你还想设置一下 RSA 密钥对:

ssh-keygen -t rsa -C "email@example.com"

基本操作

1
2
3
4
git init                         # 初始化仓库
# ……
git add .                        # 添加工作区所有变更到暂存区
git commit -m "some commit log." # 提交缓存区内变更到本地仓库

进阶篇

远程仓库

我们就以 站点仓库 为例。

本地仓库和远程仓库之间的传输是通过 SSH 加密的,在使用远程仓库之前,我们需要先创建 SSH Key 。

1
ssh-keygen -t rsa -C "youremail@example.com"

在用户主目录下,会生成 .ssh 目录,包含 id_rsa (私钥)和 id_rsa.pub (公钥)等文件。

……

1
2
3
4
5
6
7
8
9
# 列出远程仓库(名称及地址)
git remote -v                   # --verbose

# 添加远程仓库
git remote add <name> <url>
# 移除远程仓库
git remote remove <name>
# 重命名远程仓库分支
git remote rename <old> <new>

关于远程仓库的其他操作,会分散在后续章节中,不在此处单独列出。

分支

 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
# 列出分支
git branch
git branch -l                   # --list 列出本地分支
git branch -a                   # --all  列出远程、本地分支

# 创建分支
git checkout -b <branch>

# 切换分支
git checkout <branch>

# 删除分支
git branch -d <branch>          # --delete 删除合并完全的分支
git branch -D <branch>          # 强制删除分支(包含未合并完全)
# 删除远程分支
git branch -d <remote-repo> <remote-branch>    # --delete 删除远程仓库的指定分支
git push <remote-repo> :<remote-branch>        
# Or 推送一个空分支到远程分支,其实就相当于删除远程分支
git push <remote-repo> --delete <remote-brach>

# 重命名分支
git branch -m <branch>          # --move

# 合并分支
git merge <other-branch> <current-branch>

如果你得到以下错误消息,可能是因为其他人已经删除了这个分支。

1
error: unable to push to unqualified destination: remoteBranchName The destination refspec neither matches an existing ref on the remote nor begins with refs/, and we are unable to guess a prefix based on the source ref. error: failed to push some refs to 'git@repository_name'

此时,使用 git fetch -p 同步分支列表就可以了,如此,你的分支列表里就不会显示已远程被删除的分支了。

我们从一个远程仓库拉取分支,默认会在本地创建 master 分支,并关联到远程仓库的主分支。

1
2
3
4
5
6
7
8
# 拉取远程仓库并关联到自定义本地分支
git clone -b <local-branch> <remote-repo-url>

# 摘取远程仓库指定分支并关联自定义本地分支
git remote add origin <remote-repo-url>
git fetch origin <remote-branch>
git checkout -b <local-branch> origin/<remote-branch> # !关键
git pull origin <remote-branch>

强制推送本地分支到远程仓库分支(两者分支名称不同时),如下:

1
2
3
  # git remote add origin https://github.com/loveminimal/loveminimal.github.io.git
  # git push -f origin master:main  # 本地分支 master ,远程分支 main
  git push -f origin master         # 本地分支与远程分支名称相同,皆为 master

!!永远不要试图一蹴而就,在使用的过程中慢慢补充完善即可!

git 设置本地与远程分支关联:

1
2
3
4
5
# 查看本地与远程分支关联的情况
git branch -vv

# 设置本地与远程分支关联
git branch --set-upstream-to=origin/<remote-branch> <local-branch>

标签

 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
# === 查看标签 ===  
git tag # -l, --list 列出标签
git tag -l "v1.8.5*"
git tag -n # 列出标签,并显示描述信息
git ls-remote --tags origin # 查看远程所有标签

# === 创建标签 === 
git tag v1.4-lw # 创建轻量标签
git tag -a v2.0 -m '描述信息' # 创建附注标签
git tag -a v1.2 9fceb02 # 后期对指定提交打标签

# === 发布标签 ===
git push origin v2.0.0 # 其中 origin 为远程仓库名
git push origin --tags # 将本地标签一次性提交到远程仓库

# === 删除标签 ===
git tag -d v1.4-lw # 删除标签
git push origin --delete [tagname] # 删除远程标签
git push origin :[tagname]         # 删除远程标签

# === 检出标签 ===  
# 拉取远程仓库指定标签到本地,
# 并创建 master 分支跳转
git clone --branch v1.0.0 https://github.com/loveminimal/hugo-theme-virgo.git
git switch -c master

git checkout -b <branchName> <tagName> # 检出某个标签  

# === 切换标签 ===  
git checkout [tagname] # 切换到指定标签
git show [tagname]     # 查看标签版本信息

Git Submodule 子模块

参考 https://zhuanlan.zhihu.com/p/87053283

:: 果然,可以模块的地方,最终都会模块化。

假定我们有两个项目: project-main (主项目)和 project-sub-1 (子模块项目)。

我们可以使用 git submodule add <submodule_url> [<local_dir_name>] 命令在项目中创建一个子模块。

💡 其中, <local_dir_name> 可选。

上述命令执行之后,项目仓库会多出两个文件: .gitmodules (子模块的相关信息)和 project-sub-1 (子模块当前版本的版本号信息)。

如何获取 submodule 呢?

为了方便,我们不妨称主模块为 Main ,子模块为 Sub 。

对于主项目 Main 使用普通的 clone 操作并不会拉取到子模块 Sub 中的实际代码(它是空的)。如果希望子模块代码也获取到,可以使用以下两种方式:

  1. 在克隆 Main 的时候带上参数 --recurse-submodules ,如此会递归地将项目中所有子模块的代码拉取;或,
  2. 在 Main 中执行 git submodule init & git submodule update ,会根据主项目的配置信息,拉取更新子模块中的代码(or git submodule update --init --recursive)。

子模块内容更新了如何操作?

对于子模块 Sub 而言,它并不知道引用自己的主项目的存在,其自身是一个完整的 Git 仓库,按照正常的 Git 代码管理规范操作即可。

主模块的处理,目前就记住,操作了 Sub 后,再去操作 Main 即可。

删除子模块

使用 git submodule deinit 命令卸载一个子模块。这个命令如果添加上参数 --force ,则子模块工作区内即使有本地的修改,也会被移除。

其他

搭建 Git 服务器并启用 Hooks

许多教程使用的都是 root 账户,其实我们使用哪个账户都可以,比如,这里我们就用当前用户 jack 来搭建 git 服务。

首先,把客户机的公钥 id_rsa.pub 文件,导入到 /home/jack/.ssh/authorized_keys 文件里(若没有,新建该文件即可),一行一个。

然后,选定一个目录作为 Git 仓库,比如 ~/.repo/site.git ,在 .repo 目录下,输入:

1
git init --bare site.git

Git 就会创建一个裸仓库,它没有工作区,纯粹是为了共享。

最后,在客户机上,就可以通过 git clone 命令克隆远程仓库了,如:

1
2
3
# jack - 服务器主机用户名
# ovirgo.com - 服务器绑定的域名(可以是 IP)
git clone jack@ovirgo.com:/home/jack/.repo/site.git

什么是 Hooks 呢?

执行 cd ~/.repo/site.git/hooks && ls ,你会发现许多 hooks 文件示例,用于该仓库去响应客户机的某些指令时的一些操作,比如,我们在客户机向该仓库推送时,自动克隆部署该仓库。

在 hooks 目录下,执行以下操作:

1
2
3
# 新建 post-receive
touch post-receive
vim post-receive

编辑 post-receive 这个钩子:

1
2
3
4
#!/bin/sh
cd /home/jack
rm -rf blog
git clone .repo/site.git blog

是的,它是一个 shell 脚本,如此而已。

综上,我们完成了服务器端的 Git 搭建及自动化部署。

实例

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# === 全局化配置用户信息
git config --global user.name  "jack"
git config --global user.email "loveminimal@163.com"

# === 生成 rsa 密钥对
ssh-keygen -t rsa -C "email@example.com"
cd ~/.ssh
cat id_rsa.pub

# === 克隆远程仓库
# --- 常用 bash 别名
cd ~
git clone https://gitee.com/loveminimal/shell.git .shell
cp -r .shell/* ./
source .bash_alias

# --- 站点源码(私有仓库)
cd ~/AppData/Roaming
git clone https://gitee.com/loveminimal/site.git

# --- 递归拉取项目及其子模块
# git clone https://gitee.com/loveminimal/site.git --recurse-submodules
# --- OR
cd ~/site
# git submodule init & git submodule update
git submodule update --init --recursive
# --- 添加其它的远程仓库
cd ~/site/themes/virgo
git remote add github https://github.com/loveminimal/hugo-theme-virgo.git
cd ~/site/static/ship
git remote add github https://github.com/loveminimal/ship.git
cd ~/site/static/emojing
git remote add github https://github.com/loveminimal/emojing.git
cd ~/site/content/.obsidian/themes/Virgo
git remote add github https://github.com/loveminimal/obsidian-theme-virgo.git

# --- 配置 Vim
cd ~
git clone https://gitee.com/loveminimal/vim.git .vim
# --- OR
# scp -r aituyaa.com:/home/jack/.vim .vim

# --- 下载浏览器插件 copy-link
cd ~/local
git clone https://gitee.com/loveminimal/copy-link.git

常见问题

git rebase 和 git merge 的区别

参考 git base 的用法

假设你现在基于远程分支” origin “,创建一个叫” mywork “的分支。

![[assets/Pasted image 20241213115440.png|130]]

现在我们在这个分支( mywork )做一些修改,然后生成两个提交( commit )。

但是与此同时,有些人也在” origin “分支上做了一些修改并且做了提交了,这就意味着” origin “和” mywork “这两个分支各自”前进”了,它们之间”分叉”了。

![[assets/Pasted image 20241213115542.png|300]]

在这里,你可以用” pull “命令把” origin “分支上的修改拉下来并且和你的修改合并; 结果看起来就像一个新的”合并的提交”(merge commit):

![[assets/Pasted image 20241213115619.png|390]]

但是,如果你想让” mywork “分支历史看起来像没有经过任何合并一样,也可以用 git rebase,如下所示:

git checkout mywork
git rebase origin

这些命令会把你的” mywork “分支里的每个提交(commit)取消掉,并且把它们临时 保存为补丁(patch)(这些补丁放到” .git/rebase “目录中),然后把” mywork “分支更新 到最新的” origin “分支,最后把保存的这些补丁应用到” mywork “分支上。

![[assets/Pasted image 20241213115803.png|460]]

mywork 分支更新之后,它会指向这些新创建的提交(commit),而那些老的提交会被丢弃。 如果运行垃圾收集命令(pruning garbage collection), 这些被丢弃的提交就会删除。

![[assets/Pasted image 20241213115851.png|460]]

现在我们可以看一下用合并(merge)和用 rebase 所产生的历史的区别:

![[assets/Pasted image 20241213115910.png|550]]

rebase 的过程中,也许会出现冲突(conflict)。在这种情况,Git会停止 rebase 并会让你去解决冲突;在解决完冲突后,用” git add “命令去更新这些内容的索引(index), 然后,你无需执行 git commit,只要执行 git rebase --continue git 会继续应用(apply)余下的补丁。

在任何时候,可以用 --abort 参数来终止 rebase 的操作,并且” mywork “ 分支会回到 rebase 开始前的状态。

git 多平台换行符问题(LF/CRLF)

详见 git多平台换行符问题(LF/CRLF) - Sampwood的One Piece

git 提供了一个 core.autocrlf 的配置项,用于在提交和检出时自动转换换行符,该配置有三个可选项:

  • true: 提交时转换为 LF,检出时转换为 CRLF
  • false: 默认值,提交检出均不转换
  • input: 提交时转换为 LF,检出时不转换

一种规范换行符的方式是这样的:

# 使用 Windows 系统的开发者设置
git config --global core.aurocrlf true

使用 Linux/MacOS 的开发者设置
git config --global core.autocrlf input

git 同时提供了另一个配置项 core.safecrlf,用于检查文件是否包含混合换行符,该配置也有三个可选项:

  • true 默认值,禁止提交混合换行符的文本文件(git add 的时候会被拦截,提示异常)
  • warn 提交混合换行符的文本文件的时候发出警告,但是不会阻止 git add 操作
  • false 不禁止提交混合换行符的文本文件(默认配置)

该配置用来防止错误的标准化(提交)与转换(检出)。嫌烦的话,可以:

git config --global core.safecrlf false

SSH 通过 RSA 认证实现免密登录远程服务器

我们假设你在本机已经生成了 ssh-key

1
2
3
4
5
cd ~/.ssh
# 复制本机公钥到服务器
scp id_rsa.pub aituyaa.com:/home/jack
# 追加公钥内容到服务器认证文件末尾
cat id_rsa.pub >> .ssh/authorized_keys

推送到 Github 时出现错误相关 (10054 $ 443)

OpenSSL SSL_read: Connection was reset, errno 10054
# OR
Failed to connect to github.com port 443: Timed out

解决办法:

# 关闭验证
git config --global http.sslVerify "false"
# 设置代理,
# 本机代理端口为 7890
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy http://127.0.0.1:7890

上面的本机代理端口号为 7890 ,你需要查询自己的,在系统 设置 / 网络和 Internet / 代理 层级,如下:

![[assets/Pasted image 20230908112808.png]]

当然,这需要你科学上网才行。

:: 真心的,不翻墙,Github 这破玩意儿用起来是真的糟心……

再次提交,就可以正常提交了,过程中可以会出现这个警告,无视它即可!

warning: ----------------- SECURITY WARNING ----------------
warning: | TLS certificate verification has been disabled! |
warning: ---------------------------------------------------      
warning: HTTPS connections may not be secure. See https://aka.ms/gcmcore-tlsverify for more information.

怎么恢复呢?很简单。

git config --global http.sslVerify "true"
# 设置代理,
# 本机代理端口为 7890
git config --global --unset http.proxy 
git config --global --unset https.proxy

解决 git status 不能显示中文

Ref

默认设置下,中文文件名在工作区状态输出,中文名不能正确显示,而是显示为八进制的字符编码。

  1. 终端输入:
git config --global core.quotepath false
  1. 同时, git-bash 中 Options/Text 终端也要设置成中文和 utf-8 编码。

![[assets/Pasted image 20230529085346.png]]

当然,你也可以通过修改配置文件来解决中文乱码,此处暂不讨论(因为一般上面这种办法就可以解决)。

Git 解决每次拉取、提交代码时都需要输入用户名和密码

在家目录运行 git config --global credential.helper store ,然后在拉取时输入正确的用户名和密码,就可以成功记录下来。

解决 git bash 不能登录 mysql

在使用 git bash 登录 mysql 时会卡死,怎么办呢?加上 winpty 就好了,如下:

winpty mysql -u root -p

设置 git-bash 为 Emacs 默认 shell

→ https://blog.csdn.net/csfreebird/article/details/9719221

1
2
3
4
(setq explicit-shell-file-name
	"C:/Program Files/Git/bin/bash. exe")
(setq shell-file-name explicit-shell-file-name)
(add-to-list 'exec-path "C:/Program Files/Git/bin")