0%

Git 使用总结 01

Git 的三种状态

下文的所有命令都在普通用户下执行,因此有些命令前会有 $ 号,系统是 Debian 9.6,Git 版本是 git version 2.11.0。本文参考:Pro Git v2 CHS 的第1章,第2章。

三种状态

已提交(committed)、已修改(modified)、已暂存(staged)

工作区的三个概念

有三种状态,相应的就有三个概念:Git 仓库、工作目录、暂存区域

已提交对应 Git 仓库,已修改对应工作目录,已暂存对应暂存区域。

Git 的基本工作流程

① 修改文件:在工作目录中修改文件。
② 暂存文件:将文件的快照放入暂存区域。
③ 提交更新:找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。

Git 不像其他版本管理系统,比较文件的差异并记录下来,Git 是直接记录文件的快照。因此 Git 更像是一个小型的文件系统,它把数据看作是对小型文件系统的一组快照。文件系统快照可以理解成是,对文件或目录的修改(不论是增,删,改)记录快照,下次就能恢复到原样。它恢复的信息是很全面的,不仅有文件的数据,还可以有文件或目录的元数据,比如创建时间,修改时间等等。

在 Linux 上安装 Git

Redhat 系或 Debian 系安装方式如下:

1
2
3
4
5
6
# 安装命令
sudo yum install git
sudo apt-get install git

# 查看版本
git --version

Git 的三个配置文件

/etc/gitconfig:系统级全局配置文件,对所有用户有效。使用带有 --system 选项的 git config 时,会读写此配置文件。
~/.gitconfig~/.config/git/config:当前用户的配置文件。传递 --global 选项可以读写此文件。
.git/config:当前仓库 Git 目录中的配置文件,针对该仓库。传递 --local 选项可以读写此文件。

每一个级别覆盖上一级别的配置,所以 .git/config 的配置变量会覆盖 /etc/gitconfig 中的配置变量。

设置 Git 的用户信息和文本编辑器

相关命令:git config

安装完 Git 的第一件事是设置用户名和邮件地址。每个 Git 提交都会使用这些信息:

1
2
3
4
5
6
# 设置用户名和邮件地址
git config --global user.name "JasonZh"
git config --global user.email "example@qq.com"

# 设置 git 默认使用的文本编辑器
git config --global core.editor vim

查看 Git 的配置信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 默认会依次读取 /etc/ 下,~/ 下的配置文件
# 如果处于 Git 仓库目录中,还会读取 .git/ 下的配置文件
# 所以配置的生效优先级是 ".git/" > "~/" > "/etc/"
git config --list

# 查看某一项配置
git config user.name

# 查看指定配置文件的配置项
# 仅查看全局配置项
git config --system --list

# 仅查看当前用户配置项
git config --global --list

# 处于特定仓库目录内时
# 仅查看特定仓库的配置项
git config --local --list

获取 Git 帮助

有三种获取 Git 帮助的方法:

1
2
3
git help config
git config --help
man git-config

获取 Git 仓库

相关命令:git init, git clone

有两种方法可以获取 Git 仓库:

① 初始化一个仓库:

1
2
# 假设把目录 test 当作 Git 仓库,先进入 test 目录,然后执行,完了会在目录里生成 .git 子目录,这是 Git 仓库的骨干目录
git init

② 克隆现有仓库:

1
2
3
4
5
# 从远程仓库克隆
git clone https://github.com/libgit2/libgit2

# 克隆并且重命名仓库名为 mylibgit
git clone https://github.com/libgit2/libgit2 mylibgit

检查当前文件状态

命令:git status

下面以 libgit2 仓库为例。如果克隆仓库后,进入该仓库目录,立即运行如下命令,会看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean

# 新加一个文件,默认它处于 未跟踪(Untracked files) 状态,Git 不会自动跟踪新添加的文件
echo 'My Project' > README

# 再次检查当前文件状态,新加的文件处于 未跟踪 状态
$ 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)

跟踪新文件

把新添加的文件加到 Git 跟踪,使用 git add 命令:

1
2
3
4
5
6
7
8
9
10
git add README

# 这个时候文件会处于 被跟踪(Changes to be committed) 状态,并且处于暂存状态
$ 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

注意 *. 的区别

git add *

添加所有文件到暂存区域。包括当前工作目录内所有子孙目录内的 . 开头的隐藏文件。但是不包括当前工作目录下的 . 开头的隐藏文件。

