typora-copy-images-to: ......\md_pics

2 基本操作

Git 的基本操作都要由命令行完成,当然也有 GUI 方式的 Git 工具,是对某些命令的封。

[TOC]

2.1 获取 Git 仓库

在现有目录中初始化仓库

1
2
# 进入该项目,并执行一下命令
git init

该命令将创建一个名为 .git 的子目录,包含了初始化的 Git 仓库中所有的必须文件,是 Git 仓库的骨干。

接着通过 git add 命令来对指定文件进行跟踪,并将其放入暂存区域,执行 git commit 进行提交。

1
2
3
git add *.md

git commit -m "description of this commit"

克隆已有的仓库

Git 克隆的是该 Git ==仓库服务器上的几乎所有数据==,而不是仅仅复制完成你的工作所需要文件。 当你执行 git clone 命令的时候,默认配置下远程 Git 仓库中的每一个文件的每一个版本都将被拉取下来。 事实上,如果你的服务器的磁盘坏掉了,你通常可以使用任何一个克隆下来的用户端来重建服务器上的仓库。

1
2
3
4
# git clone [url] [local repository name]
git clone https://github.com/weirdWimp/gitpro-guide.git

#

支持多种数据传输协议。https://、git:// 或者 SSH 协议。

2.2 记录每次更新到仓库

仓库中每个文件的状态

Git 下文件生命周期图。

工作目录下的文件不外乎两种状态:已跟踪或未跟踪。已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录。工作一段时间后,状态可能处于 未修改 已修改 已暂存。 新建的文件属于未跟踪文件。初次克隆某个仓库有的时候,工作目录中所有文件都属于以跟踪文件,并处于未修改状态。

状态简览

1
2
3
4
5
6
7
8
# 查看当前文件的状态,显示当前所属的分支,反应每个文件是处于 已修改未暂存 还是 已暂存 或者 未追踪状态
git status


# 输出跟为简单的状态

git status -s
git status --short

新添加的未跟踪文件前面有 ?? 标记

新添加到暂存区中的文件前面有 A 标记

修改过的文件前面有 M 标记

M 有两个可以出现的位置,出现在右边的 M 表示该文件被修改了但是还没放入暂存区,出现在靠左边的 M 表示该文件被修改了并放入了暂存区。

例如,上面的状态报告显示: README 文件在工作区被修改了但是还没有将修改后的文件放入暂存区, lib/simplegit.rb 文件被修改了并将修改后的文件放入了暂存区。 而 Rakefile 在工作区被修改并提交到暂存区后又在工作区中被修改了,所以在暂存区和工作区都有该文件被修改了的记录。

追踪文件或将已修改文件放入暂存区

1
2
3
4
5
6
7
# git add 不仅可以用于追踪文件,还用于暂存文件

git add newFile #追踪文件并将其加入暂存区

git add modifiedFile #将已经修改的文件加入暂存区

# 还可以用于修改冲突,其实最好的解释是 git add 操作的是下次要提交的文件(保存文件快照,并生成索引?)

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

1
2
3
4
5
6
7
# 查看已修改文件和暂存区域文件的差异
git diff


# 查看暂存区域文件与最近的上一次提交的文件的差异
git diff --cached
git diff --staged

提交更新

1
2
3
4
5
# 将暂存区域的更新提交只版本库
git commit -m "description of commit"

# 给 git commit 加上 -a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤
git commit -a -m "description of commit"

移除文件

  • 先将文件从工作目录中删除,然后运行 git rm 记录此次移除文件的操作,也就是不在追踪此文件。之后再提交后,版本库里的文件也被移除掉了。
  • 如果要保留工作目录中的文件,即需要移除暂存区的文件,需要通过 git rm --cached READEME.md, 提交后,Git 就不再跟踪此文件,但工作目录中依然保留。

git rm 命令后面可以列出文件或者目录的名字,也可以使用 glob 模式。

1
2
3
4
5
git rm --cached log/\*.log

git rm \*~

# * 号之前添加反斜杠,是因为 Git 有自己的文件模式扩展匹配方式。

移动文件

移动文件和文件的重命名是一样的。

1
git mv fileFrom fileTo

相当于运行了下面的三条命令

1
2
3
mv README.md README
git rm README.md
git add README

忽略文件

有些文件不需要被 Git 管理,比如编译后生成的文件、日志文件、临时生成的文件。这时就可以创建一个名为 .gitignore 的文件。列出要忽略的文件模式。

