git使用教程

1 git基础

1.1 初始化git仓库

git仓库也就是在git管理下的代码的版本库和一些git配置存放的地方,比如.git文件夹

初始化git仓库有两种方式:1、将尚未进行版本控制的本地目录转换为git仓库;2、从其他服务器克隆一个已存在的git仓库。

1.1.1 在已存在的目录中初始化仓库

进入一个目录执行

$ git init

该命令将在当前路径下创建一个名为 .git 的子目录,这个子目录含有你初始化的 Git 仓库中所有的必须文件,这些文件是 Git 仓库的骨干。 但是,在这个时候,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪

执行 git status 命令,我们可以看到如下信息,表明文件还处于Untracked状态

$ git status
On branch master
No commits yet
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        szj.txt
nothing added to commit but untracked files present (use "git add" to track)

下面我们要把想要的文件加入git跟踪,并提交。可以通过 git add 命令来指定所需的文件来进行追踪,然后执行 git commit

$ git add *.c
$ git add LICENSE
$ git commit -m 'initial project version'

git add命令是一个多用途命令,这里使用git add将文件加入跟踪。此外我们还可以使用这个命令把已修改文件放入暂存区。

1.1.2 克隆现有仓库

我们使用 git clone 命令克隆一个远端仓库

$ git clone https://github.com/libgit2/libgit2

这会在当前目录下创建一个名为libgit2的目录,并在这个目录下初始化一个.git 文件夹, 从远程仓库拉取下所有数据放入.git文件夹,然后从中读取最新版本的文件的拷贝,也就是checkout仓库中的最新版本作为工作副本。

1.2 git状态变化

在1.1中我们使用git init已经创建了一个本地仓库,并把文件加入了traced状态,并且我们有一个工作副本。通常我们会对工作副本进行修改,每当完成了一个阶段目标,我们就将工作副本中的修改提交到本地仓库。

在工作目录下的文件有两种状态:traced和untraced。而traced状态又会分为:Unmodified、Modified、Staged状态。

当我们使用git init初始化一个git仓库时,里面的文件默认处于Untraced状态,我们需要执行git add把文件加入跟踪;而git clone克隆一个远端仓库时,git在后台默认会将git仓库中的最新版本checkout到工作目录,且工作目录中的文件都是traced状态,且为unmodified状态。

当我们对工作目录中的文件做了修改,git将他们标记为modified状态。

我们可以有选择的将某些修改过的文件放入暂存区,然后提交已暂存的文件到本地仓库。

 

 

可以用git status命令查看状态。

克隆后立即执行git status

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

说明工作目录是干净的。此外该命令还显示了当前所在分支,并告诉你这个分支和对应的远程分支没有偏离。

当我们新创建了文件,

$ 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)

    README

nothing added to commit but untracked files present (use "git add" to track)

会提示我们有一个untraced文件

我们使用git add跟踪新文件

 $ git add README 

我们再运行git status

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)

    new file:   README

表明文件处于staged(暂存)状态。

当我们修改了一个已被traced的文件,将会看到下面内容

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

我们执行git add命令,将修改的文件放入staged区。

$ git add CONTRIBUTING.md
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   README
    modified:   CONTRIBUTING.md

如果我么想查看工作目录和staged做了哪些修改,运行如下命令

$ git diff
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8ebb991..643e24f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -65,7 +65,8 @@ branch directly, things can get messy.
 Please include a nice description of your changes when you submit your PR;
 if we have to read the whole diff to figure out why you're contributing
 in the first place, you're less likely to get feedback and have your change