git add .

添加所有文件到暂存区域。同时包括当前工作目录下的隐藏文件。也就是说,用 . 号可以包含所有未暂存的文件。

暂存已修改文件

如果修改一个已被跟踪的文件,怎么暂存它呢,运行如下命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 比如修改文件 README.md,这个文件之前处于已跟踪状态
# 修改后,这个文件处于 未暂存(Changes not staged for commit) 状态
$ 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: README.md

暂存已经修改的被跟踪的文件,还是运行 git add 命令:

1
2
3
4
5
6
7
8
9
10
11
git add README.md

# 再次查看状态,此时文件 README.md 处于 已暂存(Changes to be commited) 状态
$ 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: README.md

Git 状态预览

上面用的 git status 命令会显示比较详细的信息,加 -s--short 选项可以显示简短的文件状态:

简短格式显示状态时,有6种情况

?? 开头:新添加的,但未跟踪的文件;或者是移出暂存区变成未跟踪状态的文件
A 开头:新添加的,且已加到暂存区中的文件,即新添加且已跟踪的文件
M_:左侧的 M,已被跟踪的文件,修改过,且已加入到暂存区
_M:右侧的 M,已被跟踪的文件,修改过,但还未加到暂存区
MM:已被跟踪的文件,修改过并且已加到暂存区,但之后又修改了
AM:新添加的文件,已加到暂存区,但之后又修改了

正常格式 vs 简短格式:

正常格式显示状态时,有 3 大类情况

① Changes to be committed:已暂存正等待提交的(即已跟踪,已暂存,等待提交的)
② Changes not staged for commit:已跟踪,但未暂存的(这种情况不会提交)
③ Untracked files:未跟踪的文件(可以是新建的文件,或把已跟踪的文件设为未跟踪,这种情况不会提交)

注意:
1、一个文件可以同时处于 情况① 与 情况② 下面。
2、一个文件的三大类情况组合起来的所有情况就是简短格式的6种情况。

忽略文件

如果需要忽略一些文件,不想加入 Git 的管理,比如编译程序产生的一些中间文件,就没必要提交到 Git。可以创建 .gitignore 文件排除这些文件。Git 仓库创建之初,就创建一个 .gitignore 文件是个很好的习惯。

文件 .gitignore 的格式:
1、以 # 开头都是注释
2、可以使用通配符 *, ?, [], **
3、以 / 开头防止递归
4、以 / 结尾用于指定目录
5、以 ! 开头指定例外文件或目录,即强制不被忽略的文件或目录

看看 .gitignore 文件的实际例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# * 匹配零个或多个任意字符
# 即以 ~ 结尾的文件都被忽略
# 默认会递归地排除仓库目录内所有子孙目录内的文件
*~

# [oa] 匹配字符 o 或者字符 a
# 即 *.o 或 *.a 文件都被忽略
*.[oa]

# no .a files
*.a

# but do track lib.a, even though you're ignoring .a files above
# 添加例外,上面规则排除了 *.a,但 lib.a 不会被排除
!lib.a

# only ignore the TODO file in the current directory, not subdir/TODO
# 仅排除直接子目录下的 TODO 文件,不递归子孙目录
/TODO

# ignore all files in the build/ directory
# 这个目录是仓库目录内的直接子目录,这个目录被排除,则这个目录内的全部内容也就被排除了
build/

# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt

# ignore all .pdf files in the doc/ directory
# ** 表示任意中间目录,包括空字符串
# 比如 a/**/z 可以匹配 a/z, a/b/z 或 a/b/c/z 等文件
doc/**/*.pdf

查看已暂存和未暂存的修改

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查看未暂存的文件修改的部分
# 此命令比较当前未暂存文件和暂存区域快照之间的差异
# 注意 diff 命令都是以 "行" 作为文件修改的最小单位
git diff

# 查看当前已暂存文件与已提交文件之间的差异
git diff --cached

# 使用其他差异比较工具
git difftool

# 查看系统支持的 diff 工具
git difftool --tool-help

提交更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 注意提交之前先把未暂存的文件暂存起来
# git add <file>
# 然后看看当前的文件状态
# git status

# 交互式提交
# 这种方式会启动文本编辑器以便输入本次提交的说明
# 执行 git config --global core.editor <editor> 设定文本编辑器
git commit

# 直接命令行提交
git commit -m "commit message"

