%%
# 纲要
> 主干纲要、Hint/线索/路标
# Q&A
#### 已明确
#### 待明确
> 当下仍存有的疑惑
**❓<font color="#c0504d"> 有什么问题?</font>**
# Buffer
## 闪念
> sudden idea
## 候选资料
> Read it later
%%
# 远程仓库与本地仓库的关系
- **本地仓库可以==关联多个远程仓库==**,从而能够**从不同远程仓库拉取更新**,或是**将本地更新推送到不同远程仓库**
- "**远程跟踪分支**" 是**对本地已知的"远程分支最新一次提交" 的==只读引用==**,**标识==本地已知的该远程分支最新指向的 commit==**
- 当远程库的分支推进时,本地的 "**远程跟踪分支**" 引用仍然保留着上一次拉取时的指向,直到下一次通过 `git fetch` 拉取远程分支更新时,"**远程跟踪分支**" 才会同步指向 "**远程库中分支的最新一次提交**"。
- 通常会**为本地分支设置其==所关联的远程分支==,称之为"upstream"**,如**本地 `master` 分支关联于远程分支 `origin/master`**。
- 关联后,执行 `git push` 时会**将本地分支的更新推送到==关联的远程分支==**;
- 关联后,执行 `git fetch` 时将**拉取==关联的远程分支的更新==**,更新本地所见的 "**远程跟踪分支**"的指向。
- 关联后,执行 `git pull` 时会**拉取==关联的远程分支的更新==并合并到本地当前分支**。
<br><br>
# 远程仓库管理 ⭐——git remote
| 相关命令 |
| -------------------- |
| `git remote` |
| `git remote add` |
| `git remote remove` |
| `git remote rename` |
| `git remote set-url` |
### 添加&删除关联的远程库
- `git remote add [OPTION] <name> <url>`: 为本地仓库**添加关联==远程仓库==**
- `<name>`:指定远程仓库名(自定义),通常使用 `origin` 作为主远程库名称
- `<url>`:远程仓库地址
- `git remote rm <remote-name>`:删除关联的远程库
示例:
```shell
git remote add origin
[email protected]:DHDouglas/news-sys.git
git remote add origin https://github.com/DHDouglas/news-sys.git
```
### 查看已关联的远程库
- `git remote [-v]`:查看**远程库名称**
- `-v`:显示**远程库的 url**
![[_attachment/05-工具/git 使用/git 远程仓库&远程分支管理.assets/IMG-git 远程仓库&远程分支管理-0244AF53E5D429D2C47E2D42C1DB757D.png|309]]
- `git remote show <remote-name>` :查看**远程库的详细信息**
- 详细信息:**Fetch/Push URL**,**远程 HEAD 所指分支**,**==git push 和 git pull 配置==** <br>![[_attachment/05-工具/git 使用/git 远程仓库&远程分支管理.assets/IMG-git 远程仓库&远程分支管理-756B511616A5B758DFEFB2ACDBF65E2C.png|363]]
### 修改已关联远程库的信息
- `git remote rename <oldname> <newname>`: **重命名**远程库名称
- `git remote set-url <remotename> <new-url>`: **修改**远程库的 **URL**
> [!example] 示例:修改本地仓库关联的远程仓库的 url
>
> ```shell
> git remote -v
> # View existing remotes
> # origin https://github.com/user/repo.git (fetch)
> # origin https://github.com/user/repo.git (push)
>
> git remote set-url origin https://github.com/user/repo2.git
> # Change the 'origin' remote's URL
>
> git remote -v
> # Verify new remote URL
> # origin https://github.com/user/repo2.git (fetch)
> # origin https://github.com/user/repo2.git (push)
> ```
>
> ^gl1lub
### 添加&删除多个推送/拉取 URL
```shell
# 添加推送URL
git remote set-url --add --push origin https://github.com/user/repository.git
git remote set-url --add --push origin https://gitlab.com/user/repository.git
# 删除推送URL
git remote set-url --delete --push origin https://gitlab.com/user/repository.git
```
> [!tip] 常用于一个项目==同时关联有多个远程库==如 github、gitee、gitlab,需要**同时推送**的情况
<br><br><br>
# 远程分支管理 ⭐
![[_attachment/05-工具/git 使用/git 远程仓库&远程分支管理.assets/IMG-git 远程仓库&远程分支管理-56A24FC5C6DB53357F7AD987881E9D4A.png|565]]
相关命令:git push、git fetch、git pull。
使用说明参见[^1]。
## 设置&查看本地分支"跟踪"的远程分支
为**既有本地分支**设置其**所关联/跟踪的远程分支**,两种方式:
- (1)通过 `git branch -u <upstream> [<branchname>]` 为**当前分支(或指定分支) 关联远程分支**
- (2)通过 `git push -u <remote> <remote-branch>` **在首次推送时设置关联**
![[05-工具/git 使用/git 分支管理#^pcnp23]]
![[05-工具/git 使用/git 分支管理#查看本地分支 "跟踪" 的远程分支|git 分支管理]]
![[05-工具/git 使用/git 分支管理#取消本地分支 "跟踪" 远程分支|git 分支管理]]
<br>
## 将远程跟踪分支检出为本地分支
等效命令:
- `git checkout -b <branch> <remote/branch>`:创建名为 `<branch>` 的分支,其关联/跟踪于**远程分支`<remote/branch>`**
- `git checkout --track <remote/branch>`:创建**与远程分支同名的==本地分支==**,其关联/跟踪于**远程分支`<remote/branch>`**。
> [!NOTE]
>
> 假设本地仓库存在一个**远程跟踪分支** `origin/ba`,**但没有任何本地分支"基于"该分支时**,
> 可以**检出该远程跟踪分支**,得到一个**本地分支 `ba` 关联于该 `origin/ba`**,
> 此后可以**对本地分支 `ba` 进行修改,并==推送==给所关联的远程分支 `origin/ba`**。
>
> [!NOTE] 如果直接 git checkout 切换到一个不存在的分支,而**恰好有个名称相匹配的远程分支**时,则会**自动创建该跟踪分支**。
>
> ![[_attachment/05-工具/git 使用/git 远程仓库&远程分支管理.assets/IMG-git 远程仓库&远程分支管理-9A380FF413FC1B7366E7514490763681.png]]
>
<br>
## 推送更新到远程分支——git push
将**本地仓库==当前分支==的变更**推送到**远程仓库==关联分支==或==指定分支==**:
- `git push [OPTIONS] [<remote> [<refspec> ...]]`
- `-u | --set-upstream`: 将**本地分支与指定的远程分支==关联==** 并 **推送变更**(此后可以只用 git push 推送)
- `-f | --force`: **==强制推送==**,覆盖远程仓库中的提交
- `<remote>`:远程仓库名
- `<refspec>`:**远程仓库的指定分支**
- 可为 `本地分支名:远程分支名` 形式,表示推送 "**==本地库指定分支==** 到 **==远程库指定分支==**"
- `--all <remote>`:推送**所有本地分支到远程仓库**
- `--tags <remote>`:推送**本地所有 tag 到远程仓库**
- `-d | --delete <branch | tag>`:删除**远程仓库**中的**指定分支或标签**
- `-n | --dry-run`:仅**模拟推送过程**,不实际推送任何数据
> [!NOTE] 若**当前本地分支已经与远程分支关联**,则直接执行 `git push` 即可推送到关联的远程分支
```shell
git remote add origin
[email protected]:DHDouglas/git_test.git # 添加远程库
git branch -M main # 强制更改当前分支名为main
git push -u origin main # 首次推送当前分支到远程库的main分支, 为当前分支指定关联于远程分支main
....
git push # 当前分支已有关联的远程分支, 直接推送到关联分支.
# 推送本地分支main到远程分支remote_branch(该远程分支不存在时自动创建)
git push origin main:remote_branch
# 推送本地仓库中的所有分支到远程仓库
git push --all origin
# 删除远程仓库中的指定分支或标签
git push origin --delete feature-branch # 删除分支
git push origin --delete v1.0.0 # 删除标签
```
> [!example] 删除远程库中的指定分支
>
> ![[_attachment/05-工具/git 使用/git 远程仓库&远程分支管理.assets/IMG-git 远程仓库&远程分支管理-DC6E007A98BDD441F36124379A669262.png|278]]
>
>
<br>
## 拉取远程分支的更新——git fetch
**拉取远程仓库的所有更新(分支、标签等)到本地——更新本地的 "==远程跟踪分支==" 引用**:
(即拉取远程分支的提交历史,更新记录到本地的 `.git/FETCH_HEAD` 文件中)
- `git fetch [OPTIONS] [<remote> [<refspec> ...]]`
- `--prune`:删除**本地仓库中**已被远程仓库删除的分支,用以**清理本地仓库**
- `-n | --dry-run`:仅**模拟拉取过程**,不实际拉取任何数据
- **`--force` 或 `-f`**:强制更新本地的"**远程跟踪分支**"引用——**导致非快进合并**。
- `--all`:**拉取远程仓库所有分支的更新**
```shell
git fetch --all # 获取远程仓库所有分支的更新
git fetch # 获取当前分支"关联的远程分支"的更新
git fetch origin main # 获取远程分支main的更新
```
> [!info] `git fetch` 与 `git pull` 的区别
>
> - `git fetch` **仅拉取远程仓库的更新到本地**,但 **==不会合并远程分支到当前分支==**;
> - `git pull` 会合并远程分支到本地,等价于 `git fetch && git merge <remote/branch>`
>
> 为此,在执行`git fetch` 后若希望将获取到的更新合并于当前分支,可再手动执行以下操作:
>
> - `git merge origin/main`:将**获取到的远程分支==合并到当前分支==**。
> - `git rebase origin/main`:将**当前分支==变基==到远程分支的最新提交之后**。
比对本地 FETCH_HEAD 记录与远程仓库的版本号,拉取当前指向的远程分支的后续版本数据到本地, 即更新 FETCH_HEAD
如下图所示,`remotes/origin/HEAD -> origin/main` 即表示本地的“远程跟踪分支`origin/main`” 是对远程仓库的引用。
`git fetch`的作用即是更新远程跟踪分支,**获取远程仓库的最新提交历史并同步给本地的远程跟踪分支 `origin/main`。**

#### 检查 fetch 结果
- `git log FETCH_HEAD` 查看 fetch 日志
- `git log <branch>..origin/<branch>`:查**看本地分支与远程分支**之间的差异——二者之间尚未同步的提交
- `git diff <branch> origin/<branch>`:查看**本地分支和远程分支**之间的**文件差异**。
<br>
## 拉取&合并远程分支的更新——git pull
**==拉取==远程分支的更新到本地并==自动合并==到当前分支(将产生一个==合并提交==)**:
- `git pull [OPTIONS] [<remote> [<refspec> ...]]`
- `--rebase`:**拉取更新后执行 rebase** 而不是 merge——将**当前分支上的所有本地提交==变基==到==远程分支的最新提交**==之后
- `--no-commit`:获取并合并更改,但不自动创建合并提交。
- `--squash`:将获取的**所有远程提交**压缩为**一个单独的提交**,而不是逐个合并。
- `--ff-only`:只允许**快进(fast-forward)合并**,**不会创建合并提交**。若不能进行快进合并将报告失败,。
- `--no-ff`:禁用快进合并,保证会**创建一个新的合并提交**——有助于在提交历史中**保留合并信息**。
- `-e | --edit`:在成功合并后,打开编辑器,编辑 commit 命令。
> [!NOTE] `git pull` 默认等价于 `git fetch && git merge <remote/branch>` 命令
>
> ![[_attachment/05-工具/git 使用/git 远程仓库&远程分支管理.assets/IMG-git 远程仓库&远程分支管理-90F52962DBD09A40CDCAD8B0D5E95F73.png|655]]
```shell
# 拉取当前分支关联的远程分支的更新, 且自动合并到当前分支、
git push
# 拉取远程分支main的更新且合并到当前分支.
git pull origin main # 等价于git fetch origin main && git merge origin/main
# 拉取远程分支更新, 并将当前分支"变基"到关联的远程分支的最新提交之后
git pull --rebase origin main # 等价于 git fetch origin main && git rebase origin/main
```
> [!info] 配置 git pull 默认启用 `--rebase`
>
> `git config --global pull.rebase true`
![[05-工具/git 使用/git 分支管理#^otyima]]
<br><br>
# 远程仓库连接常见报错 🚨
### Permission denied
![[_attachment/05-工具/git 使用/git 使用.assets/IMG-git 使用-6FE06376280E2E2B76444E84CD3B3153.png|680]]
原因:**git 未配置到 github 的 SSH 连接**,因此**对 github 的请求被拒绝**。
解决办法:参见[[#首次使用说明]] 以及官方说明[^2]。
### Connection timed out
原因:防火墙禁用了 SSH 连接。

解决办法:将 SSH 默认的 22 端口改为 443,即**在 HTTPS 的 443 端口建立 ssh 连接**。
步骤:
1. 测试是否能**在 HTTPS 的 443 端口上使用 SSH**,如果显示如下说明可以使用。
> 
2. 设置 SSH,为到 `ssh.github.com` 的连接**指定使用端口 443**,编辑`~/.ssh/config` 文件,增加下列内容:
> 
3. 检查设置是否成功:
```shell
ssh -T
[email protected] # 如果能够成功连接,则意味着可以直接git push上传到远程仓库了
```
<br><br><br>
# 参考资料
# Footnotes
[^1]: [Git - 远程分支](https://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF)
[^2]: [Error: Permission denied (publickey)](https://docs.github.com/en/authentication/troubleshooting-ssh/error-permission-denied-publickey) 、