0%

Git 使用 命令

git

Git 简介

原文链接Git 教程

Git 是什么?

Git 是目前世界上最先进的分布式版本控制系统(没有之一)。

Git 有什么特点?简单来说就是:高端大气上档次!

那什么是版本控制系统?

如果你用 Microsoft Word 写过长篇大论,那你一定有这样的经历:

想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件“另存为……”一个新的 Word 文件,再接着改,改到一定程度,再“另存为……”一个新文件,这样一直改下去,最后你的 Word 文档变成了这样:

https://raw.githubusercontent.com/duanzhaoqian/pic/master/git/1.jpg

过了一周,你想找回被删除的文字,但是已经记不清删除前保存在哪个文件里了,只好一个一个文件去找,真麻烦。

看着一堆乱七八糟的文件,想保留最新的一个,然后把其他的删掉,又怕哪天会用上,还不敢删,真郁闷。

更要命的是,有些部分需要你的财务同事帮助填写,于是你把文件 Copy 到 U 盘里给她(也可能通过 Email 发送一份给她),然后,你继续修改 Word 文件。一天后,同事再把 Word 文件传给你,此时,你必须想想,发给她之后到你收到她的文件期间,你作了哪些改动,得把你的改动和她的部分合并,真困难。

于是你想,如果有一个软件,不但能自动帮我记录每次文件的改动,还可以让同事协作编辑,这样就不用自己管理一堆类似的文件了,也不需要把文件传来传去。如果想查看某次改动,只需要在软件里瞄一眼就可以,岂不是很方便?

这个软件用起来就应该像这个样子,能记录每次文件的改动:

版本 用户 说明 日期
1 张三 删除了软件服务条款 5 7/12 10:38
2 张三 增加了 License 人数限制 7/12 18:09
3 李四 财务部门调整了合同金额 7/13 9:51
4 张三 延长了免费升级周期 7/14 15:17

这样,你就结束了手动管理多个“版本”的史前时代,进入到版本控制的 20 世纪。

Git 的诞生

很多人都知道,Linus 在 1991 年创建了开源的 Linux,从此,Linux 系统不断发展,已经成为最大的服务器系统软件了。

Linus 虽然创建了 Linux,但 Linux 的壮大是靠全世界热心的志愿者参与的,这么多人在世界各地为 Linux 编写代码,那 Linux 的代码是如何管理的呢?

事实是,在 2002 年以前,世界各地的志愿者把源代码文件通过 diff 的方式发给 Linus,然后由 Linus 本人通过手工方式合并代码!

你也许会想,为什么 Linus 不把 Linux 代码放到版本控制系统里呢?不是有 CVS、SVN 这些免费的版本控制系统吗?因为 Linus 坚定地反对 CVS 和 SVN,这些集中式的版本控制系统不但速度慢,而且必须联网才能使用。有一些商用的版本控制系统,虽然比 CVS、SVN 好用,但那是付费的,和 Linux 的开源精神不符。

不过,到了 2002 年,Linux 系统已经发展了十年了,代码库之大让 Linus 很难继续通过手工方式管理了,社区的弟兄们也对这种方式表达了强烈不满,于是 Linus 选择了一个商业的版本控制系统 BitKeeper,BitKeeper 的东家 BitMover 公司出于人道主义精神,授权 Linux 社区免费使用这个版本控制系统。

安定团结的大好局面在 2005 年就被打破了,原因是 Linux 社区牛人聚集,不免沾染了一些梁山好汉的江湖习气。开发 Samba 的 Andrew 试图破解 BitKeeper 的协议(这么干的其实也不只他一个),被 BitMover 公司发现了(监控工作做得不错!),于是 BitMover 公司怒了,要收回 Linux 社区的免费使用权。

Linus 可以向 BitMover 公司道个歉,保证以后严格管教弟兄们,嗯,这是不可能的。实际情况是这样的:

Linus 花了两周时间自己用 C 写了一个分布式版本控制系统,这就是 Git!一个月之内,Linux 系统的源码已经由 Git 管理了!牛是怎么定义的呢?大家可以体会一下。

Git 迅速成为最流行的分布式版本控制系统,尤其是 2008 年,GitHub 网站上线了,它为开源项目免费提供 Git 存储,无数开源项目开始迁移至 GitHub,包括 jQuery,PHP,Ruby 等等。

历史就是这么偶然,如果不是当年 BitMover 公司威胁 Linux 社区,可能现在我们就没有免费而超级好用的 Git 了。

集中式 vs 分布式

Linus 一直痛恨的 CVS 及 SVN 都是集中式的版本控制系统,而 Git 是分布式版本控制系统,集中式和分布式版本控制系统有什么区别呢?

先说集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。

https://raw.githubusercontent.com/duanzhaoqian/pic/master/git/2.jpg

集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网内还好,带宽够大,速度够快,可如果在互联网上,遇到网速慢的话,可能提交一个 10M 的文件就需要 5 分钟,这还不得把人给憋死啊。

那分布式版本控制系统与集中式版本控制系统有何不同呢?首先,分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说你在自己电脑上改了文件 A,你的同事也在他的电脑上改了文件 A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。

和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。

在实际使用分布式版本控制系统的时候,其实很少在两人之间的电脑上推送版本库的修改,因为可能你们俩不在一个局域网内,两台电脑互相访问不了,也可能今天你的同事病了,他的电脑压根没有开机。因此,分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。

https://raw.githubusercontent.com/duanzhaoqian/pic/master/git/3.jpg

