Git know how 手册

404 阅读43分钟

本文主要是学习Git过程中的一些问答积累,并不涉及Git的背景,安装等介绍,如果需要这些了解这些相关知识,请访问官网:git-scm.com

由于内容较多,会陆陆续续补充完整。

如何将在本地文件夹变成git仓库

~ git init

这样就在你的当前目录创建了一个空的仓库,同时还在当前目录生成了一个叫.git的目录,里面有所有版本控制的相关信息。

如何获取远程的git库

~ git clone https://github.com/tensorflow/tensorflow.git

这样就在当前目录创建一个叫tensorflow的目录,并且将远程的master分支下的代码下载到tensorflow目录里面

如何获取远程某个分支的git库

~ git clone -b 0.6.0 https://github.com/tensorflow/tensorflow.git

这样就在当前目录创建一个叫tensorflow的目录,并且将远程的0.6.0分支下的代码下载到tensorflow目录里面

如何删除本地git仓库

~ rm -rf .git

在当前git仓库目录里,删除.git文件即可

如何往本地git仓库添加文件到暂存区

~ echo 'Hello' > hello.md  # 假如说新建了一个hello.md文件到工作区
~ git add hello.md # 通过git add 文件名1 文件名2... 将该文件(多个文件可以空格间隔)先加入到暂存区

如何往本地git仓库添加所有文件到暂存区

~ echo 'Hello' > hello.md  # 假如说新建了一个hello.md文件到工作区
~ echo 'World' > world.md  # 假如说新建了一个world.md文件到工作区
~ echo 'Git' > git.md  # 假如说新建了一个hello.md文件到工作区
~ git add . # 通过git add . 可将该上述所以文件都加入到暂存区

如何从本地git仓库的暂存区删除不想后续提交到git仓库的文件

~ git rm --cached hello.md # 通过git rm --cached 文件名1 文件名2... 可以进行多个暂存区文件的删除

如何将本地git仓库的暂存区的文件提交到对象库

# 方式一: 可以将暂存区的消息提交到对象库,这也就是我们通常意义提到的入库操作
~ git commit -m '测试提交' # 通过git commit -m '此次提交消息说明' 

# 方式二: 可以将已经修改的文件但还没通过git add命令到暂存区,以及暂存区的文件都一并提交到对象库
# 注意: 该命令对新增的文件无效, 还得通过git add命令将其添加到暂存区
~ git commit -am '测试提交' # 通过git commit -am '此次提交消息说明' 

如何修改上一次提交到对象库时的提交信息修改

~ git commit --amend -m '正确提交' # 通过git commit --amend -m '最新提交说明' 可以在不增加git提交log的基础上,只是将最后一次提交的日志信息进行修改

如何将查看git仓库的提交历史