1
2
3
4
5
6
7
# 创建一个 .gitignore 文件 
cat > .gitignore
# 忽略txt格式的文件
*.txt

# 忽略以~结尾的文件
*~

.gitignore 文件的格式规范如下:

  • 所有空行或者以 开头的行都会被 Git 忽略。

  • 可以使用标准的 glob 模式匹配。

  • 匹配模式可以以(/)开头防止递归。

  • 匹配模式可以以(/)结尾指定目录。

  • 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。

glob 模式是指使用简化了的正则表达式

* 表示匹配零个或多个任意字符

[abc ] 匹配方括号中的任意一个字符

[0-9] 范围

?匹配一个任意字符

使用两个星号 ** 匹配任意中间目录,比如 a/**/z 可以匹配 a/z a/b/z 或 a/b/c/z

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# no .a files
*.a

# but do track lib.a, even though you're ignoring .a files above
!lib.a

# only ignore the TODO file in the current directory, not subdir/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
doc/**/*.pdf

2.3 查看提交历史

在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。 完成这个任务最简单而又有效的工具是 git log 命令。

默认不用任何参数的话,git log 会按提交时间列出所有的更新,最近的更新排在最上面。这个命令会列出每个提交的 SHA-1 校验和、作者的名字和电子邮件地址、提交时间以及提交说明。

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
# 只显示提交的作者、时间信息和提交的注释说明
git log -1
Author: Guo <splendidmonkey@163.com>
Date: Fri May 18 13:17:10 2018 +0800

Modify revoke.md



# -p 参数,显示此次提交的具体信息,包括具体的行删除、增加、改动信息。
git log -p -1
Author: Guo <splendidmonkey@163.com>
Date: Fri May 18 13:17:10 2018 +0800

Modify revoke.md

