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 <verb>
, git <verb> --help
, man git-<verb>
可查看相关命令的详细用法。
命令 | 描述 |
---|---|
■ 基本操作 | |
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/<your user name>/.gitconfig |
|
local | 当前仓库 .git 文件夹下的 .gitconfig |
|
Windows | system | C:\Program Files\Git\etc\gitconfig |
global | C:\Users\<your user name>\.gitconfig |
|
local | 当前仓库 .git 文件夹下的 .gitconfig |
|
Linux | ||
如下,可通过 cat
命令查看三个层级下的 git 配置内容。
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
查看。
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
命令要在仓库内执行,否则返回如下错误提示。
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。
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
参数单独设置,如下。
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
文件如下。
koshiba@koshiba ~ % cat ~/.gitignore_global
# MacOS .DS_Store
**/.DS_Store
koshiba@koshiba ~ %
**
表示所有层级目录
然后使用 git config --global core.excludesfile ~/.gitignore_global
命令配置到全局配置文件中。
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
。
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 <file>..." 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
禁止转义,以便显示中文。
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 <file>..." 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
参数的作用如下 (来自 官方文档 )。
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 用于版本管理的各种信息文件。
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
查看仓库文件状态。
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
信息。
koshiba@koshiba git-demo % git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." 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
即可将文件放入「暂存区」(标记为追踪文件)。
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 <file>..." to unstage)
new file: hello.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
.DS_Store
koshiba@koshiba git-demo %
git add .
会把本地所 有 untrack 的 (包括追踪过但又修改了的) 文件都加入暂存区。git add *
会忽略.gitignore
把任何文件都加入。执行时会出现如下提示,加上-f
参数后可强制加入,但显然通常我们不会这么做。
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 "<message>" <filename>
提交指定文件。
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 <file>..." 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 会检测到被修改的文件。
koshiba@koshiba git-demo % git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: hello.txt
Untracked files:
(use "git add <file>..." 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 %
再次添加并提交
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 <file>..." to unstage)
modified: hello.txt
Untracked files:
(use "git add <file>..." 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 "<message>"
将所有已跟踪的文件 暂存并提交 (即 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
新建后添加到了暂存区,且已提交,并在提交后做了修改,之后又提交到了暂存区。
koshiba@koshiba git-demo % git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: added.txt
modified: modifiedAdded.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: .DS_Store
modified: modified.txt
Untracked files:
(use "git add <file>..." 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
命令均无回显。
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
列出首行差异,因为此时工作目录比当前仓库版本多一行 (多了此文件,并且有一行差异) 。
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 之后。可看到如下。此时工作目录、暂存区和当前版本均无差异。
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
列出第二行差异,因为此时工作目录下该文件相比指定的仓库版本多了一行 。
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 %
「版本号」的不同写法。
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 <filename>
从已跟踪的文件清单中移除指定文件 (即从暂存区域移除) 并提交,同时删除该文件。如果只是 使用 rm
,虽然文件被删除,但 git status
后还是能看到已被删除的文件。
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 <file>..." to unstage)
modified: hello.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: .DS_Store
modified: hello.txt
koshiba@koshiba git-demo %
如上,在 git rm
删除指定文件前,若该文件有过修改,则拒绝删除,这样做的目的是防止删除未添加到仓库中的数据,这样的数据无法被 git 恢复。确定要删除时可以使用 -f
参数强制删除 (force) 。
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 <file>..." to unstage)
deleted: hello.txt
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: .DS_Store
koshiba@koshiba git-demo %
git mv¶
使用 git mv
命令修改文件名,例如 git mv a.txt b.txt
相当于如下三行命令。
mv a.txt b.txt
git rm a.txt
git add b.txt
git log¶
使用 git log
查看提交历史。
koshiba@koshiba git-demo % git log
commit b8d829123843fe1d760983ed5bfc9f2cbcc22d13 (HEAD -> master)
Author: ikoshiba <youremail@yama.com>
Date: Mon Sep 26 19:54:50 2022 +0800
modified one line
commit 5f3d03819d450b534153b90531393efd38e6a0a1
Author: ikoshiba <youremail@yama.com>
Date: Mon Sep 26 19:53:32 2022 +0800
first commit
使用 git log -p
查看每次提交的差异,使用 git log -p -2
查看最近 2 次提交的差异。
koshiba@koshiba git-demo % git log -p
commit b8d829123843fe1d760983ed5bfc9f2cbcc22d13 (HEAD -> master)
Author: ikoshiba <youremail@yama.com>
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 <youremail@yama.com>
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
类似。
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
。
如下先完成两次提交。
commit 6d70b38bd6becddcd2b70fb1a5bc3ee929070497 (HEAD -> master)
Author: koshiba <koshiba@163.com>
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 <koshiba@163.com>
Date: Wed Sep 28 20:11:37 2022 +0800
second commit
commit 6d70b38bd6becddcd2b70fb1a5bc3ee929070497
Author: koshiba <koshiba@163.com>
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 发生变化但提交时间和作者信息与之前是一致的。
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 <file>..." 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 <koshiba@163.com>
Date: Wed Sep 28 20:11:37 2022 +0800
second commit (amend)
commit 6d70b38bd6becddcd2b70fb1a5bc3ee929070497
Author: koshiba <koshiba@163.com>
Date: Wed Sep 28 20:11:01 2022 +0800
first commit
koshiba@koshiba hello-git %
每次提交时都可以使用 git commit --amend -m "<message>"
来取代上一次提交,这样无论有多少次提交, git log
都只显示最后一次提交。(但是 git reflog
仍会显示每一次提交) 。
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 <文件名>
将工作目录下的「已修改」文件恢复为未修改状态。
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 <file>..." 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 <file>..." to update what will be committed)
(use "git restore <file>..." 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 <filename>
将指定的已暂存的文件移出暂存区 (同 git restore --staged <filename>
), 若不指定文件,则取消所有文件的暂存内容 (清空暂存区)。其中,HEAD
表示当前版本,退回到上一个版本可用 HEAD^
。
git reset HEAD <filename>
与 git restore --staged <filename>
效果相同,但 git 的提示推荐使用 git restore <文件名>
用法。
如下展示 hello.txt
加入暂存区后,执行 git reset HEAD
取消暂存的过程。
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 <file>..." 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 <file>..." to update what will be committed)
(use "git restore <file>..." 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 -- <filename>
将工作目录下的「已修改」文件恢复为未修改状态,同 git restore <文件名>
,git 的提示推荐使用 git restore <文件名>
用法。
如下,接上一节的操作。
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 版本加入 git restore
命令后,推荐使用 git restore --staged <文件名>
来代替 git reset HEAD <filename>
,原因之一就是 git reset
承担的功能太多,有必要引入新的更直观的命令来取代一部分功能。
git reset --soft¶
git reset --soft <commit id>
将 HEAD 指向指定的版本 (commit) ,不改动 index 和 working tree。简单直观的理解即执行后,在版本历史中的第一条 (当前版本) 即为指定的那个版本,除此之外无其他变化。
如下演示 --soft
模式的效果。
首先,依次提交 1.txt , 2.txt 和 3.txt 之后git add
4.txt 但不提交,则当前有 3 个版本,暂存区中有四个文件 (通过 git ls-files
查看)。
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 <file>..." 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」。
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 <file>..." 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 <commit id>
命令,可以指定文件或不指定文件,效果不同。
- 不指定文件时。即包含了
--soft
实现的功能,即HEAD
指针指向指定的版本,又实现了「暂存区」的切换,即使得暂存区与指定版本一致。 - 指定文件时。
HEAD
指针不变,但指定的文件对应的暂存区内容将切换到指定版本的状态。
首先,提交 1.txt 作为第一个版本 (fc746eb) ,提交 2-1.txt 和 2-2.txt 作为第二个版本 (d3f6a97) ,提交 3.txt 作为第三个版本 (23a6441) ,将 4.txt 放入暂存区但不提交。
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 <file>..." to unstage)
new file: 4.txt
koshiba@koshiba hello-git %
可以看到暂存区中有五个文件,提交历史有三次,当前 HEAD 指向的是提交了 3.txt 的版本。
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」(在指定版本之后才创建的)。
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 <file>..." 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 不变。
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」。
koshiba@koshiba hello-git % git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: 2-1.txt
Untracked files:
(use "git add <file>..." 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 <commit id>
命令将 HEAD, index 和 working tree 都切换到指定版本号的状态
接着之前 git reset --mixed
的操作后继续演示,首先执行 git reset 23a6441
恢复到提交了 3.txt 之后的版本状态。
koshiba@koshiba hello-git % git reset 23a6441
koshiba@koshiba hello-git % git status
On branch master
Untracked files:
(use "git add <file>..." 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 所管控。
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 <file>..." 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 仍然存在,原因同前述。
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 <file>..." 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 <分支名>
创建分支。
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
查看本地仓库分支列表, *
表示当前分支。
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 <分支名>
切换分支。
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 <分支名>
切换分支。
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 合并到当前分支中。
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 最后一次提交是同一个。
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,则此时合并为「三方合并」。若产生冲突,则有如下提示。
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 分支各有一个提交版本。
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 ,执行变基,如下,有冲突,根据提示先解决冲突。
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 <conflicted_files>", 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
。
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 <basebranch> <topicbranch>
将 topicbranch 分叉后的修改在 basebranch 上重放,省去了 git checkout
切换到 basebranch 的操作。
变基的原则:只对尚未推送的本地修改执行变基,而不对本地仓库外有副本的分支执行变基。
远程操作¶
本地仓库与远程仓库之间建立关联的方式如下。
- 方式一(本地到远程):先建立本地仓库,然后将本地仓库推送到远程。
- 方式二(远程到本地):先建立远程仓库,然后在本地克隆远程仓库。
git remote¶
实现从本地到远程的方式一,首先要使用 git remote add <仓库别名> <url/ssh>
关联本地仓库与远程 github 仓库。根据通信协议的不同,分为 https 方式和 ssh 方式。
https方式¶
【1. 关联本地仓库与远程仓库】
执行如下命令完成关联,并检查关联结果。
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
即可。
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) ,设置方式在 这里 查看。
填入正确的 PAT 后即可成功推送,如下。
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 上创建目标关联远程仓库的话,推送将会失败,并显示失败原因是找不到指定的远程仓库。
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
查看远程库信息。
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 通信时的口令,通常不使用,回车即可 |
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@<host>
方式执行 ssh 连接时,<host>
填入的即 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 方式通信。
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
删除。
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.
。
koshiba@koshiba hello-git % git push -u origin master
ERROR: Repository not found.
fatal: Could not read from remote repository.
创建后再次推送成功,远程库中也出现了本地库推送的文件。
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
查看远程库信息。
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
。
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
。
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 <command> [<revision>...] -- [<file>...]'
koshiba@koshiba hello-git %
git clone¶
使用 git clone
命令将远程仓库克隆到当前本地目录下。可以看到该命令将远程仓库拉取到当前路径下 (即相当于下载了 hello-git) ,并且完成了 git init
管理已经下载到本地的 hello-git
(有 .git
隐藏文件夹),也完成了 git remote add
关联,并且远程仓库地址的别名也已自动设置为 origin
。
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 <远程仓库地址>/<远程分支>
来将「本地的远程分支」合并到本地当前分支。
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
。
koshiba@koshiba hello-git % git push
Everything up-to-date
若有更新,且正常推送,则显示如下。
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
) 。若本地分支与远程分支版本相同,则无回显,若不同,则如下。
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
查看本地的远程分支提交历史来确认确实拉取了远程分支当前版本。
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) 合并。
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
) 。格式如下。
git pull <远程仓库别名> <远程分支名>:<本地分支名>
若远程分支名与本地分支名相同,可简写如下。
git pull <远程仓库别名> <分支名>
若已经配置过 upstream ,可简写如下。
git pull
若版本相同,则显示 Already up to date.
。
koshiba@koshiba hello-git % git pull
Already up to date.
koshiba@koshiba hello-git %
若有冲突,则显示如下。此时需要先解决冲突,接着 git add
, git commit
,然后再 git pull
。
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 <file>'
hint: as appropriate to mark resolution and make a commit.
fatal: Exiting because of an unresolved conflict.
koshiba@koshiba hello-git %
若拉取后可正常合并,则显示如下。
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
是更好的实践 (参考) 。