~ git log # 不带参数的git log
Author: Yuhao Bi <byh0831@gmail.com>
Date:   Fri Sep 20 10:36:02 2019 +0800

    Stabilize EtcdMetadataReportTest (#5088)

commit 87631aa992448e09cad350b9141afbae1cca09ef  #摘要信息默认使用SHA1算法


~ git log --graph --pretty=oneline --abbrev-commit # 带参数的简洁一行显示

* 87631aa99 [Dubbo-5049]support annotation config id property to be a alias for a config bean. Fixes #5049 (#5063)
* 46486bb34 [Dubbo-4331] Improve api doc (#5070)
* a3c89e2e4 delete 'config.' prefix for url generated from ConfigCenterConfig (#5078)
* 5842dff89 fix set generic method error (#5079)
* b53624e4e [Dubbo-4882] Add support for overriding Map properties in AbstractConfig.refresh (#4899)
* 87c78878c try to fix travis javax.ex dependency issue
* d1ea5e0c8 try to fix travis javax.ex dependency issue
* 3222ea2f9 try to fix travis javax.ex dependency issue

~ git log --graph --topo-order --decorate --oneline --all # 带参数的拓扑显示
| | | * 0bd3a97bd unit test for metadata
| | | *   20c2b1d79 Merge remote-tracking branch 'origin/cloud-native' into cloud-native
| | | |\
| | | | *   f4b4ce719 unit test
| | | | |\
| | | | * | ef4415e82 unit test
| | | | * |   95d85e514 Merge branch 'cloud-native' of github.com:apache/incubator-dubbo into cloud-native
| | | | |\ \
| | | | * \ \   3cc498191 merge
| | | | |\ \ \
| | | | * \ \ \   e41963b96 merge
| | | | |\ \ \ \
| | | | * \ \ \ \   4a21c6bb0 Merge branch 'cloud-native' of github.com:apache/incubator-dubbo into cloud-native
| | | | |\ \ \ \ \
| | | | * \ \ \ \ \   1fa487e27 Merge branch 'cloud-native' of github.com:apache/incubator-dubbo into cloud-native
| | | | |\ \ \ \ \ \
| | | | * | | | | | | 7725906c7 nacos
| | | * | | | | | | | b2d5e64ca change pom version to 2.7.5-cloudnative-SNAPSHOT
| | | | |_|_|_|_|_|/
| | | |/| | | | | |
| | * | | | | | | | 50f8a5127 grpc protocol

如何将本地git仓库的对象库的文件删除

# 方式一:直接使用git命令
~ git rm hello.md #  通过git rm 文件名1 文件名2... 可以进行多个对象库文件的删除

# 方式二:使用bash+git命令
~ rm hello.md # 通过bash命令将文件删除,此时工作区发生了变化
~ git add hello.md # 将删除的文件,从工作区添加到暂存区

如何查看当前工作区和对象库头指针的内容变化

~ git status # 如果当前有未提交到暂存区的文件(.gitignore未忽略)、已经提交暂存区但还没提交到对象库的文件列表则会通过该命令显示出来

如何将本地git仓库的删除的对象库的文件重新恢复

# 这个操作一共分两个步骤
# 步骤一: 从暂存区还原到工作区
~ git reset HEAD hello.md # 使用git reset HEAD 文件名1 文件名2... (或者.表示所有文件) 来还原的前提是只是运行了git rm,但还未提交的文件
Unstaged changes after reset: 
D	hello.md # 此时会发现删除的hello.md已经恢复在了工作区,还没进入暂存区

# 步骤二: 从工作区还原修改过的文件
~ git checkout -- hello.md # 使用git checkout -- 文件名1 文件名2... (或者.表示所有文件) 将工作区修改的文件进行回退

如何重命名本地git仓库的对象库文件

# 方式一:直接使用git命令
~ git mv hello.md hello1.md # 把 hello.md 重命名为 hello1.md

# 方式二:使用bash+git命令
~ mv hello.md hello1.md # 通过bash命令将hello.md 重命名为 hello1.md
~ git add . # 将删除的文件hello.md,新加入的文件hello1.md从工作区添加到暂存区

如何忽略本地工作区的一些文件和目录追踪和提交

# 创建.gitignore文件
~ touch .gitignore 
# 编辑.gitignore文件,每一行添加一个忽略规则
dummy.txt # 忽略根目录下的dummy.txt文件
aa/a.txt  # 忽略从根目录到aa目录下的a.txt文件
bb/ # 忽略根目录下的bb目录
cc/*.txt # 忽略cc目录下所有以txt结尾的文件
dd/*/*.csv # 忽略dd目录下面的二级目录所有以txt结尾的文件
ee/**/*.csv # 忽略ee目录下面及所有字母下面所有以txt结尾的文件

如何重命名本地git仓库的对象库文件

# 方式一:直接使用git命令
~ git mv hello.md hello1.md # 把 hello.md 重命名为 hello1.md

# 方式二:使用bash+git命令
~ mv hello.md hello1.md # 通过bash命令将hello.md 重命名为 hello1.md
~ git add . # 将删除的文件hello.md,新加入的文件hello1.md从工作区添加到暂存区

如何在本地git仓库创建一个新的分支

# 方式一
~ git branch dev # 创建新分支dev
~ git checkout dev # 进入新分支dev

# 方式二
~ git checkout -b prod # 创建新分支prod并且直接checkout出来

如何在本地git仓库的分支直接切换

# 方式一
~ git branch master # git branch 分支名称: 从当前分支切换到master分支

# 方式二
~ git checkout - # 从当前分支和上一次的分支直接交替切换

如何查看本地git仓库的分支情况

# 方式一
~ git branch # 运行后显示本地仓库所以分支的列表,其中*号标记的是当前活动分支
  dev
  master
* prod
(END)

# 方式二
~ git branch -r # 运行后显示所以远程分支
  origin/master
(END)

# 方式二
~ git branch -a # 运行后显示所以分支(本地+远程),其中*号标记的是当前活动分支
  dev
* master
  prod
  remotes/origin/master
(END)

如何本地git仓库的分支合并

# 假设在dev分支开发完了,加入了新文件User.txt,准备合并到master发布系统,则可以进行下面的操作
~ git checkout master # 因为是需要合并到master分支,所以要确保当前的工作分支是master。
~ git merge dev # git merge 被合并的分支: 通过该命令,就能将dev新的改动合并到master
分支上面了

# git合并时,默认会使用fast-forward快进方式进行合并,在这种模式下,删除分支时会修掉分支信息,如果想保留分支信息,合并时可以增加参数--no-ff来禁用ff合并方式,这样就会增加一个commit id的信息
~ git merge dev --no-ff # 运行后会打开vi,要求输入提交信息,生成一个新的commit id信息了

如何制造本地git仓库的合并冲突

# 假设在master分支上对文件hello.md增加了一行,并且完成提交;然后切换到dev分支,对文件hello.md也增加一行,并且完成提交。现在要把dev分支的修改合并到master上面。
~ git checkout master # 先确保当前的工作分支是master。
~ echo 'World' >> hello.md # 给hello.md增加一行
~ git add . && git commit -m 'master分支修改' #将修改添加到暂存区后直接提交到对象库
~ git checkout dev # 切换当前的工作分支是dev。
~ echo 'Bejing' >> hello.md # 给hello.md增加一行
~ git add . && git commit -m 'dev分支修改' #将修改添加到暂存区后直接提交到对象库
分支上面了
~ git checkout - # 切换回master,准备进行合并
~ git merge dev # 尝试将dev合并回master,发现自动合并失败,接下来需要手工解决冲突
Auto-merging hello1.md
CONFLICT (content): Merge conflict in hello1.md
Automatic merge failed; fix conflicts and then commit the result.

如何解决本地git仓库的合并冲突

# 合并失败后,再次打开有冲突的文件,会发现里面有需要解决冲突的标记。
~ cat hello.md # 查看冲突情况
Hello
<<<<<<< HEAD # HEAD是执行当前分支的指针,在这里当前分支就是master
world
======= # 冲突分割线,上面是HEAD执行分支的修改,下面是被合并的分支的修改
Beijing
>>>>>>> dev # 指向被合并的分支的指针,在这里就是dev
~ vim hello.md # 这里我最终修改为
Hello
world
Beijing
# 将冲突解决后,重新用git add和git commit命令提交
~ git add hello.md && git commit -m '解决冲突' 
~ git checkout - # 切换到dev分支
~ git merge master # 将解决完冲突的master合并到dev分支,到此为止,HEAD,master,dev三个指针都指向了最新版本

如何回退本地git仓库的分支到之前版本

# 使用^方式进行回退
~ git reset --hard HEAD^ # 回退当前HEAD执行的分支的上一个版本
~ git reset --hard HEAD^^ # 回退当前HEAD执行的分支的上两个版本,多个版本以此类推
# 使用~n来进行回退
~ git reset --hard HEAD~3 # 回调当前HEAD执行的分支上的三个版本
# 使用摘要的commit id进行前进或者回退
~ git reset --hard 7795ce9 # 直接让HEAD指向摘要是7795ce9的提交版本(可以是回退,也可以是前进,实现可以用git log把摘要信息commit id都记录下来)

忘记了git log信息,如何前进本地git仓库的分支到更新的版本上

# 比如git reset --head~10 直接回退了10个版本,发现回退多了,但是针对的HEAD指针只能回退,不能前进;而且各个提交的摘要信息commit id事先没有保存,也没有办法直接前进,这个时候可以使用下面命令
~ git reflog # 得到git的操作日志,就能找回之前的提交的commit id
了。
~ git reset --hard 7795ce9 # 在使用git reset命令将HEAD指向想要前进的对应提交版本

如何把当前分支的修改临时保存,然后切换去别的分支

# 比如当前的分支是master,修改到一半的时候,dev有一个紧急bug需要修复,之间切换过去要么丢失当前的修改,要么得提交到master,都不是很好的做法,因此可以考虑使用git stash来暂存
~ echo 'bar' >> foo.txt # 修改foo.txt文件
~ git stash #将刚刚的修改暂存 可以使用 git stash save '临时保存' 增加一个消息说明

如何查看当前分支下的临时保存列表

# 可以使用如下命令进行操作
~ git stash list # 下面会显示当前分支下所有暂存的列表信息
stash@{0}: WIP on master: 9f9943c 解决冲突
stash@{1}: On master: 临时保存
(END)

如何恢复当前分支下的临时保存

# 方式一: 恢复最后一次临时保存的信息并且从列表删除
~ git stash pop 

# 方式一: 恢复指定临时保存区索引序号的信息,但是该命令只是读取并不会删除,如需删除,需要手工删除
~ git stash apply stash@{0} # stash@{0} 表示是列表中索引第0个位置的临时信息
~ git stash drop stash@{0} # 手动删除列表中索引第0个位置的临时信息
(END)

如何给当前分支创建标签

# 方式一: 快速创建的tag不会生成tag对象,会直接指向当前提交点的SHA-1值。
~ git tag v1.0 # git tag 版本号: 根据当前分支创建一个标签

# 方式二: 带参数的创建,生成的tag会创建出tag对象。其中对象里面包含了指向当前提交点的SHA-1值。
~ git tag -a v1.1 -m '发布v1.1' # 通过增加 -a 标签,-m 标签信息来创建
(END)

如何查看创建的标签列表

# 方式一: 显示所有标签
~ git tag # 或者 git tag --l 或者 git tag --list
v1.0
v1.1
(END)

# 方式二: 根据条件搜索标签
~ git tag --list '*1.0' # 查找后缀是1.0的所有标签
v1.0
(END)

如何查看某个标签

# git show [tag]
~ git show v1.1

tag v1.1
Tagger: hubo <hubo@qq.com>
Date:   Sun Mar 22 13:28:39 2020 +0800

发布1.1

commit 9f9943c73a0e116c2f1938e489e4b7b3a8809ded (tag: v1.1)
Merge: 5e2707d e2ac997
Author: hubo <hubo@qq.com>
Date:   Mon Mar 16 15:57:24 2020 +0800

    解决冲突

diff --cc hello1.md
index 65a56c3,2c6e471..fa3e5bb
--- a/hello1.md
+++ b/hello1.md
@@@ -1,2 -1,2 +1,3 @@@
  Hello
 +world
+ Beijing

如何查看删除标签

~ git tag -d v1.0 # 通过参数 -d 删除标签
Deleted tag 'v1.0' (was 9f9943c)

如何查看文件的修改

~ git blame hello.txt # git blame 文件名: 一次只能查看一个文件的修改信息
eaba830 hello.md  (*** 2020-03-16 12:11:45 +0800 1) Hello
5e2707d3 hello1.md (***  2020-03-16 15:39:17 +0800 2) world
e2ac997f hello1.md (***  2020-03-16 15:39:46 +0800 3) Beijing

如何查看工作区和暂存区之间的差别

~ echo 'new line' >> foo.txt # 给文件增加一行,相当于修改了工作区用来测试
~ git diff # 运行git diff查看区别

diff --git a/foo.txt b/foo.txt # 文件同名所以加了前缀 a/ 和 /b
index 257cc56..fe08ec6 100644
--- a/foo.txt # --- 表示的是当前暂存区的版本
+++ b/foo.txt # +++ 表示的是当前工作区的版本
@@ -1 +1,2 @@ # -1 表示暂存区从第1行开始算,只取1行 +1,2 表示工作区从1行开始算,连续2行
 foo # 工作区和缓冲区都有这个信息,没有区别
+new line # 工作区新增加的信息
(END)

如何查看工作区和某个对象库(提交)之间的差别

~ echo 'new line' >> foo.txt # 给文件增加一行,相当于修改了工作区用来测试
~ git diff HEAD # 运行git diff commit_id: 这里的commit_id =HEAD 查看工作区和HEAD指针指向的对象库的区别
## 内容和上面一样
diff --git a/foo.txt b/foo.txt # 文件同名所以加了前缀 a/ 和 /b
index 257cc56..fe08ec6 100644
--- a/foo.txt # --- 表示的是当前对象库的版本
+++ b/foo.txt # +++ 表示的是当前工作区的版本
@@ -1 +1,2 @@ # -1 表示对象库从第1行开始算,只取1行 +1,2 表示工作区从1行开始算,连续2行
 foo # 工作区和缓冲区都有这个信息,没有区别
+new line # 工作区新增加的信息
(END)

如何查看暂存区和某个对象库(提交)之间的差别

~ echo 'new line' >> foo.txt # 给文件增加一行,相当于修改了工作区用来测试
~ git add foo.txt # 将工作区的修改添加到暂存区
~ git diff --cached HEAD # 通过增加参数--cached,将暂存区和HEAD指针指向的对象库的区别
## 内容和上面一样
diff --git a/foo.txt b/foo.txt # 文件同名所以加了前缀 a/ 和 /b
index 257cc56..fe08ec6 100644
--- a/foo.txt # --- 表示的是当前对象库的版本
+++ b/foo.txt # +++ 表示的是当前暂存区的版本
@@ -1 +1,2 @@ # -1 表示对象库从第1行开始算,只取1行 +1,2 表示暂存区从1行开始算,连续2行
 foo # 工作区和缓冲区都有这个信息,没有区别
+new line # 暂存区新增加的信息
(END)

如何使用cherry-pick

# cherry-pick主要作用是将某个分支的提交应用到另外一个分支上。
# 假设有2个分支,maste和develop,在develop开发了几个修改,不使用merge操作,使用cherry-pick将修改应用到master
~ git checkout -b dev
Switched to a new branch 'dev'
# 增加测试修改
~ echo 2 >> 'hello.txt'
~ git add . && git commit -m "2st"
[dev bd17b50] 2st
 1 file changed, 1 insertion(+)
 # 增加测试修改
~ echo 3 >> 'hello.txt'
~ git add . && git commit -m "3st"
[dev f9d1e2f] 3st
 1 file changed, 1 insertion(+)
# 查看dev分支提交日志
~ git log --format="%h"
f9d1e2f # 添加3的提交
bd17b50 # 添加2的提交
dc3102e
(END)
# 切换会master分支
~ git checkout master
# 先对添加2的提交进行cherry-pick
~ git cherry-pick bd17b50
[master ec3a12b] 2st
 Date: Mon Jun 1 22:34:40 2020 +0800
 1 file changed, 1 insertion(+)
# 在对添加3的提交进行cherry-pick
~ git:(master) git cherry-pick f9d1e2f
[master 41831be] 3st
 Date: Mon Jun 1 22:34:48 2020 +0800
 1 file changed, 1 insertion(+)
# 最后和dev分支进行对比,发现是一致的
~ git diff dev 
# 注意:如果刚刚进行cherry-pick操作时,先对添加3的提交进行check-pick操作时,会出现冲突,这个时候手工解决再提交即可。

如何使用rebase

# rebase目标是变基、衍合,即改变分支的根基。
# rebase 和 merge 区别
# rebase:修改提交的历史,将执行rebase的分支变基到目标的分支上,从而使得整个git log串成了一条线。如果历史修改记录很重要,就不要进行rebase操作。rebase实际上是在源分支的提交,生成一个个的patch,来应用到目标分支上
# merge:不修改提交的历史。
# 原则不要对master进行rebase,否则会因为不断的解决冲突,而导致很多操作,非常麻烦。一般rebase都是对自己的本地分支,并且该本地库还没有推送到远程仓库
# 假设有2个分支,maste和develop,在master开发了几个修改,不使用merge操作,使用rebase将修改应用到develop上
~ git checkout master
Switched to branch 'master'
# 增加测试修改
~ echo 2 >> 'hello.txt'
~ git add . && git commit -m "2st"
[master bd17b50] 2st
 1 file changed, 1 insertion(+)
 # 增加测试修改
~ echo 3 >> 'hello.txt'
~ git add . && git commit -m "3st"
[master f9d1e2f] 3st
 1 file changed, 1 insertion(+)
# 在dev分钟进行rebase操作
# git rebase [以此分支进行rebase]
~ git checkout dev 
~ git rebase master
Successfully rebased and updated refs/heads/dev.

# 当rebase发生冲突时,可以按照下面的命令进行解决:
# 首先rebase操作会进入源所在分支的头部,然后依次开始运行目标的提交。
# 当发现冲突后,会中断停止,手工解决冲突后通过git add解决后,继续运行下面命令,继续rebase
~ git rebase --continue 
# 如果想跳过目标的冲突的某个patch应用,以源的代码为主,可以用下面的命令跳过冲突
~ git rebase --skip
# 如果不想解决冲突了,可以通过下面的操作,恢复到rebase开始的状态:
~ git rebase --abort 

如何删除本地git仓库的分支

~ git branch -d dev # git branch -d 要删除的分支名称: 在非dev分支的情况下,删除dev分支,否则会报错
# 如果dev分支比当前分支要新,并且还未将这些新提交的合并到当前正在操作的分区下,-d 参数是无法删除的,因而需要使用下面的参数
~ git branch -D dev # git branch -D 要强制删除的分支名称: 通过该命令会将dev分支强制删除,并且那些新添加或者修改的文件也都一并被清理掉

在哪里可以设置提交的user.name和user.email

1. /etc/gitconfig: 对使用该操作系统的所有用户都有效。使用 git config --system 来设置
2. ~/.gitconfig: 只对当前登录系统的用户账号有效。使用 git config --global 来设置
3. 当前git仓库的.git/config: 只对当前git仓库有效。使用 git config --local 来设置

配置查找的优先级顺序是 3 > 2 > 1。

如何查看设置提交的user.name和user.email

# 查看系统文件/etc/gitconfig的设置
~ git config --system -l 
# 查看个人目录下~/.gitconfig的设置
~ git config --global -l
# 查看当前git仓库的.git/config设置
~ git config --local -l

如何通过命令设置提交的user.name和user.email

# 这里以当前git仓库的设置信息为例
~ git config --local user.name superleo # 设置用户名
~ git config --local user.email superleo@gmail.com # 设置邮箱
~ git config --local -l # 使用命令查看是否添加成功
~ cat .git/config # 也可以用Bash命令查看刚刚添加完成的信息

如何通过命令重新设置提交的user.name和user.email

# 这里以当前git仓库的设置信息为例
~ git config --local user.name newname # 重新设置用户名
~ git config --local user.email newname@gmail.com # 重新设置邮箱
~ git commit --amend --reset-author # 使用改命令重新修改git作者信息,此时进入vim编辑页面,保存操作即可
~ git log # 查看是否作者信息已经修改成功
commit 472ff172946a269ce48c3d090ca6deecb14d06fa (HEAD -> master)
Merge: 5e2707d e2ac997
Author: newname <newname@gmail.com> # 可以看到已经修改成功了
Date:   Sat Apr 4 22:48:01 2020 +0800

如何添加和删除git的配置信息

# 这里以当前git仓库的设置信息为例
~ git config --local user.temp tempvalue # 在user节点下面创建一个key=temp, value=tempvalue的键值对
~ git config --local -l # 查看是否添加成功
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
core.precomposeunicode=true
user.name=newname
user.email=newname@gmail.com
user.temp=tempvalue # 添加成功
(END)

~ cat .git/config # 或者使用cat命令查看.git/config文件是否有这个值
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
	ignorecase = true
	precomposeunicode = true
[user]
	name = newname
	email = newname@gmail.com
	temp = tempvalue
	
~ git config --local --unset user.temp # 使用unset从local删除这个属性
~ git config --local -l # 查看是否删除成功
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
core.precomposeunicode=true
user.name=newname
user.email=newname@gmail.com
(END)

如何通过命令设置别名

# 设置方式 git config --[local|global|system] alias.别名 要替换的git命令
# 对于非git命令,则需要以!开头,表示该别名从外部命令调用。
# 这里以当前git仓库的设置信息为例
~ git config --local alias.br branch # 给git branch添加别名git br
~ git config --local alias.rv 'reset HEAD' # 给git reset HEAD添加别名git rv
~ git config --local alias.ui '!gitk' # 设置git ui调用外部的命令gitk
# 设置后,可以分别通过下面的操作来执行
~ git br # 显示当前的分支情况
~ git rv hello1.md # 如果修改了本地hello1.md文件,还未add到暂存区,可以通过git rv进行回退操作
~ git ui # 调用gitk的UI图形界面
# 查看一下git的配置文件
~ cat .git/config
... 省略其他信息
# 可以看到多了一个alias的组,里面有刚刚添加的两个别名。
# 如果是 --global设置,可以查看 cat ~/.gitconfig 
[alias] 
	br = branch
	rv = reset HEAD
	ui = !gitk

如何将本地仓库推送到远程新建仓库

# 首先确保远程的仓库已经创建好,并且你有响应的账号和密码
# 这里以当前git仓库的设置信息为例,假设当前的分支是master分支
~ git config --local user.name hubo # 设置远程的用户名
~ git config --local user.email hubo@qq.com # 设置远程的邮箱
~ git commit --amend --reset-author # 使用改命令重新修改git作者信息,此时进入vim编辑页面,保存操作即可
# git remote add [别名] [URL地址],这里的origin是约定的名称,也可以改成别的,它指代了后面的URL地址
~ git remote add origin http://[你远程仓库的url]/hubo/demo1.git
# 将本地的master推送到远程origin执行地址的master分支上,其中-u表示将本地的master和远程的master做一次关联,以后再次提交的时候,就不用在写完整的 git push origin master 了
~ git push -u origin master
# 运行后会有类似下面的信息输出
Delta compression using up to 12 threads
Compressing objects: 100% (14/14), done.
Writing objects: 100% (21/21), 1.67 KiB | 1.67 MiB/s, done.
Total 21 (delta 3), reused 0 (delta 0)
# 这里会有用户名和密码的输入提示,成功后,会显示下面的信息
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

# 未来只需要再运行 git push 就能直接往远程推送本地对象库已经提交的信息了
~ vim foo.txt # 编辑个本地文件
~ git commit -am "add new line" # 提交到对象库
~ git push # 推送到远程
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 258 bytes | 258.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To http://[你远程仓库的url]/hubo/demo1.git
   67866bb..e851e80  master -> master

如何获取远程仓库的修改

# 方法一: 等于方法二的 git fetch + git merge
~ git pull # 或者完整命令 git pull origin master 获取拉取远程的更新,并且尝试进行自动合并
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From http://[你远程仓库的url]/hubo/demo1
 * branch            master     -> FETCH_HEAD
   e851e80..5d16000  master     -> origin/master
Updating e851e80..5d16000
Fast-forward
 foo.txt | 1 +
 1 file changed, 1 insertion(+)
 
# 方法二: 分成2个步骤 git fetch 和 git merge
~ git fetch # 或者完整命令 git fetch origin master 获取拉取远程的更新
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From http://[你远程仓库的url]/hubo/demo1
   5d16000..0bbca28  master     -> origin/master
   
~ git merge # 将拉取到本地的远程更新和本地对象库进行合并
Updating 5d16000..0bbca28
Fast-forward
 foo.txt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

如何查看远程分支和详情

~ git remote show origin # 查看远程仓库某个仓库的详情
* remote origin
  Fetch URL: http://[你远程仓库的url]/hubo/demo1.git #远程拉取到本地地址
  Push  URL: http://[你远程仓库的url]/hubo/demo1.git #本地推送到远程的地址
  HEAD branch: master # HEAD指向的branch是本地的master
  Remote branch:
    master tracked #远程的一个叫master的branch已经被追踪了
  Local branch configured for 'git pull': # 执行git pull命令时
    master merges with remote master # 本地的master会被远程的master进行合并操作
  Local ref configured for 'git push': # 执行git push命令时
    master pushes to master (up to date) #本地的master会被推送到远程的master

如何把本地一个新分支提交到远程

# 假设当前处于master分支上
~ git checkout -b dev # 本地基于master创建一个dev分支,此时master和dev是一致的
~ git branch -av # 查看一下本地和远程的分支情况,确定远程没有dev分支
* dev                   0bbca28 Update 'foo.txt' # 刚创建的dev分支
  master                0bbca28 Update 'foo.txt' # 之前已有的master分支
  remotes/origin/master 0bbca28 Update 'foo.txt' # 远程的master分支
(END)

### 方式一 git push -u origin source:destination
~ git push -u origin dev # 由于本地和远程的分支名称都叫dev,因此可以使用该命令把dev推送到远程,同时建立联系
Total 0 (delta 0), reused 0 (delta 0)
To http://[你远程仓库的url]/hubo/demo1.git
 * [new branch]      dev -> dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

### 方式二 git --set-upstream origin source:destination
~ git push --set-upstream origin dev # 由于本地和远程的分支名称都叫dev,因此可以使用该命令把dev推送到远程,同时建立联系
Total 0 (delta 0), reused 0 (delta 0)
To http://[你远程仓库的url]/hubo/demo1.git
 * [new branch]      dev -> dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

~ git branch -av # 再次查看一下本地和远程的分支情况
* dev                   0bbca28 Update 'foo.txt' # 刚创建的dev分支
  master                0bbca28 Update 'foo.txt' # 之前已有的master分支
  remotes/origin/master 0bbca28 Update 'foo.txt' # 远程的master分支
  remotes/origin/dev    0bbca28 Update 'foo.txt' # 此时已经有了远程的dev分支
(END)

如何从远程拉取一个新创建的分支并且进行追踪

# 假设同事在当前项目创建了一个叫prod分支,并且push到了远程仓库
~ git pull # 先把远程的仓库拉取到本地
From http://[你远程仓库的url]/hubo/demo1
 * [new branch]      prod       -> origin/prod
Already up to date.

### 方式一 git checkout -b [本地分支名称] [远程别名/远程分支名]
~ git checkout -b prod origin/prod
Branch 'prod' set up to track remote branch 'prod' from 'origin'.
Switched to a new branch 'prod'

### 方式二 git checkout --track [远程别名/远程分支名]
~ git checkout --track origin/prod # 本地的分支名和远程分支名都叫prod
Branch 'prod' set up to track remote branch 'prod' from 'origin'.
Switched to a new branch 'prod'

~ git branch -av # 再次查看一下本地和远程的分支情况
  dev                   0bbca28 Update 'foo.txt'
  master                0bbca28 Update 'foo.txt'
* prod                  0bbca28 Update 'foo.txt'
  remotes/origin/dev    0bbca28 Update 'foo.txt'
  remotes/origin/master 0bbca28 Update 'foo.txt'
  remotes/origin/prod   0bbca28 Update 'foo.txt'
(END)

如何删除远程的一个分支

# 假设现在要删除远程的prod分支
~ git push 
Already up to date.

### 方式一 git push [远程别名] [本地分支名:远程分支名]
~ git push origin :prod # 把本地分支一个"空"分支提到远程的prod分支,这就相当于执行了删除
To http://[你远程仓库的url]/hubo/demo1.git
 - [deleted]         prod

### 方式二 git push [远程别名] --delete [远程分支名称]
~ git push origin --delete prod # 通过--delete参数删除远程分支
To http://[你远程仓库的url]/hubo/demo1.git
 - [deleted]         prod

~ git branch -av # 再次查看一下本地和远程的分支情况, 发现远程分支已经没有了
  dev                   0bbca28 Update 'foo.txt'
  master                0bbca28 Update 'foo.txt'
* prod                  0bbca28 [gone] Update 'foo.txt'
  remotes/origin/dev    0bbca28 Update 'foo.txt'
  remotes/origin/master 0bbca28 Update 'foo.txt'
(END)

# 如果还想删除本地的prod分支,切换到别的分支,在用git branch -d 命令进行操作
~ git checkout master # 切换到别的分支
~ git branch -d prod # 删除prod分支
Deleted branch prod (was 0bbca28).

删除远程分支后如何解决本地分支stale的情况

# git删除远程分支后,本地所对应的分支会变的陈旧
### 加上执行了删除远程dev的分支 git remote origin --delete dev,
### 后面通过git remote show origin 会发现本地的dev变成了stale的警告
## 可以通过下面的命令进行进行裁剪
~ git remote prune # 剪裁命令

如何追踪一个本地和远程分支名称不一样的情况

# 假设要将本地的prod推送到远程,但是远程的分支名称叫prod_remote
~ git push --set-upstream origin prod:prod_remote
Total 0 (delta 0), reused 0 (delta 0)
To http://[你远程仓库的url]/hubo/demo1.git
 * [new branch]      prod -> prod_remote
Branch 'prod' set up to track remote branch 'prod_remote' from 'origin'.

# 此次直接使用 git push 或者 git pull 命令是不行的。需要使用完整的命令才行:
~ git push origin HEAD:prod_remote # 将当前HEAD指向的分支(master)推送到远程的prod_remote分支
# 或者
~ git push origin prod:prod_remote # 将prod分支推送到远程的prod_remote分支

如何处理远程一个和本地分支名称不一样的情况

# 假设远程有一个分支名称叫mymaster,先通过下面的命令进行拉取
# 这样就把远程的一个叫mymaster的分支和本地的远程建立了联系
~ git fetch orgin master:refs/remotes/origin/mymaster
# 然后在运行git checkout --track [远程地址]/[远程分支名称]建立跟踪
~ git checkout --track origin/mymaster

如何把本地的标签推送到远程

~ git tag --list # 查看本地tag情况, 假设本地创建了3个tag
v1
v1.1
v1.2
(END)

### 注意: 默认情况下git push并不会把本地打好的标签推送到远程
### 方式一 git push [tag1][tag2]... 
~ git push origin v1
Total 0 (delta 0), reused 0 (delta 0)
To http://[你远程仓库的url]/hubo/demo1.git
 * [new tag]         v1 -> v1
 
### 方式二 git push --tags 把本地尚未推送到远程的所有tag,都推送到远程
~ git push origin --tags
Enumerating objects: 8, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 12 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 430 bytes | 430.00 KiB/s, done.
Total 4 (delta 1), reused 0 (delta 0)
To http://[你远程仓库的url]/hubo/demo1.git
 * [new tag]         v1.1 -> v1.1
 * [new tag]         v1.2 -> v1.2

如何把获取远程的标签

### 方式一 git fetch origin tag [tag] 获取获取远程指定的标签
~ git fetch origin tag v1
From http://[你远程仓库的url]/hubo/demo1
 * [new tag]         v1         -> v1
 * [new tag]         v1.2       -> v1.2

### 方式二 git fetch origin --tags 或者 git pull 获取获取远程所以标签
~ git fetch origin --tag
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 4 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (4/4), done.
From http://[你远程仓库的url]/hubo/demo1
 * [new branch]      prod_remote -> origin/prod_remote
 * [new tag]         v1.1        -> v1.1

如何删除远程的标签

### 方式一 git push origin :refs/tags/[tag] 把本地一个空的tag推送到远程
~ git push origin :refs/tags/v1
To http://[你远程仓库的url]/hubo/demo1.git
 - [deleted]         v1

### 方式二 git push origin tag --delete [tag] 使用--delete来删除
~ git push origin tag --delete v1.1
To http://[你远程仓库的url]/hubo/demo1.git
 - [deleted]         v1.1

如何读取HEAD文件

### 注意: 手工修改HEAD文件,是不会记录到reflog中去的
### 方式一 cat .git/HEAD
~ cat .git/HEAD
ref: refs/heads/prod

### 方式二 git symbolic-ref HEAD
~ git symbolic-ref HEAD
refs/heads/prod

如何读取远程分支的ORIG_HEAD文件

### 远程HEAD所在的提交点,Git会将HEAD原来所指向commit对象的sha-1值存放于ORIG_HEAD文件中。也就是说ORIG_HEAD可以让我们找到进行最近一次危险操作之前的HEAD位置
~ cat .git/ORIG_HEAD
0bbca286b716ba69738d4faa2aa55f6ccb1d113a

如何查看远程分支的日志

### 方式一: git log [远程仓库名称]/[远程分支名称]
~ git log origin/master
commit 0bbca286b716ba69738d4faa2aa55f6ccb1d113a (HEAD -> prod, tag: v1.2, tag: v1, origin/prod_remote, origin/prod, origin/master, origin/dev, origin/HEAD, master)
Author: hubo <hubo@qq.com>
Date:   Sat Apr 4 23:21:04 2020 +0800

    Update 'foo.txt'

commit 5d16000c30f8c9b2967d48c409634125dad15ac4
Author: hubo <hubo@qq.com>
Date:   Sat Apr 4 23:20:19 2020 +0800
...


### 方式二: git log remote/[远程仓库名称]/[远程分支名称]
~ git log remotes/origin/master
commit 0bbca286b716ba69738d4faa2aa55f6ccb1d113a (HEAD -> prod, tag: v1.2, tag: v1, origin/prod_remote, origin/prod, origin/master, origin/dev, origin/HEAD, master)
Author: hubo <hubo@qq.com>
Date:   Sat Apr 4 23:21:04 2020 +0800

    Update 'foo.txt'

commit 5d16000c30f8c9b2967d48c409634125dad15ac4
Author: hubo <hubo@qq.com>
Date:   Sat Apr 4 23:20:19 2020 +0800
...

### 方式三: git log refs/remote/[远程仓库名称]/[远程分支名称]
### 上面两种方式最终都会转换这这种完整的命令进行交互
~ git log refs/remotes/origin/master
commit 0bbca286b716ba69738d4faa2aa55f6ccb1d113a (HEAD -> prod, tag: v1.2, tag: v1, origin/prod_remote, origin/prod, origin/master, origin/dev, origin/HEAD, master)
Author: hubo <hubo@qq.com>
Date:   Sat Apr 4 23:21:04 2020 +0800

    Update 'foo.txt'

commit 5d16000c30f8c9b2967d48c409634125dad15ac4
Author: hubo <hubo@qq.com>
Date:   Sat Apr 4 23:20:19 2020 +0800
...

如何使用git压缩

# 执行git gc后,git会把本地的分支,标签还有保存的对象就进行压缩,但是不会影响任何功能
### 执行gc前
.
├── FETCH_HEAD
├── HEAD
├── ORIG_HEAD
├── config
├── description
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   └── update.sample
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       ├── heads
│       │   ├── master
│       │   └── prod
│       └── remotes
│           └── origin
│               ├── HEAD
│               ├── prod
│               └── prod_remote
├── objects # 所用本地提交的对象,文件夹是2位数字,表示是提交点SHA-1的前2位,文件名则是后面的位数,拼接一起就是完整的SHA-1。后面会被压缩
│   ├── 08
│   │   └── f400c3a7b06b649c4dcc2bcf5caa4f6ca9b077
│   ├── 0b
│   │   └── bca286b716ba69738d4faa2aa55f6ccb1d113a
│   ├── 21
│   │   └── 6e97ce08229b8776d3feb731c6d23a2f669ac8
│   ├── 25
│   │   └── 7cc5642cb1a054f08cc83f2d943e56fd3ebe99
│   ├── 27
│   │   └── 1fcde7fd1b091e71e38b2acb147b7b6c36c792
│   ├── 2b
│   │   └── 382a7c565adae70021f03bc0af3b49b0cdfe21
│   ├── 2c
│   │   └── 6e4718913b8daabf7ca007fdc9f50fddd4b3f5
│   ├── 35
│   │   └── a22ca37757236b7e4a9d8f169fffdbadbb5dcb
│   ├── 3c
│   │   ├── 3f423d447933af034b1648b95f51bef6913830
│   │   └── afaf7662b4e75af79ea5c4d115b3831edef7de
│   ├── 4e
│   │   └── 83893cd6a55bfd870c6649a85ccf32ec00114f
│   ├── 5d
│   │   ├── 16000c30f8c9b2967d48c409634125dad15ac4
│   │   └── b3c883d05d79142f22e38edac3ff191daa58b9
│   ├── 5e
│   │   └── 2707d35e6afacee152aef3481992075984bc41
│   ├── 5f
│   │   └── 7efdd866c46e99b36fc1b4f0b00bffce7206d7
│   ├── 62
│   │   └── 6429cd178ee53b49ebe3b4922ae5aa1f17117b
│   ├── 65
│   │   └── a56c31f5c5cea938dd5e98241037ee14acef8b
│   ├── 67
│   │   └── 866bb307cc46e403254e2d5c229cd62f6f5fb0
│   ├── 6a
│   │   └── 7468598e89e9c90f91d96794c56dc3365dfaca
│   ├── 9f
│   │   └── 9943c73a0e116c2f1938e489e4b7b3a8809ded
│   ├── a6
│   │   └── 0a1dca78641445b1e9d63a515114afbb35e673
│   ├── c0
│   │   └── 29eb538a6981466836a440dc350967e4a9f724
│   ├── cc
│   │   └── 7079edef2b1e66e7870c31c4c4a95c2c135751
│   ├── e2
│   │   └── ac997f689c37676c95e5599beafc2a12ccb21e
│   ├── e5
│   │   └── 3a7f9ddb07575168c6c4242a8e1710c4b60397
│   ├── e6
│   │   └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
│   ├── e8
│   │   └── 51e806c539d76bace732035ea743de16dc4402
│   ├── e9
│   │   └── 65047ad7c57865823c7d992b1d046ea66edf78
│   ├── ea
│   │   └── ba830655708291fd7e70ac81e6a395580b4fe7
│   ├── f7
│   │   └── fc955dada899037da01fcbe8dfefe93ae73dd9
│   ├── fa
│   │   └── 3e5bb14a426d0190772f58e6ca0c596737fe08
│   ├── fe
│   │   └── 08ec6dabb19b24f70dd4dee82a02fd57334d51
│   ├── info
│   └── pack
├── packed-refs
└── refs
    ├── heads # HEAD信息,后面会压缩
    │   ├── master
    │   └── prod
    ├── remotes # 分支信息,后面会压缩
    │   └── origin
    │       ├── HEAD
    │       ├── prod
    │       └── prod_remote
    └── tags # 标签信息,后面会压缩
        ├── show
        ├── v1
        ├── v1.1
        └── v1.2


### 执行git gc后
~ git gc 
.
├── FETCH_HEAD
├── HEAD
├── ORIG_HEAD
├── config
├── description
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   └── update.sample
├── index
├── info
│   ├── exclude
│   └── refs
├── logs
│   ├── HEAD
│   └── refs
│       ├── heads
│       │   ├── master
│       │   └── prod
│       └── remotes
│           └── origin
│               ├── HEAD
│               ├── prod
│               └── prod_remote
├── objects
│   ├── info
│   │   └── packs
│   └── pack # 保存文件的索引和对象压缩了
│       ├── pack-328f0e218de58c6ae5578d006fd384d1b4a691e1.idx
│       └── pack-328f0e218de58c6ae5578d006fd384d1b4a691e1.pack
├── packed-refs # branch和tag都压缩到这个文件了
└── refs
    ├── heads
    ├── remotes
    │   └── origin
    │       └── HEAD
    └── tags

如何读删除远程地址

# git remote rm [删除远程仓库]
~ git remote rm origin 

如何创建一个裸库

~ git init --bare # 创建裸库的命令
Initialized empty Git repository in /Users/hubo/Documents/dev/git/demo2/
~ ls # 查看一下裸考的目录结构,会发现没有工作区
HEAD        config      description hooks       info        objects     refs

如何使用submodule

# 假设有一个base-common项目,存放自己的公共代码
~ git clone http://[你远程仓库的url]/hubo/base-common.git 
# 在里面创建一个hello.txt,然后提交到远程

# 假设有一个user-service项目,它用到了上面的base-common的代码
~ git clone http://[你远程仓库的url]/hubo/user-service.git

# 在user-service,使用下面的命令,把base-common以submodule加入进来
~ git submodule add http://[你远程仓库的url]/hubo/base-common.git
Cloning into '/Users/hubo/Documents/dev/git/user-service/base-common'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.

~ ls -al # 使用ls命令,会发现user-service下面已经有了base-common项目了,也多出了一个叫.gitmodules的文件
base-common .gitmodules .git

~ cat .gitmodules # 查看.gitmodules中的内容
[submodule "base-common"]
	path = base-common
	url = http://[你远程仓库的url]/hubo/base-common.git

# 如果通过web查看user-service的仓库,会发现里面的base-common是一个带有sha1标识的文件夹,点击后,会直接跳转到base-common项目。

如何更新submodule

# 假如说我在base-common修改了hello.txt的内容,现在想在user-service项目中进行同步更新
# 注意: 默认情况下,直接在user-service项目运行git pull是不会更新submodule中的项目的
### 方式一 在user-service的base-common目录中运行git pull命令
~ cd base-common && git pull
remote: Counting objects: 5, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From http://[你远程仓库的url]/hubo/base-common
   176eda1..d4d70da  master     -> origin/master
Updating 176eda1..d4d70da
Fast-forward
 hello.txt | 1 +
 1 file changed, 1 insertion(+)

### 方式二 直接在user-service的根目录下执行submodule foreach git pull让其遍历所有的submodule
# 并且以FastForward方式进行拉取合并
~ git submodule foreach git pull
Entering 'base-common'
remote: Counting objects: 5, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From http://[你远程仓库的url]/hubo/base-common
   d4d70da..fc2cb4f  master     -> origin/master
Updating d4d70da..fc2cb4f
Fast-forward
 hello.txt | 1 +
 1 file changed, 1 insertion(+)
 
 # 注意: 在更新完submodule后,你的自模块(user-service)会发现分支发生了变化,需要使用git add, git commit 和 git push推送到远程。

如何克隆一个带有submodule的项目

# 如果另外一个人直接使用git clone user-service项目,会发现里面虽然有了.gitsubmodules文件,但是base-common是空的
~ git clone http://[你远程仓库的url]/hubo/user-service user-service2
Cloning into 'user-service2'...
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
# 进去查看一下submodule的情况
~ cd user-service2 && tree
.
└── base-common

1 directory, 0 files

# 此时需要使用git submodule的其他命令来辅助
### 方式一: 步骤稍微多一些
~ cd base-common
~ git submodule init # 先对该submodule进行初始化
Submodule 'base-common' (http://[你远程仓库的url]/hubo/base-common.git) registered for path './'
~ git submodule update --recursive # 低估更新该submodule的内容
Cloning into '/Users/hubo/Documents/dev/git/user-service2/base-common'...
Submodule path './': checked out '176eda1ab73a18e2f12ae511670680bf2c55ed06'
# 注意此时checkout出来的分支是base-common最后一次提交的sha1值,运行下面的命令,让其再次切到master分支即可
~ git checkout master
Previous HEAD position was 176eda1 hello
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

# 此时需要使用git submodule的其他命令来辅助
### 方式二: 直接使用带--recursive的git clone命令,他会递归的把所有submodule都一并clone下来。
~ git clone http://[你远程仓库的url]/hubo/user-service user-service3 --recursive
Cloning into 'user-service3'...

remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
Submodule 'base-common' (http://[你远程仓库的url]/hubo/base-common.git) registered for path 'base-common'
Cloning into '/Users/hubo/Documents/dev/git/user-service3/base-common'...
remote: Counting objects: 9, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 9 (delta 0), reused 0 (delta 0)
Submodule path 'base-common': checked out '176eda1ab73a18e2f12ae511670680bf2c55ed06'

# 此时checkout处理的base-common也是base-common最后一次提交的sha1值,运行下面的命令,让其再次切到master分支即可
~ cd user-service3/base-common
~ git checkout master
Previous HEAD position was 176eda1 hello
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

如何删除项目中的submodule的项目

# 如果要把user-service的base-common的submodule删除,可以按照下面的步骤
~ git rm --cached base-common # 从暂存区
rm 'base-common'
~ rm -rf base-common # 删除base-common目录
~ rm .gitmodules # 删除.gitmodules文件
~ git add . && git commit -m "delete" && git push # 把更新推送到远程

[master 5454827] delete
 2 files changed, 4 deletions(-)
 delete mode 100644 .gitmodules
 delete mode 160000 base-common
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 12 threads
Compressing objects: 100% (1/1), done.
Writing objects: 100% (2/2), 180 bytes | 180.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0)
To http://[你远程仓库的url]/hubo/user-service
   3bd89a2..5454827  master -> master

如何使用subtree

# 假设有一个subtree项目, 里面存放了一些可以公用的代码或者配置
~ git clone http://[你远程仓库的url]/hubo/subtree.git 
# 在里面创建一个文本hello.txt,然后提交到远程
 
# 假设有一个base项目,它用到了上面的subtree的代码
~ git clone http://[你远程仓库的url]/hubo/subtree.git

# 在base,使用下面的命令,把subtree项目的git仓库地址先添加进来
~ git remote add subtree-origin http://[你远程仓库的url]/hubo/subtree.git

# 然后使用下面的命令,把subtree的项目的master拉取到base项目中,文件名就叫subtree。
# 如果加上--squash参数,则每次pull subtree远程仓库时,都会将subtree的代码合并成一个后然后在本地进行merge在提交创建一个新的提交
# 如果不加上,则会把subtree的提交记录到带到base项目中来。
# 这里的使用原则就是:要加上--squash那么后续操作都加上,要不加则都不加,不能混合使用,否则容易出现问题。
~ git subtree add --prefix=subtree subtree-origin master
git fetch subtree-origin master
warning: no common commits
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), 197 bytes | 98.00 KiB/s, done.
From http://[你远程仓库的url]/hubo/subtree
 * branch            master     -> FETCH_HEAD
 * [new branch]      master     -> subtree-origin/master
Added dir 'subtree'

~ tree # 查看是否已经有了subtree目录,并且里面有了文件
.
├── index.txt
└── subtree
    └── hello.txt

如何更新subtree

# 方式一:假设现在subtree模块进行了修改,然后base中去拉取最新的修改
# 在subtree中,我们做几次修改
~ echo 'demo1' > demo1.txt
~ git add . && git commit -m "add demo1" && git push
~ echo 'demo2' > demo2.txt
~ git add . && git commit -m "add demo1" && git push

# 然后切换到base项目,执行下面的代码来获取subtree的最新修改
# 此时会出现merge的vi界面,操作后,就能看到以下合并输出结果
~ git subtree pull --prefix=subtree subtree-origin master
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (6/6), 521 bytes | 130.00 KiB/s, done.
From http://[你远程仓库的url]/hubo/subtree
 * branch            master     -> FETCH_HEAD
   e296214..bc1a9b3  master     -> subtree-origin/master
Merge made by the 'recursive' strategy.
 subtree/demo1.txt | 1 +
 subtree/demo2.txt | 1 +
 2 files changed, 2 insertions(+)
 create mode 100644 subtree/demo1.txt
 create mode 100644 subtree/demo2.txt

# 在运行tree就能看到最新的2个文件已经加入了
~ tree
.
├── index.txt
└── subtree
    ├── demo1.txt
    ├── demo2.txt
    └── hello.txt

1 directory, 4 files

# 方式二:假设现在base项目中对subtree的模块中的文件进行了修改,需要推送到git仓库中去
# 在base中,我们做几次修改。注意:这里的push只是push到base的仓库,subtree并未得到更新
~ cd subtree
~ echo 'demo3' > demo3.txt
~ git add . && git commit -m "add demo1" && git push
~ echo 'demo4' > demo4.txt
~ git add . && git commit -m "add demo1" && git push

# 进入到base的根目录下,推送base项目中的最新subtree修改到远程
~ cd ..
~ git subtree push --prefix=subtree subtree-origin master
git push using:  subtree-origin master
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 534 bytes | 534.00 KiB/s, done.
Total 6 (delta 1), reused 0 (delta 0), pack-reused 0
To http://[你远程仓库的url]/hubo/subtree.git
   bc1a9b3..71a5b03  71a5b034d03fa9bb61b0143a5e6ff15f2b17ad14 -> master
   
# 切换到subtree项目,执行git pull操作
~ git pull
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (6/6), 514 bytes | 171.00 KiB/s, done.
From http://[你远程仓库的url]/hubo/subtree
   bc1a9b3..71a5b03  master     -> origin/master
Updating bc1a9b3..71a5b03
Fast-forward
 demo3.txt | 1 +
 demo4.txt | 1 +
 2 files changed, 2 insertions(+)
 create mode 100644 demo3.txt
 create mode 100644 demo4.txt
 
# 在运行tree就能看到最新的2个文件已经加入了
~ tree
.
├── demo1.txt
├── demo2.txt
├── demo3.txt
├── demo4.txt
└── hello.txt