-merged in.
+merged in. Also, split your changes into comprehensive chunks if your patch is
+longer than a dozen lines.

 If you are starting to work on a particular area, feel free to submit a PR
 that highlights your work in progress (and note in the PR title that it's

如果我们想查看staged和已提交的区别,运行如下命令

$ git diff --staged
diff --git a/README b/README
new file mode 100644
index 0000000..03902a1
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+My Project

下一步,我们把staged区的代码提交到本地仓库。执行命令git commit或者git commit -m '注释信息'

$ git commit -m "Story 182: Fix benchmarks for speed"
[master 463dc4f] Story 182: Fix benchmarks for speed
 2 files changed, 2 insertions(+)
 create mode 100644 README

执行commit后,会显示一些信息,这些信息包括当前分支是哪个分支,以及修改了几个文件、插入和删除了多少行。

如果我们提交时使用了-a参数(git commit -a -m '注释信息'),将可以省略git add命令。表示把所有已修改的文件放入staged区然后再提交。

我们使用git rm命令移除一个文件,这将移除staged区和工作目录中的文件

$ git rm PROJECTS.md
rm 'PROJECTS.md'
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    deleted:    PROJECTS.md

下次提交,该文件将不再纳入版本管理了。

但有时候我们想把staged区或者git仓库中删除,但是仍然希望保留在当前工作目录中(比如我们把大量编译文件放入了staged区),我们可以使用如下命令

 $ git rm --cached README 

1.3 git log

该命令用于查看提交记录

$ git log
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 16:40:33 2008 -0700

    removed unnecessary test

commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Sat Mar 15 10:31:28 2008 -0700

    first commit

举例

git log --oneline --graph --all

查看patch,执行git log -p或git log --patch将会显示文件修改详情

$ git log -p

commit 17280786ca1714eb2a397f5a7f63d5b5ff15f37b (tag: v1.0)
Author: xxx
Date:   Wed Sep 21 13:56:11 2022 +0800

    修改szj.txt

diff --git a/szj.txt b/szj.txt
index aba5a14..0286f8d 100644
--- a/szj.txt
+++ b/szj.txt
@@ -1 +1,2 @@
-aaabbccdd
\ No newline at end of file
+aaabbccdd
+s
\ No newline at end of file

$ git log -2,表示只显示最近的2次提交

$ git log -2
commit 4dd4da02828991a353d44664443664ee76ebb519 (HEAD -> master)
Author: zhenjingcool
Date:   Wed Sep 21 21:47:51 2022 +0800

    rm

commit b3f62fcc4d7b7078d7a10cf5f1bb225df52ce1ca (origin/master)
Author: zhenjingcool
Date:   Wed Sep 21 16:31:56 2022 +0800

    推送到远端仓库

一行显示

$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 changed the version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit

格式化显示

$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Scott Chacon, 6 years ago : changed the version number
085bb3b - Scott Chacon, 6 years ago : removed unnecessary test
a11bef0 - Scott Chacon, 6 years ago : first commit

git log --pretty=format常用选项

选项说明

%H

提交的完整哈希值

%h

提交的简写哈希值

%T

树的完整哈希值

%t

树的简写哈希值

%P

父提交的完整哈希值

%p

父提交的简写哈希值

%an

作者名字

%ae

作者的电子邮件地址

%ad

作者修订日期(可以用 --date=选项 来定制格式)

%ar

作者修订日期,按多久以前的方式显示

%cn

提交者的名字

%ce

提交者的电子邮件地址

%cd

提交日期

%cr

提交日期(距今多长时间)

%s

提交说明

--since和--util

 $ git log --since=2.weeks 

-S

这个参数是一个过滤器,可以过滤提交内容(比如文件名、文件内容、提交注释中的关键字过滤等)

 

 

 常用的git log参数

选项说明

-p

按补丁格式显示每个提交引入的差异。

--stat

显示每次提交的文件修改统计信息。

--shortstat

只显示 --stat 中最后的行数修改添加移除统计。

--name-only

仅在提交信息后显示已修改的文件清单。

--name-status

显示新增、修改、删除的文件清单。

--abbrev-commit

仅显示 SHA-1 校验和所有 40 个字符中的前几个字符。

--relative-date

使用较短的相对时间而不是完整格式显示日期(比如“2 weeks ago”)。

--graph

在日志旁以 ASCII 图形显示分支与合并历史。

--pretty

使用其他格式显示历史提交信息。可用的选项包括 oneline、short、full、fuller 和 format(用来定义自己的格式)。

--oneline

--pretty=oneline --abbrev-commit 合用的简写。

 

 

 

git log常用的限制输出参数

选项说明

-<n>

仅显示最近的 n 条提交。

--since--after

仅显示指定时间之后的提交。

--until--before

仅显示指定时间之前的提交。

--author

仅显示作者匹配指定字符串的提交。

--committer

仅显示提交者匹配指定字符串的提交。

--grep

仅显示提交说明中包含指定字符串的提交。

-S

仅显示添加或删除内容匹配指定字符串的提交。

 1.4 撤销操作

 在任何阶段,我们都有撤销一个操作的需求。

1.4.1 --amend

有时候,我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了,此时可以使用git commit --amend把上次提交和本次提交合并,并且最终只有一条提交记录

如下例子,我们修改了szj.java和aaa,然后我们git add szj.java,然后我们git commit,然后发现漏了aaa,于是乎,git add aaa,然后git commit --amend。最终我们使用git log查看提交记录,只有一条提交记录。

$ git commit -m '修改szj.java'
[master a932bd8] 修改szj.java
 1 file changed, 1 insertion(+)

82474@szj MINGW64 /d/code/mygituseage (master)
$ git add aaa

82474@szj MINGW64 /d/code/mygituseage (master)
$ git commit --amend
[master f3a2644] 修改szj.java
 Date: Wed Sep 21 22:50:33 2022 +0800
 2 files changed, 1 insertion(+)
 create mode 100644 aaa

git commit --amend命令可以使我们避免提交一些注释为"小修补,修正笔误","漏提了一个文件"等这种类似的情况,这种情况下,我们使用--amend参数可以避免弄花git的提交记录。

1.4.2 取消暂存的文件

当我们修改了文件,并且放入了staged区,我们执行git status

$ vi szj.java
$ git add szj.java
$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   szj.java

将会有如下提示:use "git restore --staged <file>..." to unstage。意思就是执行这个命令可以取消暂存。

$ git restore --staged szj.java
$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.
  (use "git push" to publish your local commits)

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:   szj.java

no changes added to commit (use "git add" and/or "git commit -a")

这时文件会从暂存区中取出,放回到工作目录。

1.4.3 撤销对文件的修改

如果我们在工作目录做了修改,

$ git status
On branch master
Your branch is ahead of 'origin/master' by 5 commits.
  (use "git push" to publish your local commits)

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:   szj.java

no changes added to commit (use "git add" and/or "git commit -a")

但是由于某些原因不想保留这些修改,上面git status给出了提示:use "git restore <file>..." to discard changes in working directory。意思就是执行这个命令可以撤销工作目录做的修改。

 $ git restore szj.java 

1.5 远程仓库的使用

1.5.1 添加远程仓库

如果我们使用git init初始化本地仓库,我们执行git remote将返回给我们一个空的结果,表示没有远程仓库

使用如下方式添加远程仓库

 $ git remote add origin git@github.com:zhenjingcool/mygituseage.git 

然后我们再查看远程仓库就有了

$ git remote
origin
$ git remote -v
origin  git@github.com:zhenjingcool/mygituseage.git (fetch)
origin  git@github.com:zhenjingcool/mygituseage.git (push)

我们可以添加任意多个远程仓库

 $ git remote add pb https://github.com/paulboone/ticgit 

 

如果我们使用git init初始化本地仓库,我们想把这个本地仓库push到远程的某一个仓库,可以这样操作

$ git push origin master:master

表示的意思是,push操作,push到origin(也就是git@github.com:zhenjingcool/mygituseage.git)的master分支,并且push的是本地master分支,push到远端的master分支。如果远端没有master分支,将会自动创建。

 

当我们运行git remote -v时,会告诉我们对远程仓库有什么权限

$ git remote -v
origin  git@github.com:zhenjingcool/mygituseage.git (fetch)
origin  git@github.com:zhenjingcool/mygituseage.git (push)

上面表示的是,我们对远程仓库有push和fetch的权限。push指的是推送,fetch指的是抓取。

1.5.2 从远程仓库fetch和pull

从远程仓库获取数据,有两种方式,一种是fetch,另一种是pull

两者的区别是:

git fetch 命令只会将数据下载到你的本地仓库——它并不会自动合并或修改你当前的工作。 当准备好时你必须手动将其合并入你的工作。

如果你的当前分支设置了跟踪远程分支, 那么可以用 git pull 命令来自动抓取后合并该远程分支到当前分支

1.5.3 push到远程仓库

 $ git push origin master:master 

我们平时经常使用的命令是git push,没指明推送到哪个远程仓库,也没指定推送到哪个远程分支。其实这里git为我们做了很多,首先,如果我们只添加了一个远程仓库,则可以省略origin,如果本地分支是master,远程分支也是master,则可以省略master:master

1.5.4 查看远程仓库

$ git remote show origin
* remote origin
  Fetch URL: git@github.com:zhenjingcool/mygituseage.git
  Push  URL: git@github.com:zhenjingcool/mygituseage.git
  HEAD branch: main
  Remote branches:
    dev    tracked
    main   tracked
    master tracked
    prod   tracked
  Local branches configured for 'git pull':
    dev    merges with remote dev
    master merges with remote master
  Local refs configured for 'git push':
    dev    pushes to dev    (up to date)
    master pushes to master (up to date)

该命令告诉我们很多信息:

1 远程仓库的head是main

2 远程仓库有如下几个分支:dev、main、master、prod

3 运行git pull时远程dev分支将merge到本地dev分支,远程master分支将merge到本地master分支。当然也可以使用 git pull origin master:master 指定拉取哪个远程仓库的哪个分支到本地的哪个分支。

4 运行git push时,本地dev分支将push到远程dev分支。当然我们可以使用 git push origin master:master 指定本地分支和远程仓库、远程分支。

1.6 Tag

查看标签

$ git tag
v1.0

创建标签

$ git tag -a v1.4 -m "my version 1.4"
$ git tag
v0.1
v1.3
v1.4

也可以后期对某一个提交打标签

 $ git tag -a v1.2 9fceb02 

查看标签

$ git show v1.4
tag v1.4
Tagger: Ben Straub <ben@straub.cc>
Date:   Sat May 3 20:19:12 2014 -0700

my version 1.4

commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date:   Mon Mar 17 21:52:11 2008 -0700

    changed the version number

共享标签

默认情况下,git push 命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上

$ git push origin v1.5
Counting objects: 14, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (12/12), done.
Writing objects: 100% (14/14), 2.05 KiB | 0 bytes/s, done.
Total 14 (delta 3), reused 0 (delta 0)
To git@github.com:schacon/simplegit.git
 * [new tag]         v1.5 -> v1.5

2 git分支

2.1 简介

假如我们本地仓库中有三个文件,README,LICENSE,test.rb。当我们把他们加入staged区,会创建3个blob对象。当我们commit时,会根据3个blob对象的校验和创建一个树对象,然后创建一个commit对象,如下图所示。

 

当我们提交了3次之后,其数据结构为

 

 我们再说说git中的分支,分支其实是一个指针,它指向commit对象,并且指向最新的commit对象

 

分支创建

创建分支非常简单,它只是创建了一个新的指针而已

 $ git branch testing 

HEAD指针

对于本地分支来说,HEAD指针指向当前所在的本地分支

对于远程分支来说,HEAD指针指向远程仓库的默认分支

 

 

我们可以运行git log 查看,如下,指明了当前分支是本地master分支,对应的远程分支是master

$ git log -1
commit 74e6fcd6130d771b24cc31ee904d3f1f155bb5dc (HEAD -> master, origin/master)
Merge: 4dd4da0 0367a09
Author: zhenjingcool <>
Date:   Thu Sep 22 09:26:12 2022 +0800

    merge

分支切换

 $ git checkout testing 

运行上面命令后分支切换到testing分支,HEAD指针指向了testing

 

分支分叉

当我们在各个分支上进行开发任务时,各个分支commit记录并行进行,互不影响

 

两个分支独立演进,在后续的合适的时刻,我们可以合并两个分支

我们使用如下命令查看分支分叉

 

2.2 创建分支

假设我们正在issue53分支上开发,突然有个紧急bug要处理,我们创建新分支hotfix并切换到hotfix来处理这个问题

  $ git branch hotfix 

问题处理完之后,我们需要把hotfix分支合并到master分支,并发布

 

 

$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
 index.html | 2 ++
 1 file changed, 2 insertions(+)

我们看到Fast-forward提示,表示合并分支时master指针快进到提交点C4.

2.3 删除分支

$ git branch -d hotfix
Deleted branch hotfix (3a0874c).

2.4 合并分支

当我们开发完了iss53分支,我们将其合并到master分支

$ git checkout master
Switched to branch 'master'
$ git merge iss53
Merge made by the 'recursive' strategy.
index.html |    1 +
1 file changed, 1 insertion(+)

这里的merge打印出的提示信息和前面2.2中的不一样。因为iss53是从c2开始分叉的,而此时master指针已经指向了c4,所以git会做一些额外的工作,会有个合并提交记录c6,见下图的合并前和合并后

合并前

 

合并后

2.5 远程分支

我们使用如下命令查看远程分支

$ git branch -a
  dev
* master
  remotes/origin/HEAD -> origin/main
  remotes/origin/dev
  remotes/origin/main
  remotes/origin/master
  remotes/origin/prod

其中,上面显式了2个本地分支,和5个远程跟踪分支。远程跟踪分支顾名思义就是远程分支的引用,也就是说只要和远程仓库有网络连接,远程跟踪分支就会向前移动以指向远程分支的真实状态。

当我们使用命令git clone拷贝一个远程分支到本地时,git会默认将远程分支命名为origin,创建一个指向远程分支master的指针,并在本地将其命名为origin/master,同时创建一个本地分支master作为origin/master的对应。当我们使用git pull拉取代码时,其对应完整命令为git pull origin master:master,也就是说从origin仓库的master分支拉取代码,拉取到本地的master分支。

 

 如果要与给定的远程仓库同步数据,运行git fetch <remote>命令,该命令将会从远程分支抓取本地没有的数据,并且更新本地数据库,移动origin/master指针到更新之后的位置

我们可以添加多个远程分支

$ git remote add teamone git://git.team1.ourcompany.com
$ git remote add teamtwo git://git.team2.ourcompany.com

2.5.1 push到远程分支

我们可以把一个本地分支推送到远程仓库,或者如果远程仓库已经有这个分支,我们可以把本地修改推送到远程对应的分支

命令格式为: git push <remote> <branch> 

例子: git push origin master:master 或者 git push origin master 

2.5.2 rebase

git中整合两个分支的修改有两种方法,一种是merge,另一种是rebase,也就是变基。

merge和rebase示意图

 

上图显示了两个独立演进的分支merge和rebase后的最终效果图,对于官网介绍的其原理和过程还不甚了解,后面如有需要再补充

下面是一个变基的例子

szj分支有两次提交,master分支也有两次提交。当前在szj分支,我想把master分支最新的commit记录合并到szj分支。则在szj分支下执行rebase命令,如下图:

rebase的过程:

  • 首先,git 会把 szj 分支里面的每个 commit 取消掉;
  • 其次,把上面的操作临时保存成 patch 文件,存在 .git/rebase 目录下;
  • 然后,拉取master分支的两次commit并应用在 szj 分支;
  • 最后,把上面保存的 patch 文件应用到 szj 分支上。

2.5.3 使用rebase进行commit记录的合并

有时,我们有很多commit,但是这些commit都是对应同一个功能,我们不想在提交记录里面有额外的重复提交记录,我们可以把这些提交进行合并

比如,我们有如下的提交记录,最近的4条提交记录我们想合并

执行如下命令,注意,startpoint endpoint是前开后闭的。且endpoint省略的话则默认一直到最后提交记录

git rebase -i (startpoint endpoint]

其中-i会打开交互界面,如下

 

注意的是,这里的提交记录和git log显式的顺序是相反的,也就是,最下面的记录是最新的提交记录

我们想把后面3次提交都合并到 a3480bc ,因此,我们修改如下

 

然后wq保存退出,接下来会弹出如下界面,我们注释掉后面3个comment

 

然后,我们会发现,4个提交记录被合并为1个了,目的达到

3 git工具

 3.1 查看提交历史

 我们使用git log查看提交记录

$ git log -2
commit 24c1952c841d0bfe5c416e3174f27d78962de293 (HEAD -> szj, master)
Author: zhenjingcool <824742327@qq.com>
Date:   Fri Sep 23 16:57:22 2022 +0800

    16:56修改txt

commit 36bce6f185e9c87fc76a2ceef5cf8128bd01e276
Author: zhenjingcool <824742327@qq.com>
Date:   Fri Sep 23 16:55:19 2022 +0800

    16:54修改txt

然后使用git show查看具体修改内容

$ git show 24c1952c841d0bfe5c416e3174f27d78962de293
commit 24c1952c841d0bfe5c416e3174f27d78962de293 (HEAD -> szj, master)
Author: zhenjingcool <824742327@qq.com>
Date:   Fri Sep 23 16:57:22 2022 +0800

    16:56修改txt

diff --git a/szj.txt b/szj.txt
index e99c7fa..4b5ca68 100644
--- a/szj.txt
+++ b/szj.txt
@@ -1 +1,2 @@
 16:54修改txt文件
+16:56修改txt

3.2 HEAD引用变更日志

我们前面讲过,对于本地仓库来说,HEAD指针指向当前分支的最近一次提交点。

我们使用命令git reflog查看HEAD指针变更记录。每当我们切换分支时都会有记录。

$ git reflog
24c1952 (HEAD -> szj, master) HEAD@{0}: checkout: moving from master to szj
24c1952 (HEAD -> szj, master) HEAD@{1}: merge szj: Fast-forward
c735868 HEAD@{2}: checkout: moving from szj to master
24c1952 (HEAD -> szj, master) HEAD@{3}: rebase (finish): returning to refs/heads/szj
24c1952 (HEAD -> szj, master) HEAD@{4}: rebase (pick): 16:56修改txt
36bce6f HEAD@{5}: rebase (pick): 16:54修改txt
c735868 HEAD@{6}: rebase (start): checkout master
6bb1496 HEAD@{7}: checkout: moving from master to szj
c735868 HEAD@{8}: checkout: moving from szj to master
6bb1496 HEAD@{9}: commit: 16:56修改txt
02863f0 HEAD@{10}: checkout: moving from master to szj
c735868 HEAD@{11}: commit: 16:55修改java
9949a3a HEAD@{12}: checkout: moving from szj to master
02863f0 HEAD@{13}: commit: 16:54修改txt
3d76177 (origin/master) HEAD@{14}: checkout: moving from master to szj
9949a3a HEAD@{15}: commit: 16:52修改java文件
3d76177 (origin/master) HEAD@{16}: checkout: moving from szj to master
3d76177 (origin/master) HEAD@{17}: checkout: moving from master to szj
3d76177 (origin/master) HEAD@{18}: checkout: moving from szj to master
3d76177 (origin/master) HEAD@{19}: checkout: moving from master to szj
3d76177 (origin/master) HEAD@{20}: commit: modify szj
74e6fcd HEAD@{21}: checkout: moving from main to master
2567896 (origin/prod, origin/main, origin/dev, origin/HEAD, main) HEAD@{22}: clone: from github.com:zhenjingcool/mygituseage.git

3.3 交互式staged

当我们对工作目录做了修改,希望把特定的文件放入staged区,我们除了使用 [git add 特定文件] 这种方式外,我们还可以使用交互式界面进行更便捷的方式操作。

使用git add -i或者git add --interactive命令进入交互式界面

$ git add -i
           staged     unstaged path
  1:    unchanged        +1/-0 szj.java
  2:    unchanged        +1/-0 szj.txt

*** Commands ***
  1: status       2: update       3: revert       4: add untracked
  5: patch        6: diff         7: quit         8: help
What now>

进入交互式界面后,我们会发现,修改的文件被区分为staged和unstaged两列。然后下面是可操作的命令。如果我们想把其中一个文件放入暂存区,这样操作

What now> u //输入2或者u进行加入暂存区的操作
           staged     unstaged path
  1:    unchanged        +1/-0 szj.java
  2:    unchanged        +1/-0 szj.txt
Update>> 1 //选择把第一个文件放入暂存区
           staged     unstaged path
* 1:    unchanged        +1/-0 szj.java
  2:    unchanged        +1/-0 szj.txt
Update>> //一个回车,之后将把szj.java放入暂存区
updated 1 path

*** Commands ***
  1: status       2: update       3: revert       4: add untracked
  5: patch        6: diff         7: quit         8: help
What now>

然后我们查看一下

What now> s //输入s表示status命令,我们可以看到,szj.java已经放到了暂存区,szj.txt还是在工作区间
           staged     unstaged path
  1:        +1/-0      nothing szj.java
  2:    unchanged        +1/-0 szj.txt

*** Commands ***
  1: status       2: update       3: revert       4: add untracked
  5: patch        6: diff         7: quit         8: help
What now>

如果我们想把某些文件取消暂存,执行如下命令

What now> 3 //3或者r表示revert操作
           staged     unstaged path
  1:        +1/-0      nothing szj.java
  2:    unchanged        +1/-0 szj.txt
Revert>> 1 //选择需要取消暂存的文件
           staged     unstaged path
* 1:        +1/-0      nothing szj.java
  2:    unchanged        +1/-0 szj.txt
Revert>> //回车
reverted 1 path

*** Commands ***
  1: status       2: update       3: revert       4: add untracked
  5: patch        6: diff         7: quit         8: help
What now> s //然后,我们查看状态,可以看到两个文件都处于unstaged状态了
           staged     unstaged path
  1:    unchanged        +1/-0 szj.java
  2:    unchanged        +1/-0 szj.txt

*** Commands ***
  1: status       2: update       3: revert       4: add untracked
  5: patch        6: diff         7: quit         8: help
What now>

此外,我们还可以查看已暂存文件做了哪些修改,操作方式类似,这里就不举例子了

3.4 stash贮藏

 当我们在某一分支做了修改,突然有一个紧急任务需要切换到master分支进行处理,而我们又不想把这个进行中的工作进行一次commit。这时我们可以使用git stash命令将当前工作贮藏到一个栈中。

$ git status //有两个文件做了修改
On branch szj
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   szj.java
        modified:   szj.txt


root@DESKTOP-Q6VGN6H MINGW64 /e/mygit/mygituseage (szj)
$ git stash push //将当前工作压入贮藏栈中
Saved working directory and index state WIP on szj: 24c1952 16:56修改txt

root@DESKTOP-Q6VGN6H MINGW64 /e/mygit/mygituseage (szj)
$ git stash list //查看贮藏栈中的内容
stash@{0}: WIP on szj: 24c1952 16:56修改txt

root@DESKTOP-Q6VGN6H MINGW64 /e/mygit/mygituseage (szj)
$ git stash apply stash@{0} //将贮藏栈中的某一项恢复到工作目录
On branch szj
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:   szj.java
        modified:   szj.txt

no changes added to commit (use "git add" and/or "git commit -a")

root@DESKTOP-Q6VGN6H MINGW64 /e/mygit/mygituseage (szj)
$ git stash list
stash@{0}: WIP on szj: 24c1952 16:56修改txt

root@DESKTOP-Q6VGN6H MINGW64 /e/mygit/mygituseage (szj)
$ git stash drop stash@{0} //删除贮藏栈中的某一项,这里我们也可以使用git stash pop逐一弹出
Dropped stash@{0} (0ae8e6d384c68a5e1edc2fe9adb73d1f2af051e4)

如果我们放入贮藏栈之前,已经有一些文件放入了staged区,此时我们把当前工作压入stash栈,当我们恢复时,如果希望之前在staged区的文件还在staged区,我们需要加上--index命令

$ git stash apply stash@{0} --index
On branch szj
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   szj.java

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:   szj.txt

需要注意的是,贮藏只会贮藏traced文件,untraced文件不会被贮藏,如果希望也贮藏未跟踪文件,需要使用 --include-untracked 参数。如果也需要贮藏ignore文件,则使用--all参数。

从贮藏创建一个新的分支

如果我们把当前工作压入贮藏栈,其后在当前分支上继续做了一些工作,然后,我们git stash apply后,很可能会有冲突,我们不得不解决冲突。如果我们不希望发生这种情况,我们可以把贮藏的工作创建一个新的分支

 $ git stash branch testchanges 

3.5 修改历史

3.5.1 修改最后一次提交

当我们进行了commit,但是发现提交备注写错,或者漏提交了一些文件,而我们又不想因为这些小修改而多一个提交记录,我们可以使用如下命令

 $ git commit --amend 

3.5.2 修改多个commit

可以使用交互式变基命令git rebase -i来达到修改某个commit的目的

比如我们有3次提交

$ git log --oneline -4
e98a317 (HEAD -> szj) 第3次修改szj.java
a215815 再次修改szj.java
dd7ac0e 修改szj.java
3d76177 (origin/master, master) modify szj

我们想修改第二次提交,提交信息由"再次修改szj.java"改为"第2次修改szj.java",则我们执行

 $ git rebase -i HEAD~3 

会弹出交互式页面

 

 

 我们将第二次提交pick改为edit,如下

pick dd7ac0e 修改szj.java
edit a215815 再次修改szj.java
pick e98a317 第3次修改szj.java

然后保存退出

82474@szj MINGW64 /d/code/mygituseage (szj)
$ git rebase -i HEAD~3
Stopped at a215815...  再次修改szj.java
You can amend the commit now, with

  git commit --amend

Once you are satisfied with your changes, run

  git rebase --continue

82474@szj MINGW64 /d/code/mygituseage (szj|REBASE 2/3)
$ git commit --amend //修改提交信息
[detached HEAD a20fd25] 第2次修改szj.java
 Date: Fri Sep 23 22:21:48 2022 +0800
 1 file changed, 1 insertion(+)

82474@szj MINGW64 /d/code/mygituseage (szj|REBASE 2/3)
$ git rebase --continue //这个命令将会自动的应用第3个提交
Successfully rebased and updated refs/heads/szj.

82474@szj MINGW64 /d/code/mygituseage (szj)
$ git log --oneline -4
ae42203 (HEAD -> szj) 第3次修改szj.java //我们发现,这里第二次提交和第三次提交的SHA-1校验和变了,因为发生了变基rebase
a20fd25 第2次修改szj.java 
dd7ac0e 修改szj.java
3d76177 (origin/master, master) modify szj

此外,我们还可以进行重新排序提交、删除某个提交、压缩提交、拆分提交等操作,这里不介绍了

3.6 reset重置

我们先了解一下git的HEAD、Index、工作目录三者的含义。我们可以把它们看成3棵树

HEAD:在本地仓库,HEAD是一个指向当前分支最近一次commit的指针。

Index:是预期的下一次提交的快照。也就是staged区。git将上次检出到工作目录的所有文件填充到Index区中,之后会将一些文件替换为新版本。

工作目录:HEAD和Index以一种高效但并不直观的方式,将它们的内容存储在 .git 文件夹中。 工作目录会将它们解包为实际的文件以便编辑。 你可以把工作目录当做 沙盒。在你将修改提交到暂存区并记录到历史之前,可以随意更改

 

3.6.1 reset原理

需要注意的是:reset操作的前提条件是这些commit都还未push到远程分支,如果已经push,reset不能使远程分支有任何变化。

比如当前分支情况如下

 

此时,我们执行了

 git reset 9e5e6a4 

则,分支情况如下图

 

它本质上是撤销了上一次git commit命令。除了HEAD这棵树发生了变化之外,其他两棵树未发生任何变化

接下来,reset会使用HEAD指向的当前快照内容来更新Index。如下图

 

如果执行的命令是git reset 9e5e6a4,默认到这里为止,reset操作使得HEAD和Index两棵树恢复到git add命令之前的状态。也就是说工作目录中的内容还给我们保留下来了。

如果我们执行的命令是 git reset --hard 9e5e6a4 则会继续让工作目录看起来像Index,也就是重置了工作目录。

 

必须注意的是--hard操作是一个危险操作,需要特别注意。

3.6.2 reset单独的文件或路径

上面介绍的是reset整个仓库。如果我们将reset作用于特定文件或路径,则HEAD将不会变化,也就是说会跳过第一步

比如,我们执行 git reset file.txt ,这其实相当于 git reset --mixed HEAD file.txt ,它的实际效果是:让Index看起来像HEAD,所以它本质上只是将file.txt从HEAD复制到Index中。它的效果和取消staged效果一样。

 

 当然,我们可以指定从特定commit拉取文件

 git reset eb43bf file.txt 

3.6.3 reset提交记录示例

目前我们的提交记录

$ git log --oneline --graph --branches -4
* 40b1607 (HEAD -> szj) 测试reset
* 4d1b6ad 第三次修改
* 5b3f211 第2次修改
* 60d8e26 第一次修改

我们希望撤销最近一次提交,恢复到提交之前(且未staged)的状态

$ git reset 4d1b6ad
Unstaged changes after reset:
M       szj.java

查看一下当前的状态

$ git status
On branch szj
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:   szj.java

no changes added to commit (use "git add" and/or "git commit -a")

查看提交记录

$ git log --oneline --graph --branches -4
* 4d1b6ad (HEAD -> szj) 第三次修改
* 5b3f211 第2次修改
* 60d8e26 第一次修改
* 46e593f 交付质控

我们发现,成功撤销了最近一次提交,且被撤销的修改重新回到工作目录。

更进一步,如果我们不希望保留最近一次的修改到工作目录

执行如下命令

git reset 4d1b6ad --hard

3.6.4 reset单个文件示例

有时,我们希望针对特定文件,希望回退到某个提交点的状态,可以使用reset命令

假如当前的提交记录为

$ git log --oneline --graph --branches -4
* 01d4eba (HEAD -> szj) 测试reset单个文件
* 4d1b6ad 第三次修改
* 5b3f211 第2次修改
* 60d8e26 第一次修改

我们希望把szj.java回退到4d1b6ad这个提交点的状态,且把4d1b6ad之后的修改放到staged区。则

$ git reset 4d1b6ad szj.java
Unstaged changes after reset:
M       szj.java

我们查看状态(这里staged区和工作目录都有szj.java,暂时不知道原因),看到szj.java成功回退了

$ git status
On branch szj
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   szj.java

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:   szj.java

3.6.5 强制reset到某次提交,并强制推到远程仓库

有时,我们错误的提交了代码,并且已经push到远程仓库,我们想把代码恢复到某次提交,可这样操作

$ git reset --hard e0be915
$ git push origin HEAD --force

3.7 revert 撤销commit

revert可以撤销指定的commit,撤销后会生成一个新的commit。

3.7.1 普通commit的撤销

我们修改README文件,首先,我们查看一下修改前

$ cat README
My Project

然后修改后的内容

$ cat README
My Project
This is My Project

提交修改

$ git add .
$ git commit -m '修改README'
[szj c0429f1] 修改README
 1 file changed, 1 insertion(+)

查看提交记录

$ git log --oneline -5
c0429f1 (HEAD -> szj) 修改README
2348882 (origin/master, master) fff
24c1952 16:56修改txt
36bce6f 16:54修改txt
c735868 16:55修改java

撤销提交

$ git revert c0429f1
[szj 04c733e] Revert "修改README"
 1 file changed, 1 deletion(-)

查看提交记录,发现revert操作后,新增了一个commit记录(红色显示内容)

$ git log --oneline -5
04c733e (HEAD -> szj) Revert "修改README"
c0429f1 修改README
2348882 (origin/master, master) fff
24c1952 16:56修改txt
36bce6f 16:54修改txt

然后我们查看README文件内容,发现,改文件的修改已经撤销了

$ cat README
My Project

3.7.2 merge commit的撤销

commit分两种,一种是Fast-Foward提交,也就是普通提交。另一种是merge提交,是指提交时有两个分支的merge操作。3.7.1讲述的是普通提交,这里我们讲述merge提交。

有两个分支,master和szj。在master上我们修改了README文件并提交,在szj上我们同样修改了README文件并提交。

最终文件内容为

$ cat README
My Project
主线分支上的修改
szj分支上的修改

此时的分支情况如下

 

我们查看一下0047734这个提交点的提交信息

$ git show 0047734
commit 004773415b2258442a69c4755ab416ab2f299509 (HEAD -> szj, master)
Merge: cf0f8fe b7b8cab //这里表示是一个Merge提交,Merge的两个父节点分别是cf0f8fe和b7b8cab,前面的编号1后面的编号2,后面我们会用到这一点
Author: zhenjingcool <824742327@qq.com>
Date:   Sat Sep 24 10:08:39 2022 +0800

    解决冲突

diff --cc README
index 921ed42,8121496..bc8bf80
--- a/README
+++ b/README
@@@ -1,2 -1,2 +1,3 @@@
  My Project
 +主线分支上的修改
+ szj分支上的修改

 

然后,我们想撤销szj分支上的那次提交,保留主线分支上的提交

使用-m参数表示要revert一个merge提交,后面的数字表示保留哪个分支,这个数字对应git show 0047734命令打印出来的 Merge: cf0f8fe b7b8cab 信息

$ git revert -m 1 0047734
[szj d1948c5] Revert "解决冲突"
 1 file changed, 1 deletion(-)

revert后,我们再看一下提交记录,我们发现多了一个提交记录d1948c5

$ git log --oneline -5 --graph
* d1948c5 (HEAD -> szj) Revert "解决冲突"
*   0047734 (master) 解决冲突
|\
| * b7b8cab 修改README
* | cf0f8fe 修改README
|/
* 2348882 (origin/master) fff

我们查看README文件,发现文件内容保留了master分支中的提交记录,撤销了szj分支中的提交记录

$ cat README
My Project
主线分支上的修改

3.7.3 revert之后重新上线

 这里是在网上找的例子

假设狗蛋在自己分支 goudan/a-cool-feature 上开发了一个功能,并合并到了 master 上,之后 master 上又提交了一个修改 h,这时提交历史如下:

a -> b -> c -> f -- g -> h (master)
           \      /
            d -> e   (goudan/a-cool-feature)

突然,大家发现狗蛋的分支存在严重的 bug,需要 revert 掉,于是大家把 g 这个 merge commit revert 掉了,记为 G,如下:

a -> b -> c -> f -- g -> h -> G (master)
           \      /
            d -> e   (goudan/a-cool-feature)

然后狗蛋回到自己的分支进行 bugfix,修好之后想重新合并到 master,直觉上只需要再 merge 到 master 即可(或者使用 cherry-pick),像这样:

a -> b -> c -> f -- g -> h -> G -> i (master)
           \      /               /
            d -> e -> j -> k ----    (goudan/a-cool-feature)

i 是新的 merge commit。但需要注意的是,这 不能 得到我们期望的结果。因为 d 和 e 两个提交曾经被丢弃过,如此合并到 master 的代码,并不会重新包含 d 和 e 两个提交的内容,相当于只有 goudan/a-cool-feature 上的新 commit 被合并了进来,而 goudan/a-cool-feature 分支之前的内容,依然是被 revert 掉了。

所以,如果想恢复整个 goudan/a-cool-feature 所做的修改,应该先把 G revert 掉:

a -> b -> c -> f -- g -> h -> G -> G' -> i (master)
           \      /                     /
            d -> e -> j -> k ----------    (goudan/a-cool-feature)

其中 G' 是对 G 的 revert 操作生成的 commit,把之前撤销合并时丢弃的代码恢复了回来,然后再 merge 狗蛋的分支,把解决 bug 写的新代码合并到 master 分支。

3.8 rebase变基

 变基已经在2.5.2中介绍过

3.9 cherry-pick拣选

为了保持线性的提交历史,有些维护者喜欢用在master分支上进行变基rebase和拣选cherry-pick,而不是直接将其合并。这样我们得到一个线性的项目提交历史。

cherry-pick类似于对特定的某次提交的变基。他会提取该提交的补丁,之后尝试将其重新应用到当前分支上。

假设分支信息如下,我们想把提交 e43a6 拣选到master分支上,可以运行如下命令

$ git cherry-pick e43a6
Finished one cherry-pick.
[master]: created a0a41a9: "More friendly message when locking the index fails."
 3 files changed, 17 insertions(+), 3 deletions(-)

 

 

 如果 5ddae 这个提交我们不需要了,我们可以删除rubby_client这个分支了。

3.10 git checkout

这里不介绍该命令常用的作用:切换分支

3.10.1 检出某次指定的commit到工作区

除了切换分支,git checkout命令还可以用于检出某次指定的commit到工作区

$ git checkout d15e59e
Note: switching to 'd15e59e'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at d15e59e 对接业务系统打通demo

我们前面提过:

对于本地分支来说,HEAD指针指向当前所在的本地分支

对于远程分支来说,HEAD指针指向远程仓库的默认分支

但是,当我们执行了 git checkout d15e59e 命令后,命令行输出提示我们 You are in 'detached HEAD' state ,表示此时HEAD 并不指向一个分支,仅指向某个 commit 提交。

此时我们可以在这个提交上做任意改动,而不影响任何分支的状态。

如果我们已经做了一些改动,我们可以执行如下命令 git switch -c <new branch name> 来创建一个新分支承载这些改动。

当我们想切换回master分支,我们直接执行 git checkout master 即可

$ git checkout master
Previous HEAD position was d15e59e xxxx
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

3.10.2 撤销工作区某个文件的修改

// 放弃单个文件修改,注意不要忘记中间的"--",不写就成了检出分支了
git checkout -- filepathname
// 放弃所有的文件修改
git checkout .

此命令用来放弃掉所有还没有加入到暂存区(就是 git add 命令)的修改。但是此命令不会删除掉刚新建的文件。因为刚新建的文件还没已有加入到 git 的管理系统中。所以对于git是未知的。自己手动删除就好了。

此命令和 git restore <file_name> 功能类似,git restore命令前面介绍过,可以去前面看看详细使用说明

3.11 submodule

3.11.1 添加子模块

将一个仓库添加到另一个仓库作为子模块

$ git submodule add https://github.com/chaconinc/DbConnector
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.

此时,运行git status,我们会注意到有一个.gitmodules文件

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitmodules
    new file:   DbConnector

.gitmodules文件是一个配置文件,配置了子模块的路径和名称等信息

在主模块中,不跟踪子模块代码,只跟踪子模块的提交记录。
比如,我们执行git diff .显示的是主模块的改动

$ git diff .
diff --git a/xbio_templates b/xbio_templates
index 5a54f4d..9722819 160000
--- a/xbio_templates
+++ b/xbio_templates
@@ -1 +1 @@
-Subproject commit 5a54f4d58507dc9f685e50633c4edcacefbee5e0
+Subproject commit 97228192718c6c052bc21f0daf9152d0831f0dba

如果要显示子模块的改动,需要进入子模块查看,或者使用git submodule foreach 'git diff'后面会讲

主模块中执行commit命令,会将子模块的提交记录提交到主模块中

$ git commit -am 'Add DbConnector module'
[master fb9093c] Add DbConnector module
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 DbConnector

push到远程仓库,需要注意的是,因为主仓库只跟踪子仓库commit id,因此主仓库和子仓库要分别push。

$ git push

3.11.2 克隆包含子模块的仓库

$ git clone http://192.168.xxx.xx:8888/szj/xbio.git

克隆之后,我们去子仓库查看,会发现子仓库是空的。必须执行如下两个命令进行子仓库的初始化

$ git submodule init
Submodule 'xbio_templates' (http://192.168.xxx.xx:8888/szj/xbio_templates.git) registered for path 'xbio_templates'
$ git submodule update
Cloning into 'D://xxx/xbio_templates'...
Submodule path 'xbio_templates': checked out '093aeb5bca7b14396659024638c7031fc1599631'

如果我们想在clone时自动克隆子仓库,可以使用如下命令

$ git clone --recurse-submodules http://192.168.xxx.xx:8888/szj/xbio.git

如果子模块做了修改,我们进入到子模块目录,拉取代码。然后回到主模块目录,查看修改

$ git diff .
diff --git a/xbio_templates b/xbio_templates
index 093aeb5..325d178 160000
--- a/xbio_templates
+++ b/xbio_templates
@@ -1 +1 @@
-Subproject commit 093aeb5bca7b14396659024638c7031fc1599631
+Subproject commit 325d178713b4cf9d25baea87a9a83b297ed80b89

如果我们想看子模块所修改的文件,使用下面命令

$ git diff --submodule
Submodule xbio_templates 093aeb5..325d178:
  > Update szj.txt

但是有时输出是这样的,暂时还不大清楚

$ git diff --submodule
warning: LF will be replaced by CRLF in s.txt.
The file will have its original line endings in your working directory
diff --git a/s.txt b/s.txt
index a1a7b99..fe90af6 100644
--- a/s.txt
+++ b/s.txt
@@ -1 +1,2 @@
 fdf
+fff //以上是主模块的变动
Submodule xbio_templates contains modified content //子模块的变动

无论输出是哪样的,子模块修改的详细信息不会列出来,需要我们进入到子模块查看。

3.11.3 子模块操作

1 拉取子模块代码并检出到工作区

一种典型的场景是,只需拉取子模块代码供主模块使用,而并不会更改子模块代码,此时我们可以这样操作

进入子模块执行如下两个命令

$ git fetch
From https://github.com/chaconinc/DbConnector
   c3f01dc..d0354fc  master     -> origin/master
$ git merge origin/master
Updating c3f01dc..d0354fc
Fast-forward
 scripts/connect.sh | 1 +
 src/db.c           | 1 +
 2 files changed, 2 insertions(+)

此时,我们执行git diff --submodule查看子模块拉取的更新(如果不想每次输入--submodule参数,可以配置git config --global diff.submodule log)

$ git diff --submodule
Submodule DbConnector c3f01dc..d0354fc:
  > more efficient db routine //这里是每次提交的comment
  > better connection routine //这里是每次提交的comment

如果此时在主模块上执行commit操作,因为主模块中只记录子模块的commit id,那么此次提交的主模块将锁定此时的子模块代码。

显然上述方式比较麻烦,我们可以在主模块路径下面执行如下替代命令git submodule update --remote DbConnector,可起到相同的效果

$ git submodule update --remote DbConnector
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 2), reused 4 (delta 2)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
   3f19983..d0354fc  master     -> origin/master
Submodule path 'DbConnector': checked out 'd0354fc054692d3906c85c3af05ddce39a1c0644'

需要注意的是,上述命令默认更新并checkout子模块的master分支。如果希望操作其他分支,可以配置.gitmodules文件或者配置.git/config

如果只是运行git submodule update --remote,将会更新并检出所有子模块。如果只想更新检出特定子模块,后面加上子模块名即可

$ git config -f .gitmodules submodule.DbConnector.branch stable //-f指定将配置写入.gitmodules

$ git submodule update --remote
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 2), reused 4 (delta 2)
Unpacking objects: 100% (4/4), done.
From https://github.com/chaconinc/DbConnector
   27cf5d3..c87d55d  stable -> origin/stable