当然,Git 的优势不单是不必联网这么简单,后面我们还会看到 Git 极其强大的分支管理,把 SVN 等远远抛在了后面。

CVS 作为最早的开源而且免费的集中式版本控制系统,直到现在还有不少人在用。由于 CVS 自身设计的问题,会造成提交文件不完整,版本库莫名其妙损坏的情况。同样是开源而且免费的 SVN 修正了 CVS 的一些稳定性问题,是目前用得最多的集中式版本库控制系统。

除了免费的外,还有收费的集中式版本控制系统,比如 IBM 的 ClearCase(以前是 Rational 公司的,被 IBM 收购了),特点是安装比 Windows 还大,运行比蜗牛还慢,能用 ClearCase 的一般是世界 500 强,他们有个共同的特点是财大气粗,或者人傻钱多。

微软自己也有一个集中式版本控制系统叫 VSS,集成在 Visual Studio 中。由于其反人类的设计,连微软自己都不好意思用了。

分布式版本控制系统除了 Git 以及促使 Git 诞生的 BitKeeper 外,还有类似 Git 的 Mercurial 和 Bazaar 等。这些分布式版本控制系统各有特点,但最快、最简单也最流行的依然是 Git!

工作区和暂存区(非常重要)

https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/0013745374151782eb658c5a5ca454eaa451661275886c6000

Git 和其他版本控制系统如 SVN 的一个不同之处就是有暂存区的概念。

先来看名词解释。

工作区(Working Directory)

就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工作区:

https://raw.githubusercontent.com/duanzhaoqian/pic/master/git/4.png)

版本库(Repository)

工作区有一个隐藏目录.git,这个不算工作区,而是 Git 的版本库。

Git 的版本库里存了很多东西,其中最重要的就是称为 stage(或者叫 index)的暂存区,还有 Git 为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD

https://raw.githubusercontent.com/duanzhaoqian/pic/master/git/5.jpg

分支和HEAD的概念我们以后再讲。

前面讲了我们把文件往 Git 版本库里添加的时候,是分两步执行的:

第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;

第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。

因为我们创建 Git 版本库时,Git 自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。

你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。

俗话说,实践出真知。现在,我们再练习一遍,先对readme.txt做个修改,比如加上一行内容:

1
2
3
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.

然后,在工作区新增一个LICENSE文本文件(内容随便写)。

先用git status查看一下状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ git status
# On branch 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.txt
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# LICENSE
no changes added to commit (use "git add" and/or "git commit -a")

Git 非常清楚地告诉我们,readme.txt被修改了,而LICENSE还从来没有被添加过,所以它的状态是Untracked

现在,使用两次命令git add,把readme.txtLICENSE都添加后,用git status再查看一下:

1
2
3
4
5
6
7
8
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: LICENSE
# modified: readme.txt
#

现在,暂存区的状态就变成这样了:

https://raw.githubusercontent.com/duanzhaoqian/pic/master/git/6.jpg

所以,git add命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支。

1
2
3
4
$ git commit -m "understand how stage works"
[master 27c9860] understand how stage works
2 files changed, 675 insertions(+)
create mode 100644 LICENSE

一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:

1
2
3
$ git status
# On branch master
nothing to commit (working directory clean)

现在版本库变成了这样,暂存区就没有任何内容了:

https://raw.githubusercontent.com/duanzhaoqian/pic/master/git/7.jpg

https://stackoverflow.com/questions/2304087/what-is-head-in-git

你可以认为 HEAD(大写)是”current branch”(当下的分支)。当你用 git checkout 切换分支的时候,HEAD 修订版本重新指向新的分支。有的时候 HEAD 会指向一个没有分支名字的修订版本,这种情况叫”detached HEAD

head(小写)是 commit 对象的引用,每个 head 都有一个名字(分支名字或者标签名字等等),但是默认情况下,每个叫 master 的 repository 都会有一个 head, 一个 repository 可以包含任意数量的 head。在任何时候,只要这个 head 被选择成为”current head“,那么这个 head 就成了 HEAD,总是大写

1
2
cat .git/HEAD
ref: refs/heads/master

在版本回退里,你已经知道,每次提交,Git 都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在 Git 里,这个分支叫主分支,即 master 分支。HEAD 严格来说不是指向提交,而是指向 master,master 才是指向提交的,所以,HEAD 指向的就是当前分支。

一开始的时候,master 分支是一条线,Git 用 master 指向最新的提交,再用 HEAD 指向 master,就能确定当前分支,以及当前分支的提交点:

https://raw.githubusercontent.com/duanzhaoqian/pic/master/git/8. png)

当我们创建新的分支,例如 dev 时,Git 新建了一个指针叫 dev,指向 master 相同的提交,再把 HEAD 指向 dev,就表示当前分支在 dev 上:

https://raw.githubusercontent.com/duanzhaoqian/pic/master/git/9. png

你看,Git 创建一个分支很快,因为除了增加一个 dev 指针,改改 HEAD 的指向,工作区的文件都没有任何变化!

不过,从现在开始,对工作区的修改和提交就是针对 dev 分支了,比如新提交一次后,dev 指针往前移动一步,而 master 指针不变:

https://raw.githubusercontent.com/duanzhaoqian/pic/master/git/10. png

假如我们在 dev 上的工作完成了,就可以把 dev 合并到 master 上。Git 怎么合并呢?最简单的方法,就是直接把 master 指向 dev 的当前提交,就完成了合并