# 上面的提交命令会提交所有已暂存但未提交的文件
# 也可以提交指定的文件

# 注意提交指定文件会跳过 git add 步骤
# 比如 README 是已跟踪但未提交的文件,就会把 README 文件也一并提交
git commit -m "message" COPYING README

跳过暂存步骤直接提交

1
2
# -a 选项告诉 commit 命令自动把已跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤
git commit -a -m 'message'

移除文件

① 移除并删除:
移出暂存区域,并从当前仓库目录中删除文件。

1
2
3
4
5
6
# 移除已暂存的文件,并从仓库目录中删除此文件,此文件就不会出现在未跟踪列表里了
git rm README

# 如果移除已暂存并且之后又修改过的文件,需要用 -f 强制删除
# 这样强制删除的数据不能被 Git 恢复,因为修改的部分没有提交,所以不建议这样做!
git rm -f README.md

② 仅移除:
仅仅移出暂存区域,但不删除文件。

1
2
# 仅仅移出暂存区域,让文件变成未跟踪状态,但文件还留在当前工作目录中
git rm --cached package.json

③ 使用通配符:
注意 Git 有自己的模式扩展匹配方式,为了防止 shell 干扰,用反斜杠转义或用单引号括起来。

1
2
# 删除 log 目录下的所有 *.log 文件(递归地删除 log 目录内所有子孙目录内的 *.log)
git rm 'log/*.log'

移动文件

不像其它的 VCS 系统,Git 并不显式跟踪文件移动操作。所以,应该用 Git 的移动命令 git mv。与 git rm 一样,git mv 只能操作(重命名)已暂存的文件。

1
2
# 移动已暂存的文件,实际上是重命名
git mv COPYING COPYING1

执行 git mv 等价于执行如下三个命令:

1
2
3
mv COPYING COPYING1
git rm COPYING
git add COPYING1

你可能会奇怪,Git 是怎么知道文件被移动了,别忘了 Git 跟踪的是文件内容的哈希,不是简单的文件名,而且 Git 的工作原理更像是小型的文件系统。

查看提交历史

命令 git log 用于查看提交历史,接下来的例子用 simplegit-progit 项目演示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 克隆演示仓库
git clone https://github.com/schacon/simplegit-progit

# 查看历史,不加任何选项的话,会列出项目的所有提交,最新的在最前面
$ git log
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gmail.com>
Date: Mon Mar 17 21:52:11 2008 -0700

changed the verison number

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

removed unnecessary test code

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

first commit

命令 git log 有许多选项定制输出的内容,比如:
git log -p 2 显示每次提交的内容差异(选项 -p--patch),且仅仅显示最近两次提交
git log --stat 显示每次提交的简略统计信息,比如几个文件被修改等等
git log --pretty=OPT 定制输出格式,OPT 可以是 oneline, short, format 等,详见 git help log 选项 --pretty

定制输出格式举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ git log --stat -1
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gmail.com>
Date: Mon Mar 17 21:52:11 2008 -0700

changed the verison number

Rakefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

# 格式化输出
$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Scott Chacon, 11 years ago : changed the verison number
085bb3b - Scott Chacon, 11 years ago : removed unnecessary test code
a11bef0 - Scott Chacon, 11 years ago : first commit

选项 --pretty=formt 常用格式占位符如下表:

详见 这里 –> Table 1. git log --pretty=format 常用的选项

命令 git log 常用选项如下表:

详见 这里 –> Table 2. git log 的常用选项

限制输出长度

之前你已经看到过 -2 了,它只显示最近的两条提交。不过实践中我们是不太用这个选项的,Git 在输出所有提交时会自动调用分页程序,所以你一次只会看到一页的内容。

使用 --since ( --after ) 和 --until ( --before ) 选项,基于时间来限制输出长度,查看提交内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 显示最近的 2条 提交
$ git log -2
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gmail.com>
Date: Mon Mar 17 21:52:11 2008 -0700

changed the verison number

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

removed unnecessary test code

# 显示指定时间之后的提交
# --since= 后的时间格式可以是 "2008-01-15", "2 years 2 days 1 minute ago" 等等
$ git log --since="2008-03-17"
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gmail.com>
Date: Mon Mar 17 21:52:11 2008 -0700

changed the verison number

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

removed unnecessary test code

# 匹配提交说明中有 version 关键字的所有提交
git log --grep='version'