diff --git a/revoke.md b/revoke.md
index 14df9a5..72b1f8d 100644
--- a/revoke.md
+++ b/revoke.md
@@ -1,3 +1,4 @@
revoke
## I am falling in love with you. My stunning girl.
** I [ Hate You! **
+I love you and I hate I love you.



# --stat 总结性参数选项,显示简略的统计信息。具体为哪个文件被修改,插入了几行,删除了几行,改动了几行
git log --stat -1
Author: Guo <splendidmonkey@163.com>
Date: Fri May 18 13:17:10 2018 +0800

Modify revoke.md

revoke.md | 1 +
1 file changed, 1 insertion(+)

默认内置格式显示

另外一个常用的选项是 --pretty。 这个选项可以指定使用不同于默认格式的方式展示提交历史。 这个选项有一些内建的子选项供你使用。 比如用 oneline 将每个提交放在一行显示,查看的提交数很大时非常有用。 另外还有 shortfullfuller 可以用,展示的信息或多或少有些不同,请自己动手实践一下看看效果如何。

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

格式化显示

可以定制显示的记录格式。

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

下表列出了常用的格式占位符写法及其代表的意义。

选项 说明
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 –date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明

作者指的是实际作出修改的人,提交者指的是最后将此工作成果提交到仓库的人。 所以,当你为某个项目发布补丁,然后某个核心成员将你的补丁并入项目时,你就是作者,而那个核心成员就是提交者。

当 oneline 或 format 与另一个 log 选项 --graph 结合使用时尤其有用。 这个选项添加了一些ASCII字符串来形象地展示你的分支、合并历史:

1
$ git log --pretty=format:"%h %s" --graph

以上只是简单介绍了一些 git log 命令支持的选项。

1
2
$ git log --pretty=format:"%ad ar - %s" --date=format:%Y-%m-%d 
# 指定日志打印的时间的格式,git log --help 获取多种预定义好的格式,如 --date=short
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
%a        Abbreviated weekday name
%A Full weekday name
%b Abbreviated month name
%B Full month name
%c Date and time representation appropriate for locale
%d Day of month as decimal number (01 – 31)
%H Hour in 24-hour format (00 – 23)
%I Hour in 12-hour format (01 – 12)
%j Day of year as decimal number (001 – 366)
%m Month as decimal number (01 – 12)
%M Minute as decimal number (00 – 59)
%p Current locale's A.M./P.M. indicator for 12-hour clock
%S Second as decimal number (00 – 59)
%U Week of year as decimal number, with Sunday as first day of week (00 – 53)
%w Weekday as decimal number (0 – 6; Sunday is 0)
%W Week of year as decimal number, with Monday as first day of week (00 – 53)
%x Date representation for current locale
%X Time representation for current locale
%y Year without century, as decimal number (00 – 99)
%Y Year with century, as decimal number
%z, %Z Either the time-zone name or time zone abbreviation, depending on registry settings; no characters if time zone is unknown
%% Percent sign

Git strftime 占位符原表出处

下面列出了我们目前涉及到的和没涉及到的选项,以及它们是如何影响 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(后跟指定格式)。

限制输出长度

Git 在输出所有提交时会自动调用分页程序,所以你一次只会看到一页的内容。

按照时间作限制的选项,比如 --since--until 也很有用。

1
2
3
4
5
# 下面的命令列出所有最近两周内的提交:

$ git log --since=2.weeks

# 这个命令可以在多种格式下工作,比如说具体的某一天 "2008-01-15",或者是相对地多久以前 "2 years 1 day 3 minutes ago"。

--author 选项显示指定作者的提交,用 --grep 选项搜索提交说明中的关键字。 (请注意,如果要得到同时满足这两个选项搜索条件的提交,就必须用 --all-match 选项。否则,满足任意一个条件的提交都会被匹配出来)

另一个非常有用的筛选选项是 -S,可以列出那些添加或移除了某些字符串的提交。 比如说,你想找出添加或移除了某一个特定函数的引用的提交,你可以这样使用:

1
$ git log -Sfunction_name

最后一个很实用的 git log 选项是路径(path), 如果只关心某些文件或者目录的历史提交,可以在 git log 选项的最后指定它们的路径。 因为是放在最后位置上的选项,所以用两个短划线(–)隔开之前的选项和后面限定的路径名。

常用的选项

选项 说明
-(n) 仅显示最近的 n 条提交
--since, --after 仅显示指定时间之后的提交。
--until, --before 仅显示指定时间之前的提交。
--author 仅显示指定作者相关的提交。
--committer 仅显示指定提交者相关的提交。
--grep 仅显示含指定关键字的提交
-S 仅显示添加或移除了某个关键字的提交

来看一个实际的例子,如果要查看 Git 仓库中,2008 年 10 月期间,Junio Hamano 提交的但未合并的测试文件,可以用下面的查询命令:

1
2
3
4
5
6
7
8
$ git log --pretty="%h - %s" --author=gitster --since="2008-10-01" \
--before="2008-11-01" --no-merges -- t/
5610e3b - Fix testcase failure when extended attributes are in use
acd3b9e - Enhance hold_lock_file_for_{update,append}() API
f563754 - demonstrate breakage of detached checkout with symbolic link HEAD
d1a43f2 - reset --hard/read-tree --reset -u: remove unmerged new paths
51a94af - Fix "checkout --track -b newbranch" on detached HEAD
b0ad11e - pull: allow "git pull origin $something:$current_branch" into an unborn branch

在近 40000 条提交中,上面的输出仅列出了符合条件的 6 条记录。

2.4 撤销操作

补充提交

如果在一次提交完成之后发现有些文件还没有被添加,或者提交信息写错了。此时可以通过以下命令尝试重新提交:

1
git commit --amend

这个命令会将添加进暂存区的文件再次提交。如果上次提交后立马执行了此命令,那么修改的只是提交信息。

1
2
3
4
git commit -m "Add files."

git commit --amend -m "Add two files"
# 最终结果将只有一次提交,并且只将提交信息改为 Add two files.
1
2
3
4
5
git commit -m 'Add files'
git add forgotten_file
git commit --amend -m "Add files"

# 被忘记提交的文件与上一次提交的文件作为一次提交。

取消暂存的文件

如过不想同时暂存两个文件,但又通过命令 git add * 暂存了所有的文件,如何撤销已经暂存的另一个文件。 git 在 git status 中一般有相应的提示。

1
2
3
git reset HEAD second.md

# 执行完该命令后, second.md 文件已经变为未暂存的状态了。

撤销对文件的修改

如果对该文件的修改要退回到上次提交后的状态。 git status 中同样有所提示。

1
2
3
git checkout -- file

# 这个命令应该是重新从版本库中重新获取了该文件并进行了替换。

2.5 远程仓库

远程仓库的几个命令

进行远程仓库的克隆

1
git clone repository_url

从远程仓库中进行抓取,本地库被更新为最新的版本

1
git fetch [remote-name]

从远程仓库中进行来进行拉取,并尝试合并到当前所在的分支

1
git pull [remote-name]

推送到远程仓库中相应的分支中

1
git push [remote-name] [branch-name]

添加远程仓库

1
git remote add [remote-name] [remote-url]

查看远程仓库的别名

1
git remote

查看远程仓库的别名与远程库链接

1
git remote -v

查看远程仓库的具体信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
git remote show [remote-name]

$ git remote show origin
* remote origin
Fetch URL: https://github.com/schacon/ticgit
Push URL: https://github.com/schacon/ticgit
HEAD branch: master
Remote branches:
master tracked
dev-branch tracked
Local branch configured for 'git pull':
master merges with remote master
Local ref configured for 'git push':
master pushes to master (up to date)

一般的流程是

1
git fetch ---> git add ---> git commit ---> git push

2.6 打标签

给历史提交打上标签,来标记重要的提交节点或重大的功能完成节点。

列出已有的标签

1
2
3
4
5
git tag


# 列出特定模式的标签
git tag -l 'v1.8.5*'

创建标签

Git 中有两种标签:lightweight - 轻量标签; annotated - 附注标签

一个轻量标签只是一个特定提交的引用。

一个附注标签是存储在 Git 数据库中的一个完整对象,包括打标签作者的名字、电子邮件地址、日期时间、标签信息,可以使用 GNU Privacy Guard (GPG) 签名与验证。

通常建议创建附注标签,这样可以拥有很多信息。

  • 附注标签
1
2
3
4
5
6
7
git tag -a v1.0 -m 'version 1.0'

# -m 制定了该标签的详细描述,没有指定的话,Git 会运行编辑器要求你输入信息。

git show v1.0

# 可以看到标签信息与对应的提交信息
  • 轻量标签

    轻量标签本质上是提交校验和存储到一个文件中 - 没有保存任何其他信息。只需要提供标签名字就可以创建轻量标签。

1
2
3
4
5
6
git tag v1.0-lightweight

git show v1.0-lightweight

# 不会看到额外的标签信息。命令之后会显示出提交信息。
# 这个在 git version 2.16.2.windows.1 中为什么还是会显示提交信息?

如果前面的某一次提交忘记打标签了,可以在之后补上标签。需要在命令的末尾指定提交的校验和(部分校验和)。

1
2
3
4
5
6
7
8
9
git log --pretty=oneline

34874876b494a234ca52790d6e9cd85a47927953 (HEAD -> master, tag: v1.0-lightweight, tag: v1.0, origin/master) Add something to revoke.md
c3176ae309d942cb495257ad457172e244da21d9 Add test line to revoke.md
5969cb43806b3a632fe7b3cb6ab0201be49d3801 Add markdown notes.
c4331a9d271ee5516976a54a42ed282d9fb82d42 Initial commit


git tag -a v0.9 5969cb -m "version 0.9"
  • 共享标签至远程库

    创建完标签,必须显示地推送标签到共享服务器上。

1
2
3
4
5
6
7
8
# git push origin [tagname]

git push origin v1.0


# 一次性推送很多标签, 会把所有不在远程仓库服务器上的标签全部传送到那里。当被人从仓库中克隆或拉去,也能得到相应的标签。

git push origin --tags
  • 检出标签

    在 Git 中你并不能真的检出一个标签,因为它们并不能像分支一样来回移动。 如果你想要工作目录与仓库中特定的标签版本完全一样,可以使用 git checkout -b [branchname] [tagname] 在特定的标签上创建一个新分支:

    1
    2
    $ git checkout -b version2 v2.0.0
    Switched to a new branch 'version2'

    当然,如果在这之后又进行了一次提交,version2 分支会因为改动向前移动了,那么 version2 分支就会和 v2.0.0 标签稍微有些不同,这时就应该当心了。

2.7 别名

Git 可以通过别名的方式简化命令,为命令设置一个别名,通过别名就可完成相应的命令。别名一般从字面上就可以看出该命令的执行作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.st status

# 当要输入 git commit 时,只需要输入 git ci 就可以了。


git config --global alias.unstage 'reset HEAD --'
# 下面两个命令是等价的
git unstage file
git reset HEAD -- file


# 显示最近一次提交的日志信息
git config --global alias.last 'log -1 HEAD'

可以看出,Git 只是简单地将别名替换为对应的命令。 然而,你可能想要执行外部命令,而不是一个 Git 子命令。 如果是那样的话,可以在命令前面加入 ! 符号。 如果你自己要写一些与 Git 仓库协作的工具的话,那会很有用。 我们现在演示将 git visual 定义为 gitk 的别名:

1
git config --global alias.visual '!gitk'