https://raw.githubusercontent.com/duanzhaoqian/pic/master/git/11.png

所以 Git 合并分支也很快!就改改指针,工作区内容也不变!

合并完分支后,甚至可以删除 dev 分支。删除 dev 分支就是把 dev 指针给删掉,删掉后,我们就剩下了一条 master 分支:

https://raw.githubusercontent.com/duanzhaoqian/pic/master/git/12.png

关于 Git HEAD^与 HEAD~的关系

https://stackoverflow.com/questions/2221658/whats-the-difference-between-head-and-head-in-git

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
G   H   I   J
\ / \ /
D E F
\ | / \
\ | / |
\|/ |
B C
\ /
\ /
A
A = = A^0
B = A^ = A^1 = A~1
C = A^2 = A^2
D = A^^ = A^1^1 = A~2
E = B^2 = A^^2
F = B^3 = A^^3
G = A^^^ = A^1^1^1 = A~3
H = D^2 = B^^2 = A^^^2 = A~2^2
I = F^ = B^3^ = A^^3^
J = F^2 = B^3^2 = A^^3^2

G-D-B-A 可以认为是主干,其他都是 merge 进来的其他分支节点。

git config

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
git config
usage: git config [<options>]

Config file location
--global use global config file
--system use system config file
--local use repository config file
-f, --file <file> use given config file
--blob <blob-id> read config from given blob object

Action
--get get value: name [value-regex]
--get-all get all values: key [value-regex]
--get-regexp get values for regexp: name-regex [value-regex]
--get-urlmatch get value specific for the URL: section[.var] URL
--replace-all replace all matching variables: name value [value_regex]
--add add a new variable: name value
--unset remove a variable: name [value-regex]
--unset-all remove all matches: name [value-regex]
--rename-section rename section: old-name new-name
--remove-section remove a section: name
-l, --list list all
-e, --edit open an editor
--get-color find the color configured: slot [default]
--get-colorbool find the color setting: slot [stdout-is-tty]

Type
--bool value is "true" or "false"
--int value is decimal number
--bool-or-int value is --bool or --int
--path value is a path (file or directory name)

Other
-z, --null terminate values with NUL byte
--name-only show variable names only
--includes respect include directives on lookup

查看所有配置

1
git config -l

查看仓库级配置(文件默认位置在.git/config)

1
git config --local -l

查看用户级配置(文件默认在~/.gitconfig)

1
git config --global -l

查看系统级配置(文件默认在”git 所在目录/etc/gitconfig”,本机为/usr/local/homebrew/etc/gitconfig)

1
git config --system -l

编辑配置文件(默认编辑仓库级文件)

1
git config -e

编辑用户级文件

1
git config --global -e

编辑系统级文件

1
git config --system -e

设置用户级用户名 邮箱(建议操作)

1
2
git config --global user.name "duanzhaoqian"
git config --global user.email "[email protected]"

查看详细帮助文档及 config 文件参数大全

1
git help config

git init

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
git init --help
NAME
git-init - Create an empty Git repository or reinitialize an existing one

SYNOPSIS
git init [-q | --quiet] [--bare] [--template=<template_directory>]
[--separate-git-dir <git dir>]
[--shared[=<permissions>]] [directory]

DESCRIPTION
This command creates an empty Git repository - basically a .git directory with subdirectories for objects, refs/heads, refs/tags, and template files. An
initial HEAD file that references the HEAD of the master branch is also created.

If the $GIT_DIR environment variable is set then it specifies a path to use instead of ./.git for the base of the repository.

If the object storage directory is specified via the $GIT_OBJECT_DIRECTORY environment variable then the sha1 directories are created underneath -
otherwise the default $GIT_DIR/objects directory is used.

Running git init in an existing repository is safe. It will not overwrite things that are already there. The primary reason for rerunning git init is to
pick up newly added templates (or to move the repository to another place if --separate-git-dir is given).

OPTIONS
-q, --quiet
Only print error and warning messages; all other output will be suppressed.

--bare
Create a bare repository. If GIT_DIR environment is not set, it is set to the current working directory.

--template=<template_directory>
Specify the directory from which templates will be used. (See the "TEMPLATE DIRECTORY" section below.)

--separate-git-dir=<git dir>
Instead of initializing the repository as a directory to either $GIT_DIR or ./.git/, create a text file there containing the path to the actual
repository. This file acts as filesystem-agnostic Git symbolic link to the repository.

If this is reinitialization, the repository will be moved to the specified path.

--shared[=(false|true|umask|group|all|world|everybody|0xxx)]
Specify that the Git repository is to be shared amongst several users. This allows users belonging to the same group to push into that repository.
When specified, the config variable "core.sharedRepository" is set so that files and directories under $GIT_DIR are created with the requested
permissions. When not specified, Git will use permissions reported by umask(2).

The option can have the following values, defaulting to group if no value is given:

umask (or false)
Use permissions reported by umask(2). The default, when --shared is not specified.

group (or true)
Make the repository group-writable, (and g+sx, since the git group may be not the primary group of all users). This is used to loosen the
permissions of an otherwise safe umask(2) value. Note that the umask still applies to the other permission bits (e.g. if umask is 0022, using
group will not remove read privileges from other (non-group) users). See 0xxx for how to exactly specify the repository permissions.

all (or world or everybody)
Same as group, but make the repository readable by all users.