# 只查看特定目录或文件的历史提交
# 特定目录或文件名放在最后,并用 -- 隔开

# 查看特定文件的提交
$ git log --pretty=format:'%h - %cd' -- README
a11bef0 - Sat Mar 15 10:31:28 2008 -0700

# 查看特定目录的提交
$ git log --pretty=format:'%h - %cd' -- lib/
085bb3b - Fri Apr 17 21:55:53 2009 -0700
a11bef0 - Sat Mar 15 10:31:28 2008 -0700

命令 git log 的限制输出的常用选项如下表:

详见 这里 –> Table 3. 限制 git log 输出的选项

撤销操作

在任何一个阶段,你都有可能想要撤消某些操作。有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。此时,可以运行带有 --amend 选项的提交命令尝试重新提交:

1
2
3
# 这个命令会把暂存区中的文件提交
# 如果这次提交与上次提交之间没有修改过文件,那么快照保持不变,仅仅是最后一次提交的提交说明发生变化
git commit --amend

如果提交后,忘记暂存修改过的文件:

1
2
3
4
5
# 运行如下命令提交
# 最终只会有一个提交,第二次提交将代替第一次提交的结果
git commit -m 'initial commit'
git add another_file
git commit --amend

取消暂存的文件

如果不小心暂存了不需要的文件。应该如何取消暂存呢。使用命令 git reset

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 修改文件并且暂存,然后查看当前文件状态
$ echo 'My Project' > README
$ git add README

# 查看状态中已经有提示如何移除暂存状态了
$ 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)

modified: README

# 移出暂存状态
$ git reset HEAD README
Unstaged changes after reset:
M README

# 再次查看状态
# 状态中给出了如何添加文件到暂存,以及如何撤销对文件的修改
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
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: README

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

撤销对文件的修改

如果不想保留对文件的修改,如何撤销呢。使用 git checkout 命令。

但是,注意,撤销操作是对已暂存(或者是对已提交,反正最低要求是已暂存)的文件而言的。如果文件已经暂存,之后又修改了,这个修改才可以被撤销。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 文件内容
$ cat NEWFILE
New File

# 文件状态为已暂存
$ 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: NEWFILE

# 修改文件
$ echo 'NEW LINE' >> NEWFILE

# 修改后的文件内容
$ cat NEWFILE
New File
NEW LINE

# 修改后的文件状态
$ 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: NEWFILE

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: NEWFILE

# 撤销对已暂存文件的修改
$ git checkout -- NEWFILE

# 撤销后文件内容恢复原样
$ cat NEWFILE
New File

# 撤销后文件状态恢复原样
$ 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: NEWFILE

注意:

命令 git checkout -- <file> 很危险。撤销后,你对那个文件的任何修改都会消失,Git 会拷贝另个文件来覆盖它。除非你十分清楚想要撤销的是什么,否则不要使用这个命令!

最后,如果你仍然想保留对那个文件做出的修改,但是现在仍然需要撤消,我们将会在 Git 分支 介绍保存进度与分支,这些通常是更好的做法。

远程仓库的使用

远程仓库是指托管在因特网或其他网络中的你的项目的版本库。你可以有好几个远程仓库,通常有些仓库对你只读,有些则可以读写。与他人协作涉及管理远程仓库以及根据需要推送或拉取数据。

查看远程仓库

如果想查看你已经配置的远程仓库服务器,可以运行 git remote 命令。它会列出你指定的每一个远程服务器的简写。如果你已经克隆了自己的仓库,那么至少应该能看到 origin,这是 Git 给你克隆的仓库服务器的默认名字:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 克隆远程仓库
$ git clone https://github.com/schacon/ticgit
Cloning into 'ticgit'...
remote: Enumerating objects: 1857, done.
remote: Total 1857 (delta 0), reused 0 (delta 0), pack-reused 1857
Receiving objects: 100% (1857/1857), 334.04 KiB | 242.00 KiB/s, done.
Resolving deltas: 100% (837/837), done.

# 查看远程仓库
$ cd ticgit/
$ git remote
origin

# 使用 -v 选项会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL
# 如果远程仓库不止一个,该命令会把他们全部列出来
# 这里的意思是 ticgit 仓库可以关联到多个远程 Git 仓库,这样就方便多人协作
$ git remote -v
origin https://github.com/schacon/ticgit (fetch)
origin https://github.com/schacon/ticgit (push)

添加远程仓库

