diff --git a/docs/misc/git-from-zero-to-hero.md b/docs/misc/git-from-zero-to-hero.md
new file mode 100644
index 0000000..3f618b5
--- /dev/null
+++ b/docs/misc/git-from-zero-to-hero.md
@@ -0,0 +1,2045 @@
+---
+title: Git从入门到应付日常工作
+date: 2022-09-27 00:21:33
+tags:
+- git
+- version control
+- distributed version control
+categories:
+- tech
+---
+
+# Git从入门到应付日常工作
+
+## Git概述
+
+Git 是一个开源的 **分布式版本控制系统** 。
+---
+# Git从入门到应付日常工作
+
+## Git概述
+
+Git 是一个开源的 **分布式版本控制系统** 。
+
+
+
+在Git看来,文件有四种状态: 未跟踪 (untracked)、 未修改 (unmodified)、已修改 (modified) 、已暂存 (staged) 。
+
+| 状态 | 描述 |
+| ---------- | ------------------------------------------------------------ |
+| untracked | 未追踪。文件未被 git 管理,执行 `git add` 可将其转换为「已追踪」的「未修改状态」。 |
+| unmodified | 未修改。已追踪,从「已暂存」状态执行 `git commit` 提交后的状态。因此也被称作「已提交」状态 (commited) 。 |
+| modified | 已修改。已追踪,并且发生了修改,但还未暂存。 |
+| staged | 已暂存。已追踪,对一个修改过的文件执行 `git add` 后的状态。 |
+
+
+
+
+
+
+
+git 中的三个区域: 仓库、工作目录、暂存区域。
+
+| 区域 | 描述 |
+| ---------------- | ------------------------------------------------------------ |
+| 工作目录 | 仓库的某个版本独立提取出来的内容。从 Git 仓库的压缩数据库中提取出来,放在磁盘上供用户使用和修改。 |
+| 暂存区 / 索引 | 本质是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中,有时也被称作「索引」。 |
+| Git仓库 / 版本库 | Git 用来保存项目的元数据和对象数据库的地方。 |
+
+
+
+
+
+## 常用命令一览
+
+git 常用命令。使用 `git help `, `git --help`, `man git-` 可查看相关命令的详细用法。
+
+| 命令 | 描述 |
+| ----------------------------------------------------- | ------------------------------------------------------------ |
+| **■ 基本操作** | |
+| git config --[system/global/local] user.name <用户名> | 设置不同层级 git 配置文件中的 git 用户名 |
+| git config --[system/global/local] user.email <邮箱> | 设置不同层级 git 配置文件中的 git 邮箱 |
+| git config --[system/global/local] --list | 查看不同层级 git 配置内容 |
+| git config --[system/global/local] --edit | 编辑不同层级 git 配置内容 |
+| git config --[system/global/local] <变量组名.变量> | 查看不同层级下的git配置中的变量值 |
+| git init | 将当前文件夹初始化为本地库 |
+| git add <文件名> | 添加到暂存区 |
+| git add <目录> | 添加指定目录及其子目录到暂存区 |
+| git add . | 添加当前目录下的所有文件和所有子目录到暂存区
※ 过滤 `.gitignore` |
+| git add * | 添加当前目录下的所有文件和所有子目录到暂存区
※ 不过滤 `.gitignore` ,一般不使用该命令。 |
+| git ls-files | 查看暂存区和工作目录下的文件 |
+| git commit -m "<本次提交的附带信息>" | 提交到本地库 |
+| git status | 本地库状态 |
+| git status -s | 本地库状态缩略信息 |
+| git diff <文件名> | 列出指定文件在工作目录中修改前后的差异。 |
+| git diff --staged <文件名> | 列出指定文件在暂存区与当前仓库版本间的差异。 |
+| git diff <版本号> <文件名> | 列出指定文件在工作目录及指定仓库版本间的差异。 |
+| git reflog | 查看历史记录缩略信息 |
+| git rm <文件名> | 从已经跟踪的文件清单中 (暂存区域) 移除指定文件,同时删除该文件 |
+| git mv <文件名1> <文件名2> | 将文件名1改成文件名2 |
+| git log | 查看历史记录完整信息 |
+| git log -p | 查看历史记录完整信息并展示每次提交的差异 |
+| git log -2 | 查看2条历史记录完整信息 |
+| git log --oneline --graph --all | 以图形形式 (分支流) 查看提交历史 |
+| git reflog | 查看历史记录缩略信息 |
+| git reflog -p | 查看历史记录缩略信息并展示每次提交的差异 |
+| git reflog -2 | 查看2条历史记录缩略信息 |
+| **■ 撤销操作** | |
+| git commit --amend | 修改 message 后提交暂存区,替换掉最后一次提交,即提交数不变 (commit id不同)。 |
+| git commit --amend -m "<提交信息>" | 同上,但不会打开编辑器编辑提交信息,而是直接写上提交信息。 |
+| git restore --staged <文件名> | 将暂存区的指定文件移出暂存区 |
+| git restore <文件名> | 在工作目录下,将已修改状态的指定文件回退到未修改的状态。 |
+| git reset HEAD <文件名> | 同 `git restore --staged <文件名>` |
+| git checkout -- <文件名> | 同 `git restore <文件名>` |
+| **■ 切换操作** | |
+| git reset --soft <版本号> | 将 HEAD 指向指定的版本 (commit) ,不改动 index 和 working tree。
※ 不可指定文件名 (指定则报错)。 |
+| git reset --mixed <版本号> | 不指定文件。将 HEAD 指向指定的版本 (commit) ,并使得暂存区与指定版本一致,即在指定版本之后加入暂存区的所有文件都移出暂存区。
※ `--mixed` 参数是默认的,也可不写。 |
+| git reset --mixed <版本号> <文件名> | 指定文件。`HEAD` 指针不变,但指定的文件对应的暂存区内容将切换到指定版本的状态,其他文件在暂存区的内容不变。 |
+| git reset --hard <版本号> | 将 HEAD, index 和 working tree 都切换到指定版本号的状态。
※ 不可指定文件名 (指定则报错)。 |
+| **■ 分支操作** | |
+| git branch | 列出本地所有分支名 |
+| git branch -r | 列出远程所有分支名 |
+| git branch -a | 列出本地及远程的所有分支名 |
+| git branch <分支名> | 以指定分支名创建新分支 |
+| git branch -d <分支名> | 删除指定分支 |
+| git switch <分支名> | 切换到指定分支 |
+| git switch -c <分支名> | 创建并切换分支 |
+| git switch - | 切换回上一次所在分支 |
+| git checkout <分支名> | 切换到指定分支 |
+| git checkout -b <分支名> | 创建并切换分支 |
+| git merge <分支名> | 将指定分支合并入当前分支 |
+| git rebase <分支名> | 以当前分支为基底,将指定分支在分歧后的修改合入当前分支 (在当前分支下重放指定分支在分歧点后的提交) |
+| **■ 远程操作** | |
+| git remote add <远程仓库地址别名> <远程仓库地址> | 关联本地仓库与远程仓库 |
+| git remote | 列出远程仓库 (别名) |
+| git remove -v | 列出远程仓库详细信息 (远程地址别名和fetch&push的地址) |
+| git remote show <远程仓库地址别名> | 查看远程仓库详细信息 |
+| git remote rename <当前别名> <新别名> | 修改远程仓库地址别名 |
+| git clone <远程仓库地址> | 将远程仓库克隆到当前目录下 |
+| git push <远程仓库地址> <本地分支名>:<远程分支名> | 推送本地当前分支版本到远程仓库指定分支 |
+| git push -u <远程仓库别名> <远程仓库分支名> | 将本地仓库当前分支内容推送到指定远程仓库的指定分支
※ `-u` 是 `--set-upstream` 的缩略,关联当前分支与指定的远程仓库分支,方便之后仅使用 `git push` 完成推送。 |
+| git push <远程仓库地址> <分支名> | 当本地分支名与远程分支名相同时的略写 |
+| git push | 若当前本地分支已与远程仓库某分支通过 `--set-upstream` 参数绑定过,则可直接将本地分支当前版本推送到该远程分支 |
+| git fetch <远程仓库别名> <远程仓库分支名> | 从指定远程仓库获取指定分支的最新版本到当前仓库中,但不 merge |
+| git fetch <远程仓库别名> | 从指定远程仓库的获取所有分支中的最新版本到当前仓库中,但不 merge |
+| git fetch | 类似 push ,若已经设置了 `--set-upstream` ,则可省略后续参数 |
+| git pull <远程仓库别名> <远程分支名>:<本地分支名> | 从指定远程仓库获取指定分支的最新版本到指定本地分支中,并 merge |
+| git pull <远程仓库别名> <分支名> | 远程分支名与本地分支名相同时的简写 |
+| git pull | 类似 push ,若已经设置了 `--set-upstream` ,则可省略后续参数 |
+
+
+
+## 基本操作
+
+### git config
+
+git 有三个层级的配置文件。
+
+| 配置文件 | 描述 |
+| -------------------- | ---------------------------- |
+| 系统级 (system) | 配置对OS系统下所有用户有效。 |
+| 系统用户级 (global) | 配置对OS系统下当前用户有效。 |
+| 当前仓库级别 (local) | 配置对当前仓库有效。 |
+
+不同系统下不同级别的配置文件的路径。
+
+| 系统 | 配置文件 | 描述 |
+| ------- | -------- | --------------------------------------------------- |
+| MacOS | system | 使用 brew 安装时,为该文件: `/usr/local/etc/config` |
+| | global | 为该文件 `/Users//.gitconfig` |
+| | local | 当前仓库 `.git` 文件夹下的 `.gitconfig` |
+| Windows | system | `C:\Program Files\Git\etc\gitconfig` |
+| | global | `C:\Users\\.gitconfig` |
+| | local | 当前仓库 `.git` 文件夹下的 `.gitconfig` |
+| Linux | | |
+| | | |
+| | | |
+
+如下,可通过 `cat` 命令查看三个层级下的 git 配置内容。
+
+```shell
+koshiba@koshiba ~ % cat /usr/local/etc/gitconfig
+[credential]
+ helper = osxkeychain
+koshiba@koshiba ~ % cat ~/.gitconfig
+[filter "lfs"]
+ clean = git-lfs clean -- %f
+ smudge = git-lfs smudge -- %f
+ process = git-lfs filter-process
+ required = true
+[user]
+ name = koshiba
+ email = koshiba@yuki.com
+[init]
+ defaultBranch = master
+koshiba@koshiba ~ % cat iCloud/study/tech/git/git-demo/.git/config
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = true
+[user]
+ name = ikoshiba
+ email = koshiba@yama.com
+koshiba@koshiba ~ %
+```
+
+也可以通过 `git config --[system/global/local] --list` 查看。
+
+```shell
+koshiba@koshiba ~ % git config --system --list
+credential.helper=osxkeychain
+koshiba@koshiba ~ % git config --global --list
+filter.lfs.clean=git-lfs clean -- %f
+filter.lfs.smudge=git-lfs smudge -- %f
+filter.lfs.process=git-lfs filter-process
+filter.lfs.required=true
+user.name=koshiba
+user.email=koshiba@yuki.com
+init.defaultbranch=master
+koshiba@koshiba ~ % cd iCloud/study/tech/git/git-demo/
+koshiba@koshiba git-demo % git config --local --list
+core.repositoryformatversion=0
+core.filemode=true
+core.bare=false
+core.logallrefupdates=true
+core.ignorecase=true
+core.precomposeunicode=true
+user.name=ikoshiba
+user.email=koshiba@yama.com
+koshiba@koshiba git-demo %
+```
+
+需要注意带有 `--local` 参数的 `git config` 命令要在仓库内执行,否则返回如下错误提示。
+
+```shell
+koshiba@koshiba ~ % git config --local user.name
+fatal: --local can only be used inside a git repository
+koshiba@koshiba ~ %
+```
+
+
+
+#### 配置用户签名
+
+可以通过 `vim` 命令或者带 `--edit` 参数的 `git config` 命令打开配置文件完成编辑,更好的方式是直接使用 `git config` 命令指定具体的变量。通常在使用 git 进行版本控制管理前,需要设置用户名及邮箱,即「用户签名」,用于标识操作者信息,无该信息则无法提交。
+
+如下设置 global 层级的 user.name 及 user.email。
+
+```shell
+koshiba@koshiba git-demo % git config --global user.name koshiba
+koshiba@koshiba git-demo % git config --global user.name
+koshiba
+koshiba@koshiba git-demo % git config --global user.email koshiba@gmail.com
+koshiba@koshiba git-demo % git config --global user.email
+koshiba@gmail.com
+```
+
+当我们希望对某个具体的 git 项目 (即 `.git` 所在目录) 使用不同于 global 的用户签名时,使用 `--local` 参数单独设置,如下。
+
+```shell
+koshiba@koshiba git-demo % git config --local user.name koshiba123
+koshiba@koshiba git-demo % git config --local user.name
+koshiba123
+koshiba@koshiba git-demo % git config --local user.email koshiba@outlook.com
+koshiba@koshiba git-demo % git config --local user.email
+koshiba@outlook.com
+```
+
+需要注意的是,「用户签名」与在「远程操作」中使用远程库平台 (如 github) 的账户信息无关,只不过我们也可以将用户签名设置为远程库平台的账号名及邮箱。
+
+
+
+#### 配置全局忽略
+
+使用 MacOS 时,本地 git 仓库中可能会出现 MacOS 中特有的 `.DS_Store` 文件。类似这样的文件或文件夹,我们可以针对每一个本地 git 仓库,在 `.git` 所在目录下设置新建 `.gitignore` 文件,在其中设置需要忽略的本仓库内的文件和文件夹,也可以设置一个全局忽略配置文件 `.gitignore_global` (通常与全局配置文件 `.gitconfig` 放置在同一目录下,即 `~/` 下) 。例如新建一个 `~/.gitignore_global` 文件如下。
+
+```shell
+koshiba@koshiba ~ % cat ~/.gitignore_global
+# MacOS .DS_Store
+**/.DS_Store
+koshiba@koshiba ~ %
+```
+
+> `**` 表示所有层级目录
+
+
+
+然后使用 `git config --global core.excludesfile ~/.gitignore_global` 命令配置到全局配置文件中。
+
+```shell
+koshiba@koshiba git-demo % git config --global core.excludesfile ~/.gitignore_global
+koshiba@koshiba git-demo % git config --global --list
+filter.lfs.clean=git-lfs clean -- %f
+filter.lfs.smudge=git-lfs smudge -- %f
+filter.lfs.process=git-lfs filter-process
+target/
+filter.lfs.required=true
+user.name=koshiba
+user.email=koshiba@163.com
+init.defaultbranch=master
+core.excludesfile=/Users/koshiba/.gitignore_global
+koshiba@koshiba git-demo %
+```
+
+对于已经在 `.gitignore_global` 中的忽略规则,之后就不需要再针对每一个 git 仓库设置单独设置了。
+
+
+
+#### 配置忽略转义
+
+使用 git 命令时,命令行回显信息的中文 (或日文等) 字符会被转义。如下,`中文.md` 被显示为 `\344\270\255\346\226\207.md` 。
+
+```shell
+koshiba@koshiba pics % vim 中文.md
+koshiba@koshiba pics % git status
+On branch master
+Your branch is up to date with 'origin/master'.
+
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ "\344\270\255\346\226\207.md"
+
+nothing added to commit but untracked files present (use "git add" to track)
+koshiba@koshiba pics %
+```
+
+可以将 `core.quotepath` 设置为 `false` 禁止转义,以便显示中文。
+
+```shell
+koshiba@koshiba pics % git config --global core.quotepath false
+koshiba@koshiba pics % git status
+On branch master
+Your branch is up to date with 'origin/master'.
+
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ 中文.md
+
+nothing added to commit but untracked files present (use "git add" to track)
+koshiba@koshiba pics %
+```
+
+`core.quotePath` 参数的作用如下 (来自 [官方文档](https://git-scm.com/docs/git-config) )。
+
+> Commands that output paths (e.g. *ls-files*, *diff*), will quote "unusual" characters in the pathname by enclosing the pathname in double-quotes and escaping those characters with backslashes in the same way C escapes control characters (e.g. `\t` for TAB, `\n` for LF, `\\` for backslash) or bytes with values larger than 0x80 (e.g. octal `\302\265` for "micro" in UTF-8). If this variable is set to false, bytes higher than 0x80 are not considered "unusual" any more. Double-quotes, backslash and control characters are always escaped regardless of the setting of this variable. A simple space character is not considered "unusual". Many commands can output pathnames completely verbatim using the `-z` option. The default value is true.
+
+
+
+### git init
+
+在文件夹路径下执行 `git init` ,此文件夹即称为 git 本地仓库,文件夹下出现 `.git` 隐藏文件夹,其内保存了 git 用于版本管理的各种信息文件。
+
+```shell
+koshiba@koshiba git-demo % git init
+Initialized empty Git repository in /Users/koshiba/Library/Mobile Documents/com~apple~CloudDocs/study/tech/git/git-demo/.git/
+koshiba@koshiba git-demo %
+```
+
+此时即可使用 git 命令 `git status` 查看仓库文件状态。
+
+```shell
+koshiba@koshiba git-demo1 % git status
+On branch master
+
+No commits yet
+
+nothing to commit (create/copy files and use "git add" to track)
+koshiba@koshiba git-demo1 %
+```
+
+
+
+### git add
+
+文件夹初始化为 git 仓库后,尚未有文件被 git 追踪管理,此时使用 `git add <文件名>` 命令将指定文件放入「暂存区」。暂存区本质上是一个文件,它保存了下次将提交的文件列表信息。如下,新增一个 `hello.txt` 文件后,使用 `git status` 查看仓库内文件所属状态,可以看到 `Untracked files` 信息。
+
+```shell
+koshiba@koshiba git-demo % git status
+On branch master
+
+No commits yet
+
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ .DS_Store
+ hello.txt
+
+nothing added to commit but untracked files present (use "git add" to track)
+koshiba@koshiba git-demo %
+```
+
+此时使用 `git add helle.txt` 即可将文件放入「暂存区」(标记为追踪文件)。
+
+```shell
+koshiba@koshiba git-demo % git add hello.txt
+koshiba@koshiba git-demo % git status
+On branch master
+
+No commits yet
+
+Changes to be committed:
+ (use "git rm --cached ..." to unstage)
+ new file: hello.txt
+
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ .DS_Store
+
+koshiba@koshiba git-demo %
+```
+
+
+
+- `git add .` 会把本地所 有 untrack 的 (包括追踪过但又修改了的) 文件都加入暂存区。
+- `git add *` 会忽略 `.gitignore` 把任何文件都加入。执行时会出现如下提示,加上 `-f` 参数后可强制加入,但显然通常我们不会这么做。
+
+```shell
+koshiba@koshiba hello-git % git add *
+The following paths are ignored by one of your .gitignore files:
+test
+test.txt
+hint: Use -f if you really want to add them.
+hint: Turn this message off by running
+hint: "git config advice.addIgnoredFile false"
+koshiba@koshiba hello-git %
+```
+
+
+
+### git commit
+
+使用 `git commit -m "" `提交指定文件。
+
+```shell
+koshiba@koshiba git-demo % git commit -m "first commit" hello.txt
+[master (root-commit) 3d542ed] first commit
+ 1 file changed, 3 insertions(+)
+ create mode 100644 hello.txt
+koshiba@koshiba git-demo % git status
+On branch master
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ .DS_Store
+
+nothing added to commit but untracked files present (use "git add" to track)
+koshiba@koshiba git-demo %
+```
+
+
+
+**修改文件**
+
+修改文件后 git 会检测到被修改的文件。
+
+```shell
+koshiba@koshiba git-demo % git status
+On branch master
+Changes not staged for commit:
+ (use "git add ..." to update what will be committed)
+ (use "git restore ..." to discard changes in working directory)
+ modified: hello.txt
+
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ .DS_Store
+
+no changes added to commit (use "git add" and/or "git commit -a")
+koshiba@koshiba git-demo %
+```
+
+
+
+**再次添加并提交**
+
+```shell
+koshiba@koshiba git-demo % git add hello.txt
+koshiba@koshiba git-demo % git status
+On branch master
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ modified: hello.txt
+
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ .DS_Store
+
+koshiba@koshiba git-demo % git commit -m "first changed"
+[master c246db6] first changed
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+koshiba@koshiba git-demo %
+```
+
+
+
+也可以使用 `git commit -am ""` 将所有已跟踪的文件 **暂存并提交** (即 add 与 commit 一起完成)。
+
+
+
+### git status
+
+`git status` 命令可以添加 `-s` (`--short`) 参数以紧凑形式返回状态信息。如下,添加 `untracked.txt`, `added.txt` 和 `modified.txt` 文件演示处于 `untracked(??)`, `added(A)` , `modified(_M)` (M在右侧) 以及 `modifiedAdded(M_)` (M在左侧) 状态的情况。
+
+- `untracked.txt` 新建后尚未通过 `git add` 添加到暂存区。
+- `added.txt` 新建后通过 `git add` 添加到了暂存区,但未 `git commit` 提交。
+- `modified.txt` 新建后添加到了暂存区,且已提交,并在提交后做了修改。
+- `modifiedAdded.txt` 新建后添加到了暂存区,且已提交,并在提交后做了修改,之后又提交到了暂存区。
+
+```shell
+koshiba@koshiba git-demo % git status
+On branch master
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ new file: added.txt
+ modified: modifiedAdded.txt
+
+Changes not staged for commit:
+ (use "git add ..." to update what will be committed)
+ (use "git restore ..." to discard changes in working directory)
+ modified: .DS_Store
+ modified: modified.txt
+
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ untracked.txt
+
+koshiba@koshiba git-demo % git status -s
+ M .DS_Store
+A added.txt
+ M modified.txt
+M modifiedAdded.txt
+?? untracked.txt
+koshiba@koshiba git-demo %
+```
+
+
+
+### git diff
+
+| 命令 | 描述 |
+| -------------------------- | ---------------------------------------------- |
+| git diff <文件名> | 列出指定文件在工作目录中修改前后的差异。 |
+| git diff --staged <文件名> | 列出指定文件在暂存区与当前仓库版本间的差异。 |
+| git diff <版本号> <文件名> | 列出指定文件在工作目录及指定仓库版本间的差异。 |
+
+※ `--staged` 也可以使用 `--cached` 代替。
+
+※ 上述命令均针对被 git 管理的文件,即被 `git add` 过的文件。当不指定具体文件时,列出所有文件的差异。
+
+※ 「版本号」可以用 `HEAD`、`HEAD^`、`HEAD^^`、`HEAD~~1`、`HEAD~~2` 等形式表示。`HEAD` 表示当前版本,`HEAD^` 表示当前版本的前一个版本,有多少个 `^` 就往前多少个版本,`~1` 表示当前版本的前一个版本,数字是多少,就表示往前多少个版本。
+
+
+
+新建 diff.txt 且该文件只有一行「only first line when created」。可以看到由于尚未对 compare.txt 执行 `git add` ,即此文件还未被 git 管理,因此 三条 `git diff` 命令均无回显。
+
+```shell
+koshiba@koshiba hello-git % vim compare.txt
+koshiba@koshiba hello-git % git diff
+koshiba@koshiba hello-git % git diff --staged compare.txt
+koshiba@koshiba hello-git % git diff HEAD compare.txt
+```
+
+
+
+执行 `git add compare.txt` 之后。可以看到如下。
+
+- `git diff` 命令无回显,因为是初次被 git 管理,即第一次出现在 git 的工作目录,此文件在工作目录中无历史可比较版本。
+- `git diff --staged` 列出首行差异,因为此时暂存区比当前仓库版本多一行 (多了此文件,并且有一行差异) 。
+- `git diff HEAD` 列出首行差异,因为此时工作目录比当前仓库版本多一行 (多了此文件,并且有一行差异) 。
+
+```shell
+koshiba@koshiba hello-git % git add compare.txt
+koshiba@koshiba hello-git % git diff
+koshiba@koshiba hello-git % git diff --staged compare.txt
+diff --git a/compare.txt b/compare.txt
+new file mode 100644
+index 0000000..80dd23d
+--- /dev/null
++++ b/compare.txt
+@@ -0,0 +1 @@
++only first line when created
+koshiba@koshiba hello-git % git diff HEAD compare.txt
+diff --git a/compare.txt b/compare.txt
+new file mode 100644
+index 0000000..80dd23d
+--- /dev/null
++++ b/compare.txt
+@@ -0,0 +1 @@
++only first line when created
+koshiba@koshiba hello-git %
+```
+
+
+
+执行 `git commit -m "add compare.txt"` 提交 compare.txt 之后。可看到如下。此时工作目录、暂存区和当前版本均无差异。
+
+```shell
+koshiba@koshiba hello-git % git commit -m "add compare.txt"
+[master 1e05a4f] add compare.txt
+ 2 files changed, 1 insertion(+), 1 deletion(-)
+ create mode 100644 compare.txt
+ delete mode 100644 diff.txt
+koshiba@koshiba hello-git % git diff
+koshiba@koshiba hello-git % git diff --staged compare.txt
+koshiba@koshiba hello-git % git diff HEAD compare.txt
+koshiba@koshiba hello-git %
+```
+
+
+
+增加第二行 「second line added」之后。可以看到如下。
+
+- `git diff` 列出第二行差异,因为此时工作目录下该文件多了一行 。
+- `git diff --staged` 由于暂存区与当前仓库版本无差异,因此无回显。
+- `git diff HEAD` 列出第二行差异,因为此时工作目录下该文件相比指定的仓库版本多了一行 。
+
+```shell
+koshiba@koshiba hello-git % git diff compare.txt
+diff --git a/compare.txt b/compare.txt
+index 80dd23d..3d555a6 100644
+--- a/compare.txt
++++ b/compare.txt
+@@ -1 +1,2 @@
+ only first line when created
++second line added
+koshiba@koshiba hello-git % git diff --staged compare.txt
+koshiba@koshiba hello-git % git diff HEAD compare.txt
+diff --git a/compare.txt b/compare.txt
+index 80dd23d..3d555a6 100644
+--- a/compare.txt
++++ b/compare.txt
+@@ -1 +1,2 @@
+ only first line when created
++second line added
+koshiba@koshiba hello-git %
+```
+
+
+
+「版本号」的不同写法。
+
+```shell
+koshiba@koshiba hello-git % git diff HEAD~1 compare.txt
+diff --git a/compare.txt b/compare.txt
+new file mode 100644
+index 0000000..3d555a6
+--- /dev/null
++++ b/compare.txt
+@@ -0,0 +1,2 @@
++only first line when created
++second line added
+koshiba@koshiba hello-git % git diff HEAD^ compare.txt
+diff --git a/compare.txt b/compare.txt
+new file mode 100644
+index 0000000..3d555a6
+--- /dev/null
++++ b/compare.txt
+@@ -0,0 +1,2 @@
++only first line when created
++second line added
+koshiba@koshiba hello-git % git reflog -2
+1e05a4f (HEAD -> master) HEAD@{0}: commit: add compare.txt
+f08b6a1 HEAD@{1}: commit: add diff.txt
+koshiba@koshiba hello-git % git diff f08b6a1 compare.txt
+diff --git a/compare.txt b/compare.txt
+new file mode 100644
+index 0000000..3d555a6
+--- /dev/null
++++ b/compare.txt
+@@ -0,0 +1,2 @@
++only first line when created
++second line added
+koshiba@koshiba hello-git %
+```
+
+
+
+### git rm
+
+使用 `git rm ` 从已跟踪的文件清单中移除指定文件 (即从暂存区域移除) 并提交,同时删除该文件。如果只是 使用 `rm` ,虽然文件被删除,但 `git status` 后还是能看到已被删除的文件。
+
+```shell
+koshiba@koshiba git-demo % git rm hello.txt
+error: the following file has staged content different from both the
+file and the HEAD:
+ hello.txt
+(use -f to force removal)
+koshiba@koshiba git-demo % git status
+On branch master
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ modified: hello.txt
+
+Changes not staged for commit:
+ (use "git add ..." to update what will be committed)
+ (use "git restore ..." to discard changes in working directory)
+ modified: .DS_Store
+ modified: hello.txt
+
+koshiba@koshiba git-demo %
+```
+
+如上,在 `git rm` 删除指定文件前,若该文件有过修改,则拒绝删除,这样做的目的是防止删除未添加到仓库中的数据,这样的数据无法被 git 恢复。确定要删除时可以使用 `-f` 参数强制删除 (force) 。
+
+```shell
+koshiba@koshiba git-demo % git rm -f hello.txt
+rm 'hello.txt'
+koshiba@koshiba git-demo % git status
+On branch master
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ deleted: hello.txt
+
+Changes not staged for commit:
+ (use "git add ..." to update what will be committed)
+ (use "git restore ..." to discard changes in working directory)
+ modified: .DS_Store
+
+koshiba@koshiba git-demo %
+```
+
+
+
+### git mv
+
+使用 `git mv` 命令修改文件名,例如 `git mv a.txt b.txt` 相当于如下三行命令。
+
+```shell
+mv a.txt b.txt
+git rm a.txt
+git add b.txt
+```
+
+
+
+### git log
+
+使用 `git log` 查看提交历史。
+
+```shell
+koshiba@koshiba git-demo % git log
+commit b8d829123843fe1d760983ed5bfc9f2cbcc22d13 (HEAD -> master)
+Author: ikoshiba
+Date: Mon Sep 26 19:54:50 2022 +0800
+
+ modified one line
+
+commit 5f3d03819d450b534153b90531393efd38e6a0a1
+Author: ikoshiba
+Date: Mon Sep 26 19:53:32 2022 +0800
+
+ first commit
+```
+
+
+
+使用 `git log -p` 查看每次提交的差异,使用 `git log -p -2` 查看最近 2 次提交的差异。
+
+```shell
+koshiba@koshiba git-demo % git log -p
+commit b8d829123843fe1d760983ed5bfc9f2cbcc22d13 (HEAD -> master)
+Author: ikoshiba
+Date: Mon Sep 26 19:54:50 2022 +0800
+
+ modified one line
+
+diff --git a/hello.txt b/hello.txt
+index 40f4dc0..06227ce 100644
+--- a/hello.txt
++++ b/hello.txt
+@@ -1,3 +1,3 @@
+-hello 1
++hello 1 modified
+ hello 2
+ hello 3
+
+commit 5f3d03819d450b534153b90531393efd38e6a0a1
+Author: ikoshiba
+Date: Mon Sep 26 19:53:32 2022 +0800
+
+ first commit
+
+diff --git a/hello.txt b/hello.txt
+new file mode 100644
+index 0000000..40f4dc0
+--- /dev/null
++++ b/hello.txt
+@@ -0,0 +1,3 @@
++hello 1
++hello 2
++hello 3
+```
+
+
+
+`git log` 命令有许多参数可用于调整输出历史记录的信息。
+
+
+
+### git reflog
+
+也可以使用 `git reflog` 查看缩略的提交历史信息。`git reflog -p`, `git reflog -2` 的作用与 `git log -p` 和 `git log -2` 类似。
+
+```shell
+koshiba@koshiba git-demo % git reflog
+b8d8291 (HEAD -> master) HEAD@{0}: commit: modified one line
+5f3d038 HEAD@{1}: commit: first commit
+```
+
+
+
+## 撤销操作
+
+| 撤销操作 | 执行前状态 | 区域变化 | 描述 |
+| ---------------------------------- | ---------- | --------------- | ------------------------------------------------------------ |
+| git commit --amend | 未修改 | 暂存区 > 仓库 | 修改 message 后提交暂存区,替换掉最后一次提交,即提交数不变 (commit id不同)。 |
+| git commit --amend -m "<提交信息>" | 未修改 | 暂存区 > 仓库 | 同上,但不会打开编辑器编辑提交信息,而是直接写上提交信息。 |
+| git restore --staged <文件名> | 已暂存 | 暂存区 > 工作区 | 将指定文件移出暂存区
※ 必须指定文件名 |
+| git restore <文件名> | 已修改 | 均在工作区 | 都在工作目录下,将已修改状态的指定文件回退到未修改的状态,即丢弃指定文件的所有修改。 |
+| git reset HEAD <文件名> | 已暂存 | 暂存区 > 工作区 | 同 `git restore --staged <文件名>`
※ 若不指定文件名,则清空暂存区 |
+| git checkout -- <文件名> | 已修改 | 均在工作区 | 同 `git restore <文件名>`
※ 必须指定文件名 |
+
+
+
+### git commit --amend
+
+在某次提交后发现该提交的 message 描述有误,想要修改该 message ,或者想要再 add 一些文件,可以使用 `git commit --amend` 。
+
+如下先完成两次提交。
+
+```shell
+commit 6d70b38bd6becddcd2b70fb1a5bc3ee929070497 (HEAD -> master)
+Author: koshiba
+iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjiiiiiiiiiiiixx
+Date: Wed Sep 28 20:11:01 2022 +0800
+
+ first commit
+koshiba@koshiba hello-git % vim hello.txt
+koshiba@koshiba hello-git % git add .
+koshiba@koshiba hello-git % git commit -m "second commit"
+[master 6eefb40] second commit
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+koshiba@koshiba hello-git % git log
+commit 6eefb4065d6b3f02cedf9edce05d3de2afaace87 (HEAD -> master)
+Author: koshiba
+Date: Wed Sep 28 20:11:37 2022 +0800
+
+ second commit
+
+commit 6d70b38bd6becddcd2b70fb1a5bc3ee929070497
+Author: koshiba
+Date: Wed Sep 28 20:11:01 2022 +0800
+
+ first commit
+koshiba@koshiba hello-git % git status
+On branch master
+nothing to commit, working tree clean
+koshiba@koshiba hello-git %
+```
+
+修改文件并执行 `git add` 后,将 message 修改为 "second commit (amend)" 来取代上一次提交。再次执行 `git log` 可以看到前次提交 (6eefb) 已不显示,取而代之的是新提交 (b299e) ,message 发生变化但提交时间和作者信息与之前是一致的。
+
+```shell
+koshiba@koshiba hello-git % vim hello.txt
+koshiba@koshiba hello-git % git add hello.txt
+koshiba@koshiba hello-git % git status
+On branch master
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ modified: hello.txt
+
+koshiba@koshiba hello-git % git commit --amend -m "second commit (amend)"
+[master b299e54] second commit (amend)
+ Date: Wed Sep 28 20:11:37 2022 +0800
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+koshiba@koshiba hello-git % git log
+commit b299e542f8677f2c1e55b17cd9aa24bb2241ebad (HEAD -> master)
+Author: koshiba
+Date: Wed Sep 28 20:11:37 2022 +0800
+
+ second commit (amend)
+
+commit 6d70b38bd6becddcd2b70fb1a5bc3ee929070497
+Author: koshiba
+Date: Wed Sep 28 20:11:01 2022 +0800
+
+ first commit
+koshiba@koshiba hello-git %
+```
+
+
+
+每次提交时都可以使用 `git commit --amend -m ""` 来取代上一次提交,这样无论有多少次提交, `git log` 都只显示最后一次提交。(但是 `git reflog` 仍会显示每一次提交) 。
+
+```shell
+koshiba@koshiba hello-git % git reflog
+b299e54 (HEAD -> master) HEAD@{0}: commit (amend): second commit (amend)
+6eefb40 HEAD@{1}: commit: second commit
+6d70b38 HEAD@{2}: commit (initial): first commit
+koshiba@koshiba hello-git %
+```
+
+
+
+### git restore
+
+主要有两种用法,两种用法都必须指定文件名,可以一次指定多个文件 (文件名用空格分开) 。
+
+- 使用 `git restore --staged <文件名>` 将指定的暂存区中的文件移出暂存区。
+- 使用 `git restore <文件名>` 将工作目录下的「已修改」文件恢复为未修改状态。
+
+```shell
+koshiba@koshiba hello-git % git add hello.txt
+koshiba@koshiba hello-git % git status
+On branch master
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ modified: hello.txt
+
+koshiba@koshiba hello-git % git restore --staged hello.txt
+koshiba@koshiba hello-git % git status
+On branch master
+Changes not staged for commit:
+ (use "git add ..." to update what will be committed)
+ (use "git restore ..." to discard changes in working directory)
+ modified: hello.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+koshiba@koshiba hello-git % git restore hello.txt
+koshiba@koshiba hello-git % git status
+On branch master
+nothing to commit, working tree clean
+koshiba@koshiba hello-git %
+```
+
+
+
+### git reset
+
+使用 `git reset HEAD ` 将指定的已暂存的文件移出暂存区 (同 `git restore --staged ` ), 若不指定文件,则取消所有文件的暂存内容 (清空暂存区)。其中,`HEAD` 表示当前版本,退回到上一个版本可用 `HEAD^ `。
+
+`git reset HEAD ` 与 `git restore --staged ` 效果相同,但 git 的提示推荐使用 `git restore <文件名>` 用法。
+
+如下展示 `hello.txt` 加入暂存区后,执行 `git reset HEAD` 取消暂存的过程。
+
+```shell
+koshiba@koshiba hello-git % git add hello.txt
+koshiba@koshiba hello-git % git status
+On branch master
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ modified: hello.txt
+
+koshiba@koshiba hello-git % git reset HEAD hello.txt
+Unstaged changes after reset:
+M hello.txt
+koshiba@koshiba hello-git % git status
+On branch master
+Changes not staged for commit:
+ (use "git add ..." to update what will be committed)
+ (use "git restore ..." to discard changes in working directory)
+ modified: hello.txt
+
+no changes added to commit (use "git add" and/or "git commit -a")
+koshiba@koshiba hello-git %
+```
+
+
+
+### git checkout --
+
+使用 `git checkout -- ` 将工作目录下的「已修改」文件恢复为未修改状态,同 `git restore <文件名>` ,git 的提示推荐使用 `git restore <文件名>` 用法。
+
+如下,接上一节的操作。
+
+```shell
+koshiba@koshiba hello-git % git checkout -- hello.txt
+koshiba@koshiba hello-git % git status
+On branch master
+nothing to commit, working tree clean
+koshiba@koshiba hello-git %
+```
+
+
+
+## 切换操作
+
+切换操作也属于广义上的撤销,但更强调在版本间穿梭的特点。`git reset` 的主要能力特点就是能够实现版本之间穿梭的操作,即「切换操作」。
+
+在介绍命令之前有必要先说明如下三个「切换目标」的概念。
+
+| 切换目标 | 描述 |
+| ------------ | ------------------------------------------------------------ |
+| HEAD | 指向当前分支的当前版本 (commit) 的指针,可以理解为就是当前版本。 |
+| index | 可以理解为当前暂存区 |
+| working tree | 可以理解为当前工作目录 |
+
+`git reset` 命令有三种常用模式, `--mixed` (默认,不显式写出时即为此模式)、`--soft` 以及 `--hard` ,分别实现对不同「切换目标」的操作。
+
+| 命令 | 描述 |
+| ------------------------------------ | ------------------------------------------------------------ |
+| git reset --soft <版本号> | 将 HEAD 指向指定的版本 (commit) ,不改动 index 和 working tree。
※ 不可指定文件名 (指定则报错)。 |
+| git reset --mixed <版本号> | 不指定文件。将 HEAD 指向指定的版本 (commit) ,并使得暂存区与指定版本一致,即在指定版本之后加入暂存区的所有文件都移出暂存区。
※ `--mixed` 参数是默认的,也可不写。 |
+| git reset --mixed <版本号> <文件名> | 指定文件。HEAD 指针不变,但指定的文件对应的暂存区内容将切换到指定版本的状态,其他文件在暂存区的内容不变。 |
+| git reset --hard <版本号> | 将 HEAD, index 和 working tree 都切换到指定版本号的状态。
※ 不可指定文件名 (指定则报错)。 |
+
+※ 「版本号」可以用 `HEAD`, `HEAD^`, `HEAD^^`, `HEAD~1`, `HEAD~2` 等形式表示。HEAD 表示当前版本,HEAD^ 表示当前版本的前一个版本,有多少个 `^` 就往前多少个版本,`~1` 表示当前版本的前一个版本,数字是多少,就表示往前多少个版本。
+
+※ 在「撤销操作」中,我们说过 [git 2.23](https://github.blog/2019-08-16-highlights-from-git-2-23/) 版本加入 `git restore` 命令后,推荐使用 `git restore --staged <文件名>` 来代替 `git reset HEAD ` ,原因之一就是 `git reset` 承担的功能太多,有必要引入新的更直观的命令来取代一部分功能。
+
+
+
+### git reset --soft
+
+`git reset --soft ` 将 HEAD 指向指定的版本 (commit) ,不改动 index 和 working tree。简单直观的理解即执行后,在版本历史中的第一条 (当前版本) 即为指定的那个版本,除此之外无其他变化。
+
+如下演示 `--soft` 模式的效果。
+
+
+
+首先,依次提交 1.txt , 2.txt 和 3.txt 之后`git add` 4.txt 但不提交,则当前有 3 个版本,暂存区中有四个文件 (通过 `git ls-files` 查看)。
+
+```shell
+koshiba@koshiba hello-git % git reflog
+5bff39f (HEAD -> master) HEAD@{0}: commit: add 3
+d287e4b HEAD@{1}: commit: add 2
+c97c40e HEAD@{2}: commit (initial): add 1
+koshiba@koshiba hello-git %
+koshiba@koshiba hello-git %
+koshiba@koshiba hello-git %
+koshiba@koshiba hello-git % git ls-files
+1.txt
+2.txt
+3.txt
+koshiba@koshiba hello-git % vim 4.txt
+koshiba@koshiba hello-git % git add 4.txt
+koshiba@koshiba hello-git % git status
+On branch master
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ new file: 4.txt
+
+koshiba@koshiba hello-git % git ls-files
+1.txt
+2.txt
+3.txt
+4.txt
+koshiba@koshiba hello-git %
+```
+
+然后软切换回 (`--soft`) 第一个版本 (c97c40e),此时当前分支 (master) 的 HEAD 确实指向了指定的版本。通过 `git status` 和 `git ls-files` 可以看到,在暂存区的内容无变化,只不过因为切回了若干个版本,暂存区「领先」了当前版本 (c97c40e) ,因此 2.txt, 3.txt, 4.txt 都是「Changes to be committed」。
+
+```shell
+koshiba@koshiba hello-git % git reset --soft c97c40e
+koshiba@koshiba hello-git % git reflog
+c97c40e (HEAD -> master) HEAD@{0}: reset: moving to c97c40e
+5bff39f HEAD@{1}: commit: add 3
+d287e4b HEAD@{2}: commit: add 2
+c97c40e (HEAD -> master) HEAD@{3}: commit (initial): add 1
+koshiba@koshiba hello-git % git status
+On branch master
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ new file: 2.txt
+ new file: 3.txt
+ new file: 4.txt
+
+koshiba@koshiba hello-git % git ls-files
+1.txt
+2.txt
+3.txt
+4.txt
+koshiba@koshiba hello-git %
+```
+
+由此我们验证了 `--soft` 模式确实只是将 HEAD 指向指定的版本,暂存区与工作目录无变化。
+
+
+
+### git reset --mixed
+
+`git reset --mixed ` 命令,可以指定文件或不指定文件,效果不同。
+
+- 不指定文件时。即包含了 `--soft` 实现的功能,即 `HEAD` 指针指向指定的版本,又实现了「暂存区」的切换,即使得暂存区与指定版本一致。
+- 指定文件时。`HEAD` 指针不变,但指定的文件对应的暂存区内容将切换到指定版本的状态。
+
+
+
+首先,提交 1.txt 作为第一个版本 (fc746eb) ,提交 2-1.txt 和 2-2.txt 作为第二个版本 (d3f6a97) ,提交 3.txt 作为第三个版本 (23a6441) ,将 4.txt 放入暂存区但不提交。
+
+```shell
+koshiba@koshiba hello-git % vim 1.txt
+koshiba@koshiba hello-git % git add 1.txt
+koshiba@koshiba hello-git % git commit -m "add 1.txt"
+[master (root-commit) fc746eb] add 1.txt
+ 1 file changed, 1 insertion(+)
+ create mode 100644 1.txt
+koshiba@koshiba hello-git % vim 2-1.txt
+koshiba@koshiba hello-git % vim 2-2.txt
+koshiba@koshiba hello-git % git add 2-1.txt 2-2.txt
+koshiba@koshiba hello-git % git commit -m "add 2-1.txt 2-2.txt"
+[master d3f6a97] add 2-1.txt 2-2.txt
+ 2 files changed, 2 insertions(+)
+ create mode 100644 2-1.txt
+ create mode 100644 2-2.txt
+koshiba@koshiba hello-git % vim 3.txt
+koshiba@koshiba hello-git % git add 3.txt
+koshiba@koshiba hello-git % git commit -m "add 3.txt"
+[master 23a6441] add 3.txt
+ 1 file changed, 1 insertion(+)
+ create mode 100644 3.txt
+koshiba@koshiba hello-git % vim 4.txt
+koshiba@koshiba hello-git % git add 4.txt
+koshiba@koshiba hello-git % git status
+On branch master
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ new file: 4.txt
+
+koshiba@koshiba hello-git %
+```
+
+可以看到暂存区中有五个文件,提交历史有三次,当前 HEAD 指向的是提交了 3.txt 的版本。
+
+```shell
+koshiba@koshiba hello-git % git ls-files
+1.txt
+2-1.txt
+2-2.txt
+3.txt
+4.txt
+koshiba@koshiba hello-git % git reflog
+23a6441 (HEAD -> master) HEAD@{0}: commit: add 3.txt
+d3f6a97 HEAD@{1}: commit: add 2-1.txt 2-2.txt
+fc746eb HEAD@{2}: commit (initial): add 1.txt
+koshiba@koshiba hello-git %
+```
+
+接着执行混合切换 (`--mixed`) 到第二个版本,即提交了 2-1.txt 和 2-2.txt 的版本 (d3f6a97) ,由于未指定文件,因此实现的效果是 HEAD 指向 d3f6a97 ,且暂存区也恢复到 d3f6a97 的状态。查看提交历史,HEAD 确实为 d3f6a97,且 message 提示了是通过 reset 切换而来的。通过 `git ls-files` 看到此时暂存区里的文件也确实是第二个版本对应的三个文件 1.txt, 2-1.txt, 2-2.txt ,即相当于把指定版本之后才放到暂存区的文件都移出了暂存区,导致这些文件出现在 `git status` 的提示中,即他们此时是 「Untracked files」(在指定版本之后才创建的)。
+
+```shell
+koshiba@koshiba hello-git % git reset --mixed d3f6a97
+koshiba@koshiba hello-git % git reflog
+d3f6a97 (HEAD -> master) HEAD@{0}: reset: moving to d3f6a97
+23a6441 HEAD@{1}: commit: add 3.txt
+d3f6a97 (HEAD -> master) HEAD@{2}: commit: add 2-1.txt 2-2.txt
+fc746eb HEAD@{3}: commit (initial): add 1.txt
+koshiba@koshiba hello-git % git status
+On branch master
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ 3.txt
+ 4.txt
+
+nothing added to commit but untracked files present (use "git add" to track)
+koshiba@koshiba hello-git % git ls-files
+1.txt
+2-1.txt
+2-2.txt
+koshiba@koshiba hello-git %
+```
+
+
+
+接着再测试指定文件的效果,如下,由于 `--mixed` 是默认的参数,可以省略。执行 `git reset fc746eb 2-1.txt` 将指定文件 2-1.txt 对应的暂存区内容切换回 `fc746eb` 版本 (提交了 1.txt 的第一个版本) ,即只将此文件对应暂存区的内容移除对应到指定的版本状态 (对这个例子来说,即移除,因为 1.txt 提交的时候尚未创建 2-1.txt),其他文件在暂存区的内容保留。
+
+首先通过 `git reflog` 可以看到 HEAD 不变。
+
+```shell
+koshiba@koshiba hello-git % git reset fc746eb 2-1.txt
+koshiba@koshiba hello-git % git reflog
+d3f6a97 (HEAD -> master) HEAD@{0}: reset: moving to d3f6a97
+23a6441 HEAD@{1}: commit: add 3.txt
+d3f6a97 (HEAD -> master) HEAD@{2}: commit: add 2-1.txt 2-2.txt
+fc746eb HEAD@{3}: commit (initial): add 1.txt
+koshiba@koshiba hello-git %
+```
+
+接着,通过 `git ls-files` 看到当前暂存区已无 2-1.txt ,但却有 2-2.txt ,说明只是移除了指定的文件对应的暂存区内容。另外执行 `git status` 可以看到移出的 2-1.txt 以及在前一次不带文件名的混合切换后移出的 3.txt 和 4.txt 都变为了 「Untracked files」。
+
+```shell
+koshiba@koshiba hello-git % git status
+On branch master
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ deleted: 2-1.txt
+
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ 2-1.txt
+ 3.txt
+ 4.txt
+
+koshiba@koshiba hello-git % git ls-files
+1.txt
+2-2.txt
+koshiba@koshiba hello-git %
+```
+
+
+
+### git reset --hard
+
+`git reset --hard ` 命令将 HEAD, index 和 working tree 都切换到指定版本号的状态
+
+
+
+接着之前 `git reset --mixed` 的操作后继续演示,首先执行 `git reset 23a6441` 恢复到提交了 3.txt 之后的版本状态。
+
+```shell
+koshiba@koshiba hello-git % git reset 23a6441
+koshiba@koshiba hello-git % git status
+On branch master
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ 4.txt
+
+nothing added to commit but untracked files present (use "git add" to track)
+koshiba@koshiba hello-git % git ls-files
+1.txt
+2-1.txt
+2-2.txt
+3.txt
+koshiba@koshiba hello-git % git log --oneline
+23a6441 (HEAD -> master) add 3.txt
+d3f6a97 add 2-1.txt 2-2.txt
+fc746eb add 1.txt
+koshiba@koshiba hello-git %
+```
+
+然后执行 `git reset --hard fc746eb` 硬切换回提交了 1.txt 之后的版本。可以看到如下效果,即 HEAD, index (暂存区) 和工作目录确实都切换到了 fc746eb 的状态。略有不同的是此时还存在新建状态但未 `git add` 的 4.txt,这是因为执行此次硬切换时 4.txt 是未 `git add` 的状态,不被 git 所管控。
+
+```shell
+koshiba@koshiba hello-git % git reset --hard fc746eb
+HEAD is now at fc746eb add 1.txt
+koshiba@koshiba hello-git % git log --oneline
+fc746eb (HEAD -> master) add 1.txt
+koshiba@koshiba hello-git % git reflog
+fc746eb (HEAD -> master) HEAD@{0}: reset: moving to fc746eb
+23a6441 HEAD@{1}: reset: moving to 23a6441
+d3f6a97 HEAD@{2}: reset: moving to d3f6a97
+23a6441 HEAD@{3}: commit: add 3.txt
+d3f6a97 HEAD@{4}: commit: add 2-1.txt 2-2.txt
+fc746eb (HEAD -> master) HEAD@{5}: commit (initial): add 1.txt
+koshiba@koshiba hello-git % git status
+On branch master
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ 4.txt
+
+nothing added to commit but untracked files present (use "git add" to track)
+koshiba@koshiba hello-git % git ls-files
+1.txt
+koshiba@koshiba hello-git % ls -al
+total 16
+drwxr-xr-x@ 5 koshiba staff 160 Sep 29 16:21 .
+drwxr-xr-x@ 10 koshiba staff 320 Sep 29 16:21 ..
+drwxr-xr-x@ 13 koshiba staff 416 Sep 29 16:20 .git
+-rw-r--r-- 1 koshiba staff 3 Sep 29 15:34 1.txt
+-rw-r--r-- 1 koshiba staff 4 Sep 29 15:35 4.txt
+koshiba@koshiba hello-git %
+```
+
+如果这时候我们再次硬切换回提交了 3.txt 的版本 (23a6441),可以看到如下效果,即恢复到了该版本状态,不同的也是 4.txt 仍然存在,原因同前述。
+
+```shell
+koshiba@koshiba hello-git % git reset --hard 23a6441
+HEAD is now at 23a6441 add 3.txt
+koshiba@koshiba hello-git % ls -al
+total 40
+drwxr-xr-x@ 8 koshiba staff 256 Sep 29 16:21 .
+drwxr-xr-x@ 10 koshiba staff 320 Sep 29 16:21 ..
+drwxr-xr-x@ 13 koshiba staff 416 Sep 29 16:21 .git
+-rw-r--r-- 1 koshiba staff 3 Sep 29 15:34 1.txt
+-rw-r--r-- 1 koshiba staff 2 Sep 29 16:21 2-1.txt
+-rw-r--r-- 1 koshiba staff 4 Sep 29 16:21 2-2.txt
+-rw-r--r-- 1 koshiba staff 4 Sep 29 16:21 3.txt
+-rw-r--r-- 1 koshiba staff 4 Sep 29 15:35 4.txt
+koshiba@koshiba hello-git % git status
+On branch master
+Untracked files:
+ (use "git add ..." to include in what will be committed)
+ 4.txt
+
+nothing added to commit but untracked files present (use "git add" to track)
+koshiba@koshiba hello-git % git ls-files
+1.txt
+2-1.txt
+2-2.txt
+3.txt
+koshiba@koshiba hello-git % git log --oneline
+23a6441 (HEAD -> master) add 3.txt
+d3f6a97 add 2-1.txt 2-2.txt
+fc746eb add 1.txt
+koshiba@koshiba hello-git % git reflog
+23a6441 (HEAD -> master) HEAD@{0}: reset: moving to 23a6441
+fc746eb HEAD@{1}: reset: moving to fc746eb
+23a6441 (HEAD -> master) HEAD@{2}: reset: moving to 23a6441
+d3f6a97 HEAD@{3}: reset: moving to d3f6a97
+23a6441 (HEAD -> master) HEAD@{4}: commit: add 3.txt
+d3f6a97 HEAD@{5}: commit: add 2-1.txt 2-2.txt
+fc746eb HEAD@{6}: commit (initial): add 1.txt
+koshiba@koshiba hello-git %
+```
+
+
+
+## 分支操作
+
+分支本质上是指向提交对象的可变指针。
+
+
+
+### git branch
+
+新建分支本质上是 git 创建了一个可以移动的指向当前提交对象的新指针。
+
+使用 `git branch <分支名>` 创建分支。
+
+```shell
+koshiba@koshiba hello-git % git branch testing
+koshiba@koshiba hello-git % git branch -a
+* master
+ remote
+ testing
+ remotes/origin/master
+koshiba@koshiba hello-git %
+```
+
+
+
+使用 `git branch -d <分支名>` 删除分支,执行 `git branch` 查看本地仓库分支列表, `*` 表示当前分支。
+
+```shell
+koshiba@koshiba hello-git % git branch -d testing
+Deleted branch testing (was 070472d).
+koshiba@koshiba hello-git % git branch
+* master
+ remote
+koshiba@koshiba hello-git %
+```
+
+
+
+### git switch
+
+使用 `git switch <分支名>` 切换分支。
+
+```shell
+koshiba@koshiba hello-git % git branch testing
+koshiba@koshiba hello-git % git branch
+* master
+ testing
+koshiba@koshiba hello-git % git switch testing
+Switched to branch 'testing'
+koshiba@koshiba hello-git % git status
+On branch testing
+nothing to commit, working tree clean
+koshiba@koshiba hello-git % git switch -c fix
+Switched to a new branch 'fix'
+koshiba@koshiba hello-git % git branch
+* fix
+ master
+ testing
+koshiba@koshiba hello-git %
+```
+
+
+
+使用 `git switch -c <分支名>` 创建并切换分支。
+
+使用 `git switch -` 切换到上一次所在分支。
+
+
+
+### git checkout
+
+使用 `git checkout <分支名>` 切换分支。
+
+```shell
+koshiba@koshiba hello-git % git checkout testing
+Switched to branch 'testing'
+koshiba@koshiba hello-git % git status
+On branch testing
+nothing to commit, working tree clean
+koshiba@koshiba hello-git %
+```
+
+
+
+使用 `git checkout -b <分支名>` 创建并切换分支。
+
+
+
+### git merge
+
+`git merge <分支名>` 将指定分支合并到当前分支中。合并通常有「快进」和 「三方合并」。
+
+新建并切换到 testing 分支后,在 testing 分支上完成若干次 commit,且 master 无新的 commit,此时切回 master 分支,并执行 `git merge` 将 testing 合并到当前分支中。
+
+```shell
+koshiba@koshiba hello-git % git checkout master
+Switched to branch 'master'
+Your branch is ahead of 'origin/master' by 3 commits.
+ (use "git push" to publish your local commits)
+koshiba@koshiba hello-git % git merge testing
+Updating 44f99a2..b4552ad
+Fast-forward
+ hello.txt | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+koshiba@koshiba hello-git %
+```
+
+由于 master 是 testing 的直接祖先,master 可顺着 testing 分支的历次 commit 直接到达当前 testing,此情况下的合并无需解决分歧,因此 master 可以「快进」直达 testing,而不会产生新的版本 (快照),回显中的 `Fast-forwad` 反映了这一特点。
+
+
+
+如下可以看到在 master 分支下合并 testing 分支后,当前 HEAD (55d1aca) 与 testing 最后一次提交是同一个。
+
+```shell
+55d1aca (HEAD -> master, testing) HEAD@{0}: merge testing: Fast-forward
+6255ab9 HEAD@{1}: checkout: moving from testing to master
+55d1aca (HEAD -> master, testing) HEAD@{2}: commit: testing commit
+6255ab9 HEAD@{3}: checkout: moving from master to testing
+6255ab9 HEAD@{4}: commit (initial): master commit
+```
+
+
+
+若从分歧点开始 master 和 testing 都各自前进了若干个 commit,则此时合并为「三方合并」。若产生冲突,则有如下提示。
+
+```shell
+koshiba@koshiba hello-git % git log --oneline --graph --all
+* 4a3e876 (HEAD -> testing) testing 1
+| * b6c50b9 (master) master 1
+|/
+* b4552ad modified hello.txt 3
+...(略)...
+koshiba@koshiba hello-git % git merge testing
+Auto-merging hello.txt
+CONFLICT (content): Merge conflict in hello.txt
+Automatic merge failed; fix conflicts and then commit the result.
+koshiba@koshiba hello-git %
+```
+
+解决冲突后,执行 `git status` 可以看到 `All conflicts fixed but you are still merging.` ,即说明冲突已解决,根据提示再次提交即完成合并。与「快进」合并不同的是,「三方合并」会生成一个新的快照。
+
+```
+koshiba@koshiba hello-git % git status
+On branch master
+Your branch is ahead of 'origin/master' by 7 commits.
+ (use "git push" to publish your local commits)
+
+All conflicts fixed but you are still merging.
+ (use "git commit" to conclude merge)
+
+koshiba@koshiba hello-git %
+koshiba@koshiba hello-git % git commit
+[master b8da922] Merge branch 'testing'
+koshiba@koshiba hello-git % git log --oneline --graph --all
+* b8da922 (HEAD -> master) Merge branch 'testing'
+|\
+| * 4a3e876 (testing) testing 1
+* | b6c50b9 master 1
+|/
+* b4552ad modified hello.txt 3
+...(略)...
+```
+
+
+
+使用如下命令随时以图形形式查看不同分支的前进和合并过程。
+
+```
+git log --oneline --decorate --graph --all
+```
+
+
+
+### git rebase
+
+两分支从分叉处开始各自有若干提交,合并这两个分支的另一种方式是 `git rebase` (变基),与 `git merge` 执行「三方合并」不同的是,`git rebase` 会在当前分支下重放另一个分支在分叉处后的历次提交,最终得到一个新提交,结果与 `git merge` 相同,但提交历史更整洁,分支虽并行,但变基之后的提交历史是一条串行的直线。
+
+
+
+当前分叉后 master 分支和 testing 分支各有一个提交版本。
+
+```shell
+koshiba@koshiba hello-git % git log --oneline --decorate --graph --all
+* e2f517c (HEAD -> testing) testing modified 1
+| * ae47d2e (master) master modified 1
+| * b8da922 Merge branch 'testing'
+| |\
+| |/
+|/|
+* | 4a3e876 testing 1
+...(略)...
+```
+
+
+
+切回 master ,执行变基,如下,有冲突,根据提示先解决冲突。
+
+```shell
+koshiba@koshiba hello-git % git checkout master
+Switched to branch 'master'
+koshiba@koshiba hello-git % git rebase testing
+Auto-merging hello.txt
+CONFLICT (content): Merge conflict in hello.txt
+error: could not apply b6c50b9... master 1
+hint: Resolve all conflicts manually, mark them as resolved with
+hint: "git add/rm ", then run "git rebase --continue".
+hint: You can instead skip this commit: run "git rebase --skip".
+hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
+Could not apply b6c50b9... master 1
+```
+
+
+
+解决冲突后,对冲突文件执行 `git add` 后,再次执行变基 `git rebase --continue` 。
+
+```shell
+koshiba@koshiba hello-git % git rebase --continue
+Successfully rebased and updated refs/heads/master.
+koshiba@koshiba hello-git % git log --oneline --decorate --graph --all
+* e2f517c (HEAD -> master, testing) testing modified 1
+* 4a3e876 testing 1
+...(略)...
+```
+
+
+
+也可以直接执行 `git rebase ` 将 topicbranch 分叉后的修改在 basebranch 上重放,省去了 `git checkout` 切换到 basebranch 的操作。
+
+
+
+**变基的原则**:只对尚未推送的本地修改执行变基,而不对本地仓库外有副本的分支执行变基。
+
+
+
+## 远程操作
+
+本地仓库与远程仓库之间建立关联的方式如下。
+
+- 方式一(本地到远程):先建立本地仓库,然后将本地仓库推送到远程。
+- 方式二(远程到本地):先建立远程仓库,然后在本地克隆远程仓库。
+
+
+
+### git remote
+
+实现从本地到远程的方式一,首先要使用 `git remote add <仓库别名> ` 关联本地仓库与远程 github 仓库。根据通信协议的不同,分为 https 方式和 ssh 方式。
+
+
+
+#### https方式
+
+【1. 关联本地仓库与远程仓库】
+
+执行如下命令完成关联,并检查关联结果。
+
+```shell
+koshiba@koshiba git-demo % git remote add origin https://github.com/ikoshiba/git-demo.git
+koshiba@koshiba git-demo % git remote
+origin
+koshiba@koshiba git-demo % git remote -v
+origin https://github.com/ikoshiba/git-demo.git (fetch)
+origin https://github.com/ikoshiba/git-demo.git (push)
+koshiba@koshiba git-demo %
+```
+
+
+
+【2. 在 github 上创建远程仓库】
+
+仓库名通常与本地仓库相同,但也可以不同。
+
+
+
+【3. 推送】
+
+关联后执行 `git push -u origin master` 推送本地仓库到远程仓库中。其中 `-u` 参数是 `--set-upstream` 的缩略,使得当前本地分支 `master` 与远程仓库分支 `master` 关联起来,后续再推送时,就只需要执行 `git push` 即可。
+
+```shell
+koshiba@koshiba git-demo % git push -u origin master
+Username for 'https://github.com': ikoshiba
+Password for 'https://ikoshiba@github.com':
+remote: Support for password authentication was removed on August 13, 2021.
+remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
+fatal: Authentication failed for 'https://github.com/ikoshiba/git-demo.git/'
+koshiba@koshiba git-demo %
+```
+
+注意,根据如上提示,github 从 2021年8月13日起,https 方式的接入校验不再支持使用 **普通密码** 的方式,目前支持使用 **Personal Access Token** 来完成认证,即 `Password for 'https://...'` 处应当填写目标 github 账号的 Personal Access Token (PAT) ,设置方式在 [这里](https://docs.github.com/cn/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) 查看。
+
+填入正确的 PAT 后即可成功推送,如下。
+
+```shell
+koshiba@koshiba git-demo % git push -u origin master
+Username for 'https://github.com': ikoshiba
+Password for 'https://ikoshiba@github.com':
+Enumerating objects: 57, done.
+Counting objects: 100% (57/57), done.
+Delta compression using up to 8 threads
+Compressing objects: 100% (37/37), done.
+Writing objects: 100% (57/57), 5.02 KiB | 642.00 KiB/s, done.
+Total 57 (delta 5), reused 0 (delta 0), pack-reused 0
+remote: Resolving deltas: 100% (5/5), done.
+To https://github.com/ikoshiba/git-demo.git
+ * [new branch] master -> master
+branch 'master' set up to track 'origin/master'.
+koshiba@koshiba git-demo %
+```
+
+注意,如果没有事先在 github 上创建目标关联远程仓库的话,推送将会失败,并显示失败原因是找不到指定的远程仓库。
+
+```shell
+koshiba@koshiba git-demo % git push -u origin master
+remote: Repository not found.
+fatal: repository 'https://github.com/ikoshiba/git-demo.git/' not found
+koshiba@koshiba git-demo
+```
+
+
+
+【4. 查看远程库信息】
+
+执行 `git remote show origin` 查看远程库信息。
+
+```shell
+koshiba@koshiba hello-git % git remote show origin
+* remote origin
+ Fetch URL: https://github.com/ikoshiba/hello-git.git
+ Push URL: https://github.com/ikoshiba/hello-git.git
+ HEAD branch: master
+ Remote branch:
+ master tracked
+ Local branch configured for 'git pull':
+ master merges with remote master
+ Local ref configured for 'git push':
+ master pushes to master (up to date)
+koshiba@koshiba hello-git %
+```
+
+
+
+#### ssh方式
+
+https 方式每次推送都需要认证,比较麻烦,我们可以采用 ssh 方式约定本地仓库与远程仓库的公私钥,即可自动认证。
+
+通常情况下客户端环境只需要一对公私钥建立到 github 远程库的 ssh 通信。若希望在同一个客户端下与多个 github 账号下的仓库建立 ssh 通信,则由于 github 不同账号下的公钥必须不同,相应地,在本地创建公私钥对时,也需要创建多对。
+
+如下演示如何创建两对不同的公私钥,并应用到对不同的 github 账号的 ssh 连接中。
+
+
+
+【1. ssh-keygen 命令生成公私钥对】
+
+执行 `ssh-keygen -t rsa -C "yuki.com"` 命令生成公私钥对。`-t` 选择加密算法, `-C` 添加备注,习惯上会填写一个邮箱地址,也可以填写其他字符串。
+
+| 设置 | 描述 |
+| -------------------------------------- | ------------------------------------------------------------ |
+| Enter file in which to save the key () | 括号后是默认公私钥路径和名称
私钥默认名: id_rsa
公钥默认名: id_rsa.pub |
+| Enter passphrase | 加密串,即 ssh 通信时的口令,通常不使用,回车即可 |
+
+
+
+```shell
+koshiba@koshiba ~ % ssh-keygen -t rsa -C "yuki.com"
+Generating public/private rsa key pair.
+Enter file in which to save the key (/Users/koshiba/.ssh/id_rsa): /Users/koshiba/.ssh/id_rsa_163
+Enter passphrase (empty for no passphrase):
+Enter same passphrase again:
+Your identification has been saved in /Users/koshiba/.ssh/id_rsa_163
+Your public key has been saved in /Users/koshiba/.ssh/id_rsa_163.pub
+The key fingerprint is:
+SHA256:rS6a3ou3HauNl0w+1Y5v6oTzbVo7xu/PKaOVIQbMCoI yuki.com
+The key's randomart image is:
++---[RSA 3072]----+
+| |
+| . o |
+| E . . + |
+| . . ... |
+| .S .+ . |
+| .oo o o |
+| +=o.+.o |
+| +o=**.oO+ ..|
+| .=o**=o=O=+=oo|
++----[SHA256]-----+
+koshiba@koshiba ~ %
+```
+
+接着执行 `ssh-keygen -t rsa -C "yama.com"` 生成另一对公私钥对,在 `~/.ssh` 下可以看到如下两对公私钥对。
+
+```
+id_rsa_163
+id_rsa_163.pub
+id_rsa_outlook
+id_rsa_outlook.pub
+```
+
+
+
+【2. 在 `.ssh` 下创建 `config` 文件】
+
+创建如下 `config` 文件。
+
+```
+# github-163
+Host github.com.163
+HostName github.com
+PreferredAuthentications publickey
+IdentityFile ~/.ssh/id_rsa_163
+
+# github-outlook
+Host github.com.outlook
+HostName github.com
+PreferredAuthentications publickey
+IdentityFile ~/.ssh/id_rsa_outlook
+```
+
+后面我们会看到,将指定的公私钥对与指定的 github 账户相关联的关键是 `Host` ,它是远程托管服务端的别名,在使用 `git@` 方式执行 ssh 连接时,`` 填入的即 `Host` 。
+
+`Host` 与 `IdentityFile` 决定了公私钥对与具体账户的对应关系 (该账户下用公钥与 `IdentifyFile` 中指定的一样) ,而实际上会连接 `Hostname` 。
+
+
+
+【3. 将公钥添加对应的 github 账户中】
+
+在 github 的 `setting > SSH and GPG keys > SSH keys` 中点击 `New SSH key` 添加公钥的内容。
+
+使用 `ssh -T git@github.com.outlook` 命令测试 ssh 连接,出现如下提示即说明可通过 ssh 方式通信。
+
+```shell
+koshiba@koshiba hello-git % ssh -T git@github.com.outlook
+Hi ikoshiba! You've successfully authenticated, but GitHub does not provide shell access.
+koshiba@koshiba hello-git %
+```
+
+
+
+【4. 关联本地仓库与远程仓库】
+
+使用 `git remote add` 命令关联本地仓库与远程仓库。若当前存在 https 方式的关联,先执行 `git remote rm origin` 删除。
+
+```shell
+koshiba@koshiba hello-git % git remote add origin git@github.com.outlook:ikoshiba/hello-git.git
+koshiba@koshiba hello-git % git remote -v
+origin git@github.com.outlook:ikoshiba/hello-git.git (fetch)
+origin git@github.com.outlook:ikoshiba/hello-git.git (push)
+```
+
+
+
+【5. 推送】
+
+与 https 方式一样,推送前也要提前在 github 中创建好目标远程仓库,否则会报如下错 `ERROR: Repository not found.` 。
+
+```shell
+koshiba@koshiba hello-git % git push -u origin master
+ERROR: Repository not found.
+fatal: Could not read from remote repository.
+```
+
+创建后再次推送成功,远程库中也出现了本地库推送的文件。
+
+```shell
+koshiba@koshiba hello-git % git push -u origin master
+Enumerating objects: 57, done.
+Counting objects: 100% (57/57), done.
+Delta compression using up to 8 threads
+Compressing objects: 100% (32/32), done.
+Writing objects: 100% (57/57), 5.02 KiB | 5.02 MiB/s, done.
+Total 57 (delta 5), reused 57 (delta 5), pack-reused 0
+remote: Resolving deltas: 100% (5/5), done.
+To github.com.outlook:ikoshiba/hello-git.git
+ * [new branch] master -> master
+branch 'master' set up to track 'origin/master'.
+koshiba@koshiba hello-git %
+```
+
+
+
+【6. 查看远程库信息】
+
+执行 `git remote show origin` 查看远程库信息。
+
+```shell
+koshiba@koshiba hello-git % git remote show origin
+* remote origin
+ Fetch URL: git@github.com.outlook:ikoshiba/hello-git.git
+ Push URL: git@github.com.outlook:ikoshiba/hello-git.git
+ HEAD branch: master
+ Remote branch:
+ master tracked
+ Local branch configured for 'git pull':
+ master merges with remote master
+ Local ref configured for 'git push':
+ master pushes to master (up to date)
+koshiba@koshiba hello-git %
+```
+
+
+
+#### rename
+
+可以通过 `git remote rename <当前别名> <新别名>` 来修改远程仓库地址别名。下面是一些修改后的操作,可以看到远程仓库地址别名已经变为 `main` ,原先的 `origin` 已不可用,且远程仓库分支也相应变为 `main/master` 。
+
+```shell
+koshiba@koshiba hello-git % git remote rename origin main
+Renaming remote references: 100% (1/1), done.
+koshiba@koshiba hello-git % git remote
+main
+koshiba@koshiba hello-git % git remote -v
+main git@github.com.outlook:ikoshiba/hello-git.git (fetch)
+main git@github.com.outlook:ikoshiba/hello-git.git (push)
+koshiba@koshiba hello-git % git branch -r
+ main/master
+koshiba@koshiba hello-git % git branch -a
+* master
+ remote
+ remotes/main/master
+koshiba@koshiba hello-git % git push
+Everything up-to-date
+koshiba@koshiba hello-git % git push main master
+Everything up-to-date
+koshiba@koshiba hello-git % git remote show origin
+fatal: 'origin' does not appear to be a git repository
+fatal: Could not read from remote repository.
+
+Please make sure you have the correct access rights
+and the repository exists.
+koshiba@koshiba hello-git % git remote show main
+* remote main
+ Fetch URL: git@github.com.outlook:ikoshiba/hello-git.git
+ Push URL: git@github.com.outlook:ikoshiba/hello-git.git
+ HEAD branch: master
+ Remote branch:
+ master tracked
+ Local branch configured for 'git pull':
+ master merges with remote master
+ Local ref configured for 'git push':
+ master pushes to master (up to date)
+koshiba@koshiba hello-git % git reflog main/master
+be33e38 (HEAD -> master, main/master) refs/remotes/main/master@{0}: remote: renamed refs/remotes/origin/master to refs/remotes/main/master
+be33e38 (HEAD -> master, main/master) refs/remotes/main/master@{1}: update by push
+ad50bf4 refs/remotes/main/master@{2}: fetch: fast-forward
+edf13a1 refs/remotes/main/master@{3}: fetch: fast-forward
+b0c4efa (remote) refs/remotes/main/master@{4}: update by push
+de2bb32 refs/remotes/main/master@{5}: fetch: fast-forward
+a343286 refs/remotes/main/master@{6}: update by push
+eeeea6f refs/remotes/main/master@{7}: pull: fast-forward
+2464feb refs/remotes/main/master@{8}: update by push
+koshiba@koshiba hello-git %
+```
+
+
+
+#### remove
+
+使用 `git remote remove <远程仓库地址别名>` 来解除本地仓库与远程仓库的关联。`remove` 也可以简写为 `rm` 。
+
+```shell
+koshiba@koshiba hello-git % git remote remove main
+koshiba@koshiba hello-git % git remote
+koshiba@koshiba hello-git % git branch -r
+koshiba@koshiba hello-git % git reflog main/master
+fatal: ambiguous argument 'main/master': unknown revision or path not in the working tree.
+Use '--' to separate paths from revisions, like this:
+'git [...] -- [...]'
+koshiba@koshiba hello-git %
+```
+
+
+
+### git clone
+
+使用 `git clone` 命令将远程仓库克隆到当前本地目录下。可以看到该命令将远程仓库拉取到当前路径下 (即相当于下载了 hello-git) ,并且完成了 `git init` 管理已经下载到本地的 `hello-git` (有 `.git` 隐藏文件夹),也完成了 `git remote add` 关联,并且远程仓库地址的别名也已自动设置为 `origin` 。
+
+```shell
+koshiba@koshiba git % git clone https://github.com/ikoshiba/hello-git.git
+Cloning into 'hello-git'...
+remote: Enumerating objects: 57, done.
+remote: Counting objects: 100% (57/57), done.
+remote: Compressing objects: 100% (32/32), done.
+remote: Total 57 (delta 5), reused 57 (delta 5), pack-reused 0
+Receiving objects: 100% (57/57), 5.02 KiB | 733.00 KiB/s, done.
+Resolving deltas: 100% (5/5), done.
+koshiba@koshiba git % cd hello-git
+koshiba@koshiba hello-git % ls -la
+total 24
+drwxr-xr-x@ 5 koshiba staff 160 Sep 27 05:53 .
+drwxr-xr-x@ 10 koshiba staff 320 Sep 27 05:53 ..
+-rw-r--r-- 1 koshiba staff 6148 Sep 27 05:53 .DS_Store
+drwxr-xr-x@ 12 koshiba staff 384 Sep 27 05:53 .git
+-rw-r--r-- 1 koshiba staff 31 Sep 27 05:53 hello.txt
+koshiba@koshiba hello-git % git remote -v
+origin https://github.com/ikoshiba/hello-git.git (fetch)
+origin https://github.com/ikoshiba/hello-git.git (push)
+koshiba@koshiba hello-git %
+```
+
+
+
+### git push
+
+执行 `git push <远程仓库地址> <本地分支名>:<远程分支名>` 推送当前仓库到远程库。
+
+- 远程仓库地址在 `git remote add` 命令中已经设置了 `origin` 别名,因此可以填入 `origin` 。
+- 当「本地分支名」与「远程分支名」相同时,本例中为 `master` ,可以写为 `git push origin master` 。
+- 在第一次推送时,我们使用了 `-u` 参数指定了本地分支到指定远程分支的通道 (master -> master) 。因此可以直接执行 `git push` ,效果等同如下。
+
+```
+git push git@github.com.outlook:ikoshiba/hello-git.git master:master
+```
+
+如果本地仓库当前分支比远程分支要落后,例如远程分支上的文件有更新,本地在尚未拉取合并该更新的情况下尝试推送本地的新提交,则会出现失败,如下。可以使用 `git pull` 先行拉取合并,或者执行 `git merger <远程仓库地址>/<远程分支>` 来将「本地的远程分支」合并到本地当前分支。
+
+```shell
+To github.com.outlook:ikoshiba/hello-git.git
+ ! [rejected] master -> master (non-fast-forward)
+error: failed to push some refs to 'github.com.outlook:ikoshiba/hello-git.git'
+hint: Updates were rejected because the tip of your current branch is behind
+hint: its remote counterpart. Integrate the remote changes (e.g.
+hint: 'git pull ...') before pushing again.
+hint: See the 'Note about fast-forwards' in 'git push --help' for details.
+koshiba@koshiba hello-git %
+```
+
+如果本地和远程分支是同一个版本,即无更新,则推送后显示 `Everything up-to-date` 。
+
+```shell
+koshiba@koshiba hello-git % git push
+Everything up-to-date
+```
+
+若有更新,且正常推送,则显示如下。
+
+```shell
+koshiba@koshiba hello-git % git push
+Enumerating objects: 5, done.
+Counting objects: 100% (5/5), done.
+Delta compression using up to 8 threads
+Compressing objects: 100% (3/3), done.
+Writing objects: 100% (3/3), 312 bytes | 312.00 KiB/s, done.
+Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
+To github.com.outlook:ikoshiba/hello-git.git
+ eeeea6f..a343286 master -> master
+koshiba@koshiba hello-git %
+```
+
+
+
+### git fetch
+
+将远程库当前版本拉取到本地,但不合并 (`git merge`) 。若本地分支与远程分支版本相同,则无回显,若不同,则如下。
+
+```shell
+koshiba@koshiba hello-git % git fetch
+remote: Enumerating objects: 5, done.
+remote: Counting objects: 100% (5/5), done.
+remote: Compressing objects: 100% (3/3), done.
+remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
+Unpacking objects: 100% (3/3), 683 bytes | 170.00 KiB/s, done.
+From github.com.outlook:ikoshiba/hello-git
+ edf13a1..ad50bf4 master -> origin/master
+koshiba@koshiba hello-git %
+```
+
+可通过 `git log origin/master` 或 `git reflog origin/master` 查看本地的远程分支提交历史来确认确实拉取了远程分支当前版本。
+
+```shell
+koshiba@koshiba hello-git % git reflog origin/master
+ad50bf4 (origin/master) refs/remotes/origin/master@{0}: fetch: fast-forward
+edf13a1 (HEAD -> master) refs/remotes/origin/master@{1}: fetch: fast-forward
+b0c4efa (remote) refs/remotes/origin/master@{2}: update by push
+de2bb32 refs/remotes/origin/master@{3}: fetch: fast-forward
+a343286 refs/remotes/origin/master@{4}: update by push
+eeeea6f refs/remotes/origin/master@{5}: pull: fast-forward
+2464feb refs/remotes/origin/master@{6}: update by push
+koshiba@koshiba hello-git %
+```
+
+可通过 `git branch -a` 查看本地和远程分支,或者通过 `git branch -r` 查看远程分支。
+
+
+
+### git merge
+
+`git fetch` 之后要通过 `git merge` 将远程分支合并到本地分支,相当于 `git merge origin/master` 。从回显中还能看到这是一次「快进」 (Fast-forward) 合并。
+
+```shell
+koshiba@koshiba hello-git % git merge
+Updating b0c4efa..edf13a1
+Fast-forward
+ hello.txt | 1 +
+ 1 file changed, 1 insertion(+)
+koshiba@koshiba hello-git %
+```
+
+
+
+### git pull
+
+将远程库当前版本拉取到本地并合并 (`git merge`) 。格式如下。
+
+```shell
+git pull <远程仓库别名> <远程分支名>:<本地分支名>
+```
+
+若远程分支名与本地分支名相同,可简写如下。
+
+```shell
+git pull <远程仓库别名> <分支名>
+```
+
+若已经配置过 upstream ,可简写如下。
+
+```shell
+git pull
+```
+
+
+
+若版本相同,则显示 `Already up to date.` 。
+
+```shell
+koshiba@koshiba hello-git % git pull
+Already up to date.
+koshiba@koshiba hello-git %
+```
+
+若有冲突,则显示如下。此时需要先解决冲突,接着 `git add`, `git commit` ,然后再 `git pull` 。
+
+```shell
+koshiba@koshiba hello-git % git pull
+error: Pulling is not possible because you have unmerged files.
+hint: Fix them up in the work tree, and then use 'git add/rm '
+hint: as appropriate to mark resolution and make a commit.
+fatal: Exiting because of an unresolved conflict.
+koshiba@koshiba hello-git %
+```
+
+若拉取后可正常合并,则显示如下。
+
+```shell
+koshiba@koshiba hello-git % git pull
+remote: Enumerating objects: 5, done.
+remote: Counting objects: 100% (5/5), done.
+remote: Compressing objects: 100% (2/2), done.
+remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
+Unpacking objects: 100% (3/3), 686 bytes | 171.00 KiB/s, done.
+From github.com.outlook:ikoshiba/hello-git
+ 2464feb..eeeea6f master -> origin/master
+Updating 2464feb..eeeea6f
+Fast-forward
+ hello.txt | 1 +
+ 1 file changed, 1 insertion(+)
+koshiba@koshiba hello-git %
+```
+
+
+
+使用 `git fetch` 是更好的实践 ([参考](https://www.oschina.net/translate/git-fetch-and-merge?print)) 。