0xxx
0xxx is an octal number and each file will have mode 0xxx. 0xxx will override users' umask(2) value (and not only loosen permissions as group and
all does). 0640 will create a repository which is group-readable, but not group-writable or accessible to others. 0660 will create a repo that
is readable and writable to the current user and group, but inaccessible to others.

By default, the configuration flag receive.denyNonFastForwards is enabled in shared repositories, so that you cannot force a non fast-forwarding push into
it.

If you provide a directory, the command is run inside it. If this directory does not exist, it will be created.
TEMPLATE DIRECTORY
The template directory contains files and directories that will be copied to the $GIT_DIR after it is created.

The template directory will be one of the following (in order):

o the argument given with the --template option;

o the contents of the $GIT_TEMPLATE_DIR environment variable;

o the init.templateDir configuration variable; or

o the default template directory: /usr/share/git-core/templates.

The default template directory includes some directory structure, suggested "exclude patterns" (see gitignore(5)), and sample hook files (see
githooks(5)).

EXAMPLES
Start a new Git repository for an existing code base

$ cd /path/to/my/codebase
$ git init (1)
$ git add . (2)
$ git commit (3)

1. Create a /path/to/my/codebase/.git directory.
2. Add all existing files to the index.
3. Record the pristine state as the first commit in the history.

GIT
Part of the git(1) suite

Git 2.6.4 12/08/2015 GIT-INIT(1)

bare 参数

1
git init --bare 创建一个无本地分支的库 当需要一个公用的中央仓库时,适合建为bare库
1
git init abc 创建abc目录并且abc目录下生成.git 文件夹
1
git init --bare abd (bare 空的) 创建abd目录并且abd目录下无.git文件夹,只有原来.git目录中的文件

使用–bare 创建的仓库执行 git status 等操作出现 fatal: This operation must be run in a work tree

更改默认.git 目录

1
2
export GIT_DIR=aa
echo $GIT_DIR

gitignore

查看详细文档

1
git help ignore

三个 ignore 路径

1
$HOME/.config/git/ignore, $GIT_DIR/info/exclude, .gitignore

.gitignore 提交到 git,大家都使用的 ignore

$HOME/.config/git/ignore 用户级 ignore(所有仓库都忽略)

$GIT_DIR/info/exclude 只有当前仓库忽略(且只有自己使用),因为.git目录往往不提交到git,$GIT_DIR 默认为.git 目录

git add

1
2
3
4
5
6
7
8
9
10
11
git add somefile.txt 添加单个文件

git add *.txt 添加所有txt文件

git add */*.txt 子目录的txt文件(不递归)

git add **/*.txt 效果同 */*.txt

git add . 添加所有文件 不包括空目录

git add -u 只添加修改的文件 不添加新增的文件
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
NAME
git-add - Add file contents to the index

SYNOPSIS
git add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p]
[--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]]
[--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing]
[--] [<pathspec>...]

DESCRIPTION
This command updates the index using the current content found in the working tree, to prepare the content staged for the next commit. It typically adds
the current content of existing paths as a whole, but with some options it can also be used to add content with only part of the changes made to the
working tree files applied, or remove paths that do not exist in the working tree anymore.

The "index" holds a snapshot of the content of the working tree, and it is this snapshot that is taken as the contents of the next commit. Thus after
making any changes to the working directory, and before running the commit command, you must use the add command to add any new or modified files to the
index.

This command can be performed multiple times before a commit. It only adds the content of the specified file(s) at the time the add command is run; if you
want subsequent changes included in the next commit, then you must run git add again to add the new content to the index.

The git status command can be used to obtain a summary of which files have changes that are staged for the next commit.

The git add command will not add ignored files by default. If any ignored files were explicitly specified on the command line, git add will fail with a
list of ignored files. Ignored files reached by directory recursion or filename globbing performed by Git (quote your globs before the shell) will be
silently ignored. The git add command can be used to add ignored files with the -f (force) option.

Please see git-commit(1) for alternative ways to add content to a commit.

git commit

1
2
3
4
5
6
7
8
9
10
11
12
13
git commit -m '提交信息' 提交变化

git commit -m "" -a 提交所有变化

git commit -m "" a.txt 提交单个文件

git commit -m "" *.txt 提交txt所有文件

git commit -m "" */*.txt 子目录txt文件(不递归)

git commit -C head -a --amend 增补提交 产生新提交id,覆盖上次提交,使用head message,提交时间不变化

git commit --amend 修改上次提交信息 ,不是基于上次提交id做修改,而是产生新的提交id,提交时间不变化
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-COMMIT(1)                                                               Git Manual                                                              GIT-COMMIT(1)

NAME
git-commit - Record changes to the repository

SYNOPSIS
git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]
[--dry-run] [(-c | -C | --fixup | --squash) <commit>]
[-F <file> | -m <msg>] [--reset-author] [--allow-empty]
[--allow-empty-message] [--no-verify] [-e] [--author=<author>]
[--date=<date>] [--cleanup=<mode>] [--[no-]status]
[-i | -o] [-S[<keyid>]] [--] [<file>...]

DESCRIPTION
Stores the current contents of the index in a new commit along with a log message from the user describing the changes.

The content to be added can be specified in several ways:

1. by using git add to incrementally "add" changes to the index before using the commit command (Note: even modified files must be "added");

2. by using git rm to remove files from the working tree and the index, again before using the commit command;

3. by listing files as arguments to the commit command, in which case the commit will ignore changes staged in the index, and instead record the current
content of the listed files (which must already be known to Git);