运行命令 git remote add <shortname> <url> 添加一个新的远程 Git 仓库。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 还是以上面的 ticgit 仓库为例,进入 ticgit 目录,执行
$ git remote
origin

# 下面 pb 是远程服务器的简写,后面是服务器 URL
$ git remote add pb https://github.com/paulboone/ticgit

# 查看结果
$ git remote -v
origin https://github.com/schacon/ticgit (fetch)
origin https://github.com/schacon/ticgit (push)
pb https://github.com/paulboone/ticgit (fetch)
pb https://github.com/paulboone/ticgit (push)

拉取新添加的远程仓库的内容,可以使用服务器简写 pb 代替 URL:

1
2
3
4
5
6
7
8
9
10
# 拉取后,paulboone 的 master 分支就能在本地通过 pb/master 访问
# 注意这里的 pb/master 代表分支,不是本地的 pb/master 目录
$ git fetch pb
remote: Enumerating objects: 22, done.
remote: Counting objects: 100% (22/22), done.
remote: Total 43 (delta 22), reused 22 (delta 22), pack-reused 21
Unpacking objects: 100% (43/43), done.
From https://github.com/paulboone/ticgit
* [new branch] master -> pb/master
* [new branch] ticgit -> pb/ticgit

关于分支的内容,将在 Git 分支 中详细介绍。

从远程仓库中抓取与拉取

如上文所示,从远程仓库获取数据,执行命令 git fetch [remote-name]

这个命令会访问远程仓库,从中拉取所有 你还没有的 数据。执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。

如果你使用 clone 命令克隆了一个仓库,命令会自动将其添加为远程仓库并默认以 “origin” 为简写。所以,git fetch origin 会抓取克隆(或上一次抓取)后新推送的所有工作。必须注意 git fetch 命令会将数据拉取到你的本地仓库,但它并不会自动合并或修改你当前的工作。当准备好时你必须手动将其合并入你的工作。

如果你有一个分支设置为跟踪一个远程分支(阅读下一节与 Git 分支 了解更多信息),可以使用 git pull 命令来自动地抓取然后合并远程分支到当前分支。这对你来说可能是一个更简单或更舒服的工作流程。默认情况下,git clone 命令会自动设置本地 master 分支跟踪克隆的远程仓库的 master 分支(或不管是什么名字的默认分支)。运行 git pull 通常会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支。

推送到远程仓库

推送命令 git push [remote-name] [branch-name]。比如:

1
2
3
4
# 比如你想要将 master 分支推送到 origin 服务器
# 再次说明,克隆时通常会自动帮你设置好那两个名字
# 即上文 git remote -v 命令显示的 origin 的 fetch 和 push
git push origin master

只有当你有所克隆服务器的写入权限,并且之前没有人推送过时,这条命令才能生效。当你和其他人在同一时间克隆,他们先推送到上游然后你再推送到上游,你的推送就会毫无疑问地被拒绝。你必须先将他们的工作拉取下来并将其合并进你的工作后才能推送。

查看远程仓库的更多信息

使用命令 git remote show [remote-name]

详见 这里 –> 查看远程仓库 小节 –> 如果想要查看某一个远程仓库的更多信息 …

远程仓库的移除与重命名

使用命令 git remote rename。比如,想要把 pb 重命名为 paul

1
2
3
4
5
$ git remote rename pb paul

$ git remote
origin
paul

值得注意的是这同样也会修改你的远程分支名字。那些过去引用 pb/master 的现在会引用 paul/master。

使用命令 git remote rm 移除远程仓库。比如,你已经从服务器上搬走了或不再想使用某一个特定的镜像了,又或者某一个贡献者不再贡献了。

1
2
3
4
$ git remote rm paul

$ git remote
origin

打标签

像其他版本控制系统(VCS)一样,Git 可以给历史中的某一个提交打上标签,以示重要。比较有代表性的是人们会使用这个功能来标记发布结点(v1.0 等等)。

列出标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 这次以 libgit2 仓库为例
cd libgit2

# 列标签
# 这个命令以字母顺序列出标签,但是它们出现的顺序并不重要
$ git tag
v0.1.0
v0.10.0
v0.11.0
...

# 使用特定模式查找标签
# 这个模式就是 shell 的通配符 *, ?, []
# 详见 git help tag 的 --list 选项
$ git tag --list 'v0.10*'
v0.10.0

创建标签