Submodule path 'DbConnector': checked out 'c87d55d4c6d4b05ee34fbc8cb6f7bf4585ae6687'

这时,我们运行git status,git会显示子模块中有新提交

$ git diff
diff --git a/.gitmodules b/.gitmodules
index 6fc0b3d..fd1cc29 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
 [submodule "DbConnector"]
        path = DbConnector
        url = https://github.com/chaconinc/DbConnector
+       branch = stable
 Submodule DbConnector c3f01dc..c87d55d:
  > catch non-null terminated lines
  > more robust error handling
  > more efficient db routine
  > better connection routine

也可以使用git log -p --submodule 查看子模块所修改的提交

$ git log -p --submodule
commit 0a24cfc121a8a3c118e0105ae4ae4c00281cf7ae
Author: Scott Chacon <schacon@gmail.com>
Date:   Wed Sep 17 16:37:02 2014 +0200

    updating DbConnector for bug fixes

diff --git a/.gitmodules b/.gitmodules
index 6fc0b3d..fd1cc29 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
 [submodule "DbConnector"]
        path = DbConnector
        url = https://github.com/chaconinc/DbConnector
+       branch = stable
Submodule DbConnector c3f01dc..c87d55d:
  > catch non-null terminated lines
  > more robust error handling
  > more efficient db routine
  > better connection routine

2 从remote拉取上游更新

假如其他人对子仓库做了更新,我们在主仓库执行git pull

$ git pull
From https://github.com/chaconinc/MainProject
   fb9093c..0a24cfc  master     -> origin/master
Fetching submodule DbConnector
From https://github.com/chaconinc/DbConnector
   c3f01dc..c87d55d  stable     -> origin/stable
Updating fb9093c..0a24cfc
Fast-forward
 .gitmodules         | 2 +-
 DbConnector         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

git pull将会fetch子模块的提交记录和更改,但是只是fetch,并没有merge到对应子模块和checkout,这个可以通过git status命令验证,它会显示子模块“已修改”,且“有新的提交”,并且列出了新提交的提交记录。

我们需要执行git submodule update进行子模块的更新(merge并checkout)

 

posted @ 2022-09-21 22:20  zhenjingcool  阅读(268)  评论(1编辑  收藏  举报