4. by using the -a switch with the commit command to automatically "add" changes from all known files (i.e. all files that are already listed in the
index) and to automatically "rm" files in the index that have been removed from the working tree, and then perform the actual commit;

5. by using the --interactive or --patch switches with the commit command to decide one by one which files or hunks should be part of the commit, before
finalizing the operation. See the "Interactive Mode" section of git-add(1) to learn how to operate these modes.

The --dry-run option can be used to obtain a summary of what is included by any of the above for the next commit by giving the same set of parameters
(options and paths).

If you make a commit and then find a mistake immediately after that, you can recover from it with git reset.

git mv

git mv 重命名文件

1
2
3
相当于
mv 3.txt 4.txt
git add .
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
GIT-MV(1)                                                                   Git Manual                                                                  GIT-MV(1)

NAME
git-mv - Move or rename a file, a directory, or a symlink

SYNOPSIS
git mv <options>... <args>...

DESCRIPTION
Move or rename a file, directory or symlink.

git mv [-v] [-f] [-n] [-k] <source> <destination>
git mv [-v] [-f] [-n] [-k] <source> ... <destination directory>

In the first form, it renames <source>, which must exist and be either a file, symlink or directory, to <destination>. In the second form, the last
argument has to be an existing directory; the given sources will be moved into this directory.

The index is updated after successful completion, but the change must still be committed.

OPTIONS
-f, --force
Force renaming or moving of a file even if the target exists

-k
Skip move or rename actions which would lead to an error condition. An error happens when a source is neither existing nor controlled by Git, or when
it would overwrite an existing file unless -f is given.

-n, --dry-run
Do nothing; only show what would happen

-v, --verbose
Report the names of files as they are moved.

git rm

git rm 删除文件

1
2
3
4
相当于
rm
git add .
一起操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
GIT-RM(1)                                                                   Git Manual                                                                  GIT-RM(1)

NAME
git-rm - Remove files from the working tree and from the index

SYNOPSIS
git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...

DESCRIPTION
Remove files from the index, or from the working tree and the index. git rm will not remove a file from just your working directory. (There is no option
to remove a file only from the working tree and yet keep it in the index; use /bin/rm if you want to do that.) The files being removed have to be
identical to the tip of the branch, and no updates to their contents can be staged in the index, though that default behavior can be overridden with the
-f option. When --cached is given, the staged content has to match either the tip of the branch or the file on disk, allowing the file to be removed from
just the index.

git checkout

git checkout 两个功能

  1. 撤消工作区的修改(不在暂存区的文件)

    1
    2
    3
    4
    5
    git checkout -- 1.txt 撤消指定文件修改
    git checkout head 1.txt 撤消指定文件修改
    git checkout 1.txt 撤消指定文件修改
    git checkout [head] *.txt 撤消所有txt文件修改
    git checkout [head] . 撤消所有文件修改
  2. 分支操作

    1
    2
    3
    4
    git checkout <name>	切换分支
    git checkout <tagName> 检出标签
    git checkout -b <name> 创建+切换分支
    git checkout -b <branchName> <tagName> 由标签创建分支

git revert

反转提交

1
2
3
4
5
git revert --no-commit head 反转提交但不提交,相当于最近一次提交的反操作(文件处于暂存区中)
git revert --abort 终止revert(终止暂存区中的revert)
git revert --continue 继续revert提交(继续暂存区中的revert)

git revert head 直接反转并提交
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GIT-REVERT(1)                                                               Git Manual                                                              GIT-REVERT(1)

NAME
git-revert - Revert some existing commits

SYNOPSIS
git revert [--[no-]edit] [-n] [-m parent-number] [-s] [-S[<keyid>]] <commit>...
git revert --continue
git revert --quit
git revert --abort

DESCRIPTION
Given one or more existing commits, revert the changes that the related patches introduce, and record some new commits that record them. This requires
your working tree to be clean (no modifications from the HEAD commit).

Note: git revert is used to record some new commits to reverse the effect of some earlier commits (often only a faulty one). If you want to throw away all
uncommitted changes in your working directory, you should see git-reset(1), particularly the --hard option. If you want to extract specific files as they
were in another commit, you should see git-checkout(1), specifically the git checkout <commit> -- <filename> syntax. Take care with these alternatives as
both will discard uncommitted changes in your working directory.

git reset

1
2
3
4
git reset head <fileName> 取消暂存区中的文件,保留本地已经存在的文件修改
git reset --hard <id> 重置到指定提交id (不会在版本库留下痕迹)注意 会完全丢失当前分支变更的文件
git reset --hard head^ 重置到上一版本 (不建议使用)
git reset --hard head^^ 重置到上一上一版本
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
52
53
54
55
56
57
58
59
60
61
62
63
GIT-RESET(1)                                                                Git Manual                                                               GIT-RESET(1)

NAME
git-reset - Reset current HEAD to the specified state

SYNOPSIS
git reset [-q] [<tree-ish>] [--] <paths>...
git reset (--patch | -p) [<tree-ish>] [--] [<paths>...]
git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]

DESCRIPTION
In the first and second form, copy entries from <tree-ish> to the index. In the third form, set the current branch head (HEAD) to <commit>, optionally
modifying index and working tree to match. The <tree-ish>/<commit> defaults to HEAD in all forms.

git reset [-q] [<tree-ish>] [--] <paths>...
This form resets the index entries for all <paths> to their state at <tree-ish>. (It does not affect the working tree or the current branch.)

This means that git reset <paths> is the opposite of git add <paths>.