Git 使用两种主要类型的标签:轻量标签(lightweight)与附注标签(annotated)。

轻量标签很像一个不会改变的分支,它只是一个特定提交的引用。

附注标签是存储在 Git 数据库中的一个完整对象。它们是可以被校验的,其中包含打标签者的名字、电子邮件地址、日期时间;还有一个标签信息(即标签说明,跟提交说明一个道理);并且可以使用 GnuPG (GPG) 签名与验证。通常建议创建附注标签,这样你可以拥有以上所有信息,但是如果你只是想用一个临时的标签,或者因为某些原因不想要保存那些信息,轻量标签也是可用的。

附注标签

运行命令 git tag -a 创建附注标签。-s-u 可以打 GPG 签名标签,详见 git help tag

1
2
3
4
5
6
7
8
9
10
# 这次还是以 simplegit-progit 仓库为例
$ git clone https://github.com/schacon/simplegit-progit
...

$ cd simplegit-progit/

# 添加附注标签
# 默认是给最后一次提交加标签
# 选项 -m 与 git commit 的类似
$ git tag -a v1.4 -m 'my version 1.4'

运行命令 git show 查看标签信息与对应的提交信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 查看标签详细信息
$ git show v1.4
tag v1.4
Tagger: JasonZh <jasonz666@qq.com>
Date: Sun Dec 23 01:27:02 2018 +0800

my version 1.4

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

changed the verison number

diff --git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,7 +5,7 @@ require 'rake/gempackagetask'
spec = Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = "simplegit"
- s.version = "0.1.0"
+ s.version = "0.1.1"
s.author = "Scott Chacon"
s.email = "schacon@gmail.com"
s.summary = "A simple gem for using Git in Ruby code."

# 默认给最后一次提交打了附注标签
$ git log -1
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gmail.com>
Date: Mon Mar 17 21:52:11 2008 -0700

changed the verison number

轻量标签

直接运行命令 git tag <tag-name> 而不加选项可以添加轻量标签。轻量标签本质上是将提交校验和存储到一个文件中,并没有保存任何其他信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 打轻量标签
$ git tag v1.4-lw

# 这次的标签详情只是显示了提交信息
$ git show v1.4-lw
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gmail.com>
Date: Mon Mar 17 21:52:11 2008 -0700

changed the verison number

diff --git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,7 +5,7 @@ require 'rake/gempackagetask'
spec = Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = "simplegit"
- s.version = "0.1.0"
+ s.version = "0.1.1"
s.author = "Scott Chacon"
s.email = "schacon@gmail.com"
s.summary = "A simple gem for using Git in Ruby code."

后期打标签

运行命令 git tag -a <tag-name> <commit-hash>。其中,<commit-hash> 表示提交的校验和(或部分校验和)。

也就是给指定的提交打标签。比如,之前的一个提交忘记打标签了,就属于这种情况。

1
2
3
4
5
6
7
8
# 查看提交
$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 changed the verison number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test code
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit

# 给第二个提交打标签
$ git tag -a v1.2 085bb3b -m "my version 1.2"

共享标签

运行命令 git push origin [tagname] 或命令 git push origin --tags

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

1
2
3
4
5
6
7
8
$ 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

推送多个标签。这将会把所有不在远程仓库服务器上的标签全部传送到那里。

1
2
3
4
5
6
7
$ git push origin --tags
Counting objects: 1, done.
Writing objects: 100% (1/1), 160 bytes | 0 bytes/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To git@github.com:schacon/simplegit.git
* [new tag] v1.4 -> v1.4
* [new tag] v1.4-lw -> v1.4-lw

检出标签

详见 这里 –> 检出标签 –> 在 Git 中你并不能真的检出一个标签 …

Git 别名

使用命令 git config 可以为 Git 设置别名,避免每次都输入长长的命令。比如:

1
2
3
4
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status

这样,使用 git commit 时只需要输入 git ci。Git 的子命令可以设置别名,当然也可以把选项包括进来。比如:

1
2
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'

这样,运行 git unstage fileA 就能取消暂存 fileA 文件了。第二个命令设置了查看最后一次提交的别名。

其实 Git 只是简单地将别名替换为对应的命令。所以为了方便,还可以把外部命令绑定成 git <command> 的形式,只需要在命令前加 !号。比如把 git ls 定义成 ls -a 的别名:

1
git config --global alias.ls '!ls -a'

(完毕)