After running git reset <paths> to update the index entry, you can use git-checkout(1) to check the contents out of the index to the working tree.
Alternatively, using git-checkout(1) and specifying a commit, you can copy the contents of a path out of a commit to the index and to the working tree
in one go.

git reset (--patch | -p) [<tree-ish>] [--] [<paths>...]
Interactively select hunks in the difference between the index and <tree-ish> (defaults to HEAD). The chosen hunks are applied in reverse to the
index.

This means that git reset -p is the opposite of git add -p, i.e. you can use it to selectively reset hunks. See the "Interactive Mode" section of git-
add(1) to learn how to operate the --patch mode.

git reset [<mode>] [<commit>]
This form resets the current branch head to <commit> and possibly updates the index (resetting it to the tree of <commit>) and the working tree
depending on <mode>. If <mode> is omitted, defaults to "--mixed". The <mode> must be one of the following:

--soft
Does not touch the index file or the working tree at all (but resets the head to <commit>, just like all modes do). This leaves all your changed
files "Changes to be committed", as git status would put it.

--mixed
Resets the index but not the working tree (i.e., the changed files are preserved but not marked for commit) and reports what has not been updated.
This is the default action.

If -N is specified, removed paths are marked as intent-to-add (see git-add(1)).

--hard
Resets the index and working tree. Any changes to tracked files in the working tree since <commit> are discarded.

--merge
Resets the index and updates the files in the working tree that are different between <commit> and HEAD, but keeps those which are different
between the index and working tree (i.e. which have changes which have not been added). If a file that is different between <commit> and the index
has unstaged changes, reset is aborted.

In other words, --merge does something like a git read-tree -u -m <commit>, but carries forward unmerged index entries.

--keep
Resets index entries and updates files in the working tree that are different between <commit> and HEAD. If a file that is different between
<commit> and HEAD has local changes, reset is aborted.

If you want to undo a commit other than the latest on a branch, git-revert(1) is your friend.

OPTIONS
-q, --quiet
Be quiet, only report errors.

git branch

1
2
3
4
5
6
7
8
9
10
11
git branch 列出本地分支
git branch -a 列出所有分支
git branch -r 列出远程分支
git branch <branchName> 基于当前分支的末支(HEAD ?)创建新分支
git branch <branchName> <commitId|tagId|branchName> 基本其次提交|标签|分支创建分支
git branch -m <branchName> <newName> 重命名分支 不会覆盖已存在的同名分支
git branch -M <branchName> <newName> 会覆盖已存在的同名分支
git branch -d <branchName> 如果分支没有合并会删除失败
git branch -D <branchName> 即使没有被合并也能删除
git branch -v 查看每个分支最后提交
git branch -u <remote branchName> <local branchName> 追踪本地分支到远程分支
1
2
3
4
5
6
7
8
9
10
git branch -u origin/master master
Branch master set up to track remote branch master from origin.

config file
[remote "origin"] (添加远程仓库多出此节点)
url = [email protected]:ync/pic.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"] (添加track信息时设置此节点)
remote = origin
merge = refs/heads/master

git merge

1
2
3
4
git merge <branchName>合并分支(merge tool diffmerge filemerge)
git merge --no-commit
git merge --squash <branchName> 压合合并
git merge --abort 终止合并
1
2
3
4
5
6
--squash 选项的含义是:本地文件内容与不使用该选项的合并结果相同,但是不提交、不移动HEAD,因此需要一条额外的commit命令。其效果相当于将another分支上的多个commit合并成一个,放在当前分支上,原来的commit历史则没有拿过来。
Note:

判断是否使用--squash选项最根本的标准是,待合并分支上的历史是否有意义。

实测效果 就是将所有文件变更获取到当前分支下 而不获取分支提交信息
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
GIT-MERGE(1)                                                                Git Manual                                                               GIT-MERGE(1)

NAME
git-merge - Join two or more development histories together

SYNOPSIS
git merge [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
[-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
[--[no-]rerere-autoupdate] [-m <msg>] [<commit>...]
git merge <msg> HEAD <commit>...
git merge --abort

DESCRIPTION
Incorporates changes from the named commits (since the time their histories diverged from the current branch) into the current branch. This command is
used by git pull to incorporate changes from another repository and can be used by hand to merge changes from one branch into another.

Assume the following history exists and the current branch is "master":

A---B---C topic
/
D---E---F---G master

Then "git merge topic" will replay the changes made on the topic branch since it diverged from master (i.e., E) until its current commit (C) on top of
master, and record the result in a new commit along with the names of the two parent commits and a log message from the user describing the changes.

A---B---C topic
/ \
D---E---F---G---H master

The second syntax (<msg> HEAD <commit>...) is supported for historical reasons. Do not use it from the command line or in new scripts. It is the same as
git merge -m <msg> <commit>....

The third syntax ("git merge --abort") can only be run after the merge has resulted in conflicts. git merge --abort will abort the merge process and try
to reconstruct the pre-merge state. However, if there were uncommitted changes when the merge started (and especially if those changes were further
modified after the merge was started), git merge --abort will in some cases be unable to reconstruct the original (pre-merge) changes. Therefore:

Warning: Running git merge with non-trivial uncommitted changes is discouraged: while possible, it may leave you in a state that is hard to back out of in
the case of a conflict.

git cherry-pick

1
git cherry-pick <commitId> 挑选其次提交合并
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
GIT-CHERRY-PICK(1)                                                          Git Manual                                                         GIT-CHERRY-PICK(1)

NAME
git-cherry-pick - Apply the changes introduced by some existing commits

SYNOPSIS
git cherry-pick [--edit] [-n] [-m parent-number] [-s] [-x] [--ff]
[-S[<keyid>]] <commit>...
git cherry-pick --continue
git cherry-pick --quit
git cherry-pick --abort

DESCRIPTION
Given one or more existing commits, apply the change each one introduces, recording a new commit for each. This requires your working tree to be clean (no
modifications from the HEAD commit).

When it is not obvious how to apply a change, the following happens:

1. The current branch and HEAD pointer stay at the last commit successfully made.

2. The CHERRY_PICK_HEAD ref is set to point at the commit that introduced the change that is difficult to apply.

3. Paths in which the change applied cleanly are updated both in the index file and in your working tree.

4. For conflicting paths, the index file records up to three versions, as described in the "TRUE MERGE" section of git-merge(1). The working tree files
will include a description of the conflict bracketed by the usual conflict markers <<<<<<< and >>>>>>>.

5. No other modifications are made.

See git-merge(1) for some hints on resolving such conflicts.

git rebase

将两个分支并为一个分支(不建议使用)

1
2
3
4
5
6
7
8
9
10
11
GIT-REBASE(1)                                                               Git Manual                                                              GIT-REBASE(1)

NAME
git-rebase - Forward-port local commits to the updated upstream head

SYNOPSIS
git rebase [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
[<upstream> [<branch>]]
git rebase [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
--root [<branch>]
git rebase --continue | --skip | --abort | --edit-todo

git tag

1
2
3
git tag 显示标签列表
git tag 1.0 创建标签,注意标签不能生命令
git tag -d 1.0 删除标签
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
GIT-TAG(1)                                                                  Git Manual                                                                 GIT-TAG(1)

NAME
git-tag - Create, list, delete or verify a tag object signed with GPG

SYNOPSIS
git tag [-a | -s | -u <keyid>] [-f] [-m <msg> | -F <file>]
<tagname> [<commit> | <object>]
git tag -d <tagname>...
git tag [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
[--column[=<options>] | --no-column] [--create-reflog] [<pattern>...]
git tag -v <tagname>...

DESCRIPTION
Add a tag reference in refs/tags/, unless -d/-l/-v is given to delete, list or verify tags.

Unless -f is given, the named tag must not yet exist.

If one of -a, -s, or -u <keyid> is passed, the command creates a tag object, and requires a tag message. Unless -m <msg> or -F <file> is given, an editor
is started for the user to type in the tag message.

If -m <msg> or -F <file> is given and -a, -s, and -u <keyid> are absent, -a is implied.

Otherwise just a tag reference for the SHA-1 object name of the commit object is created (i.e. a lightweight tag).

A GnuPG signed tag object will be created when -s or -u <keyid> is used. When -u <keyid> is not used, the committer identity for the current user is used
to find the GnuPG key for signing. The configuration variable gpg.program is used to specify custom GnuPG binary.

Tag objects (created with -a, -s, or -u) are called "annotated" tags; they contain a creation date, the tagger name and e-mail, a tagging message, and an
optional GnuPG signature. Whereas a "lightweight" tag is simply a name for an object (usually a commit object).

Annotated tags are meant for release while lightweight tags are meant for private or temporary object labels. For this reason, some git commands for
naming objects (like git describe) will ignore lightweight tags by default.

git status

1
git status 当前状态
1
2
3
4
5
6
7
8
9
10
11
12
13
git help status
GIT-STATUS(1) Git Manual GIT-STATUS(1)

NAME
git-status - Show the working tree status

SYNOPSIS
git status [<options>...] [--] [<pathspec>...]

DESCRIPTION
Displays paths that have differences between the index file and the current HEAD commit, paths that have differences between the working tree and the
index file, and paths in the working tree that are not tracked by Git (and are not ignored by gitignore(5)). The first are what you would commit by
running git commit; the second and third are what you could commit by running git add before running git commit.

git log

穿梭历史,用git log可以查看提交历史,以便确定要回退到哪个版本。

1
2
git log 查看历史记录
git log --graph 查看分支合并图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
git log
commit 5a06716997a4cb904e789d864f87372bd8fd7bb9
Author: duanchaoqian <[email protected]>
Date: Thu May 12 15:03:57 2016 +0800

add 1.txt 2.txt

commit 3706383b2edadedbc59a758a05d083978bf8ec28
Author: duanchaoqian <[email protected]>
Date: Tue May 10 14:51:23 2016 +0800

c

commit 1e1c9ddf7db6ca7f9a48841e7511d6768351ae8f
Author: duanchaoqian <[email protected]>
Date: Tue May 10 14:48:09 2016 +0800

b

git reflog

reset –hard 后悔药

重返未来 git reflog查看命令历史,以便确定要回到未来的哪个版本。

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
git reflog
5a06716 HEAD@{0}: reset: moving to 5a06716
3706383 HEAD@{1}: reset: moving to head^
5a06716 HEAD@{2}: reset: moving to head^
737fef4 HEAD@{3}: reset: moving to head^^
2b7c98d HEAD@{4}: reset: moving to head^
4843adf HEAD@{5}: revert: Revert "Revert "Revert "Revert "add 1.txt 2.txt""""
2b7c98d HEAD@{6}: revert: Revert "Revert "Revert "add 1.txt 2.txt"""
9303932 HEAD@{7}: revert: Revert "Revert "add 1.txt 2.txt""
737fef4 HEAD@{8}: revert: Revert "add 1.txt 2.txt"
5a06716 HEAD@{9}: commit: add 1.txt 2.txt
3706383 HEAD@{10}: reset: moving to head^^
bb8fd2e HEAD@{11}: reset: moving to head^
02c0481 HEAD@{12}: checkout: moving from b to master
02c0481 HEAD@{13}: checkout: moving from master to b
02c0481 HEAD@{14}: checkout: moving from a to master
02c0481 HEAD@{15}: checkout: moving from master to a
02c0481 HEAD@{16}: commit: 1.txt 2.txt
bb8fd2e HEAD@{17}: reset: moving to bb8fd2ecb9e4b3acde01c60fd20df87738e17123
25c7e08 HEAD@{18}: commit: a1/1.txt
bb8fd2e HEAD@{19}: reset: moving to bb8fd2ecb9e4b3acde01c60fd20df87738e17123
0f6de7e HEAD@{20}: commit: **/*.txt
33af901 HEAD@{21}: commit: *.txt
882f783 HEAD@{22}: commit: *.txt
bb8fd2e HEAD@{23}: commit (amend): a
58c6c08 HEAD@{24}: commit: a
86c8278 HEAD@{25}: commit: del c
3706383 HEAD@{26}: commit: c
1e1c9dd HEAD@{27}: commit (initial): b

gitk

显示 form 窗体,非控制台

1
2
3
gitk 查看当前分支历史记录
gitk <branchName> 查看某分支记录
gitk --all 查看所有分支记录

git archive

导出版本库

1
2
git archive --format=tar.gz head>t1.tar.gz (只有文件,没有.git目录)
git archive --format=tar.gz --prefix=ync/ head>t1.tar.gz 压缩文件里多一层目录ync

git clone

1
git clone <url>

git remote

1
2
3
git remote 查看远程分支
git remote add <别名> <远程仓库url> 添加远程仓库
git remote rm <别名> 删除远程仓库

git fetch

1
git fetch <远程仓库别名|仓库地址> 获取但不合并

git pull

1
2
3
git pull 获取并合并分支
git pull origin 获取并合并分支指定仓库名
git pull <远程仓库> <Local branchName> <remote branchName> 获取并合并远程指定分支
1
2
3
4
5
6
7
8
9
10
如果当前分支没有追踪track信息执行 git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.

git pull <remote> <branch>

If you wish to set tracking information for this branch you can do so with:

git branch --set-upstream-to=origin/<branch> master

git push

1
2
git push <远程库别名> <local branchName>:<remote branchName> 推送到远程仓库,指定分支
git push -v --set-upstream origin refs/heads/develop:refs/heads/develop 推送到远程仓库,指定分支,并添加追踪信息
1
2
3
4
5
6
7
8
9
10
追踪信息配置track
[remote "origin"]
url = [email protected]:ync/pic.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[branch "develop"]
remote = origin
merge = refs/heads/develop
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
git help push
GIT-PUSH(1) Git Manual GIT-PUSH(1)

NAME
git-push - Update remote refs along with associated objects

SYNOPSIS
git push [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
[--repo=<repository>] [-f | --force] [--prune] [-v | --verbose]
[-u | --set-upstream]
[--[no-]signed|--sign=(true|false|if-asked)]
[--force-with-lease[=<refname>[:<expect>]]]
[--no-verify] [<repository> [<refspec>...]]

DESCRIPTION
Updates remote refs using local refs, while sending objects necessary to complete the given refs.

You can make interesting things happen to a repository every time you push into it, by setting up hooks there. See documentation for git-receive-pack(1).

When the command line does not specify where to push with the <repository> argument, branch.*.remote configuration for the current branch is consulted to
determine where to push. If the configuration is missing, it defaults to origin.

When the command line does not specify what to push with <refspec>... arguments or --all, --mirror, --tags options, the command finds the default
<refspec> by consulting remote.*.push configuration, and if it is not found, honors push.default configuration to decide what to push (See git-config(1)
for the meaning of push.default).

git help

1
2
git help
git help <command> git help push git help add
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
git help
usage: git [--version] [--help] [-C <path>] [-c name=value]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
<command> [<args>]

These are common Git commands used in various situations:

start a working area (see also: git help tutorial)
clone Clone a repository into a new directory
init Create an empty Git repository or reinitialize an existing one

work on the current change (see also: git help everyday)
add Add file contents to the index
mv Move or rename a file, a directory, or a symlink
reset Reset current HEAD to the specified state
rm Remove files from the working tree and from the index

examine the history and state (see also: git help revisions)
bisect Use binary search to find the commit that introduced a bug
grep Print lines matching a pattern
log Show commit logs
show Show various types of objects
status Show the working tree status

grow, mark and tweak your common history
branch List, create, or delete branches
checkout Switch branches or restore working tree files
commit Record changes to the repository
diff Show changes between commits, commit and working tree, etc
merge Join two or more development histories together
rebase Forward-port local commits to the updated upstream head
tag Create, list, delete or verify a tag object signed with GPG

collaborate (see also: git help workflows)
fetch Download objects and refs from another repository
pull Fetch from and integrate with another repository or a local branch
push Update remote refs along with associated objects

'git help -a' and 'git help -g' list available subcommands and some
concept guides. See 'git help <command>' or 'git help <concept>'
to read about a specific subcommand or concept.

一张图总结 git

https://raw.githubusercontent.com/duanzhaoqian/pic/master/git/git.png