Git操作

花了好几天的时间,才迁移完git学习笔记,进行了重新整理,记录了常用操作。不知不觉Git已经用了2年的时间,虽然不久,但已经深深爱上了这个家伙~~~

git config


使用方法如下,如果没有指定位置参数省略的话,大部分命令默认为:--local

1
git config [<--system> | <--global> | <--local>] [option]

配置优先级

配置生效优先级:1. > 2. > 3.,即相同参数的配置优先级

  1. .git/config 工作目录下的配置,使用:git config --local
  2. ~/.gitconfig 用户目录下的配置,使用:git config --global
  3. /etc/gitconfig 对所有用户都适用的配置,使用:git cofing --system

选项

查看生效的配置

1
2
3
4
5
6
7
8
git config -l #查看所有配置
git config --list #查看所有配置
git config --system -l #查看系统用户的所有配置
git config --system --list #查看系统用户的所有配置
git config --global -l #查看当前用户的所有配置
git config --global --list #查看当前用户的所有配置
git config --local -l #查看当前工作目录下的所有配置
git config --local --list #查看当前工作目录下的所有配置

查看某个参数的配置

1
2
3
git config user.name #查看当前工作目录下的“用户名称”的配置
git config --local user.name #查看当前工作目录下的“用户名称”的配置,可以省略:--local
git config --global user.name #查看当前用户的“用户名称”的配置

设置用户名称和用户Email地址

1
2
3
4
git config --global user.name "John Doe"
git config --global user.email johndoe@example.com
git config --local user.name "John Doe"
git config --local user.email johndoe@example.com

换行符:CRLF/LF

在Windows系统上,把它设置成true,这样当签出代码时,LF会被转换成CRLF

1
git config --global core.autocrlf true

Linux或Mac系统上,因此你不想Git在签出文件时进行自动的转换;当一个以CRLF为行结束符的文件不小心被引入时你肯定想进行修正,设置成input来告诉Git在提交时把CRLF转换成LF,签出时不转换

1
git config --global core.autocrlf input

如果仅运行在Windows上的项目,可以设置false取消此功能,把回车符记录在库中

1
git config --global core.autocrlf false

换行符:SafeCRLF

拒绝提交包含混合换行符的文件

1
git config --global core.safecrlf true

允许提交包含混合换行符的文件

1
git config --global core.safecrlf false

提交包含混合换行符的文件时给出警告

1
git config --global core.safecrlf warn

core.quotepath

在使用git的时候,经常会碰到有一些中文文件名或者路径被转义成\xx\xx\xx之类的,此时可以通过git的配置来改变默认转义

1
git config --global core.quotepath false

其它配置

1
2
3
4
5
6
git config --global core.editor vim #设置文本编辑器
git config --global color.ui true
git config --global alias.st status #设置别名
git config receive.denyNonFastForwards true #禁止非快进试推送
git config --global merge.tool kdiff3
git config --global diff.word.textconv strings #在比较前把Word文件转换成文本文件

工作区


暂存区


暂存区,又称为--stage--index,是一个介于工作区和版本库的中间状态,当执行提交时,实际上是将暂存区的内容提交到版本库中。


版本库

工作区/暂存区/版本库,三者的关系图



HEAD


HEAD是当前分支引用的指针,它总是指向该分支上的最后一次提交。这表示 HEAD将是下一次提交的父节点。通常,理解HEAD的最简方式,就是将它看做 你的上一次提交 的快照。
符号^可以用于指代父提交

HEAD^代表版本库中的上一次提交,即最近一次提交的父提交
HEAD^^则代表HEAD^的父提交
对于一个提交有多个父提交,可以在符号^后面用数字表示是第几个父提交

查看快照

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ git cat-file -p HEAD
tree 684768932f4b16ef45aa5e87793a76470fb8becb
parent ae1ffd161d1ffb564ba2a0e87dae45ec63ea5eea
author Jerry.Chen <balabala@gmail.com> 1453474460 +0800
committer Jerry.Chen <balabala@gmail.com> 1453474460 +0800
commit with -a
``
## 查看引用日志,是`HEAD`头指针的变迁记录,而非`master`分支
```bash
$ git reflog
ae1ffd1 HEAD@{0}: checkout: moving from b2 to master
48c54d7 HEAD@{1}: checkout: moving from master to b2
ae1ffd1 HEAD@{2}: checkout: moving from master to master

查看引用日志的输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ git show HEAD@{0}
commit ae1ffd161d1ffb564ba2a0e87dae45ec63ea5eea
Merge: 3bce065 17ce0b6
Author: Jerry <jerry@china.com>
Date: Sun Apr 20 16:05:08 2014 +0800
merge ...
diff --cc hello.md
index 1e644f0,11f9129..70b7830
--- a/hello.md
+++ b/hello.md
@@@ -6,7 -6,7 +6,8 @@@
5 modify
6 modify by jerry
6 modify
+7 modify by jerry
+ 7 modify
branch b1 1 modify branch b1 3 modify
branch b1 2 modify branch b1 4 modify
branch b2 1 modify

SSH Key

查看已经保存的SSH密钥

1
ls -lart ~/.ssh

生成SSH密钥

1
ssh-keygen -t rsa -C "your_email@example.com"

查看公钥(*.pub)文件的内容,如果没有修改密钥的位置和保存的路径,直接复制cat出来的内容,粘帖到你需要用到的地方

1
cat ~/.ssh/id_rsa.pub

git clone


克隆操作,使用方法如下:

1
git clone [<options>] [--] <repository> [<directory>]

包含工作区的版本库

在本地创建一个的克隆工作区,所有的文件都会检出,如果设置了[<directory>],则会将此名称做为克隆的文件夹名称

1
git clone <repository> [<directory>]

裸版本库,不包含工作区

不包含工作区,直接就是版本库的内容,这样的版本库称为裸版本库。一般约定俗成裸版本库的目录名以.git为后缀,所以下面实例中将克隆出来的裸版本库目录名写作<directory.git>

1
2
git clone --bare <repository> <directory.git>
git clone --mirror <repository> <directory.git>

git clone --mirror 克隆出来的裸版本对上游版本进行了注册,这样可以在裸版本库中使用git fetch命令和上游版本库进行持续同步

支持的多种协议

常用的一些协议,不是全部

1
2
3
4
5
6
git clone file:///opt/git/project.git
git clone /opt/git/project.git
git clone ssh://user@server:project.git
git clone http[s]://example.com/gitproject.git
git clone git://example.com/path/to/repo.git/
git clone git@github.com:User/progit.git

git remote

查看当前工作目录的远程仓库信息

1
2
3
git remote -v
git remote show #查看当前工作目录所有的远程仓库的简写名称
git remote show [name]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ git remote -v
origin git@github.com:myoss/progit.git (fetch)
origin git@github.com:myoss/progit.git (push)
$ git remote show
origin
$ git remote show origin
* remote origin
Fetch URL: git@github.com:myoss/progit.git
Push URL: git@github.com:myoss/progit.git
HEAD branch: master
Remote branches:
b1 tracked
master tracked
Local branches configured for 'git pull':
b1 merges with remote b1
master merges with remote master
Local refs configured for 'git push':
b1 pushes to b1 (up to date)
master pushes to master (up to date)

添加远程仓库

1
git remote add [name] [url]

修改远程仓库的url地址

1
git remote set-url <name> <new_url>

修改某个远程仓库的简写名称

1
git remote rename <old_name> <new_name>

移除某个远程仓库的配置信息,不会删除任何文件

1
git remote rm | remove <name>

git fetch

拉取远程仓库的更新数据

只是将远端的数据拉到本地仓库,不会自动合并到当前工作分支。

1
2
git fetch <repository-name> <branch-name> #repository-name:远程仓库的简写名称
git fetch origin master

拉取所有远程仓库

1
git fetch --all

git pull

获取远程仓库的更新数据,再自动合并到本地仓库中的当前分支

1
2
git pull <repository-name> <repository-branch-name>
git pull origin master

相当于做了以下2步操作,如果有冲突,将会合并失败

1
2
git fetch origin master
git merge origin/master

获取远程仓库的更新数据,再自动合并到本地仓库中的其它分支

1
2
git pull <repository-name> <repository-branch-name>:<local-branch-name>
git pull origin master:dev #将远程仓库的master分支的更新,合并到本地的dev分支

获取远程仓库上所有的tag

1
2
git pull <repository-name> --tags
git pull origin --tags

git push

将本地分支的更新,推送到远程仓库

1
2
git push <repository-name> <local-branch-name>:[repository-branch-name]
git push origin local-dev:remote-dev #将本地的local-dev分支,推送到远程的remote-dev分支

通常的操作,省略掉远程分支名[repository-branch-name],则表示将本地分支推送与之存在“追踪关系”的远程分支(通常两者同名),如果该远程分支不存在,则会被新建。

1
2
3
git push <repository-name> <local-branch-name>
git push origin master #将本地的master分支推送到远程仓库的master分支
git push origin b1 #如果远程仓库不存在b1分支,则会创建b1分支

删除远程仓库的分支

第一种方式,如果省略[local-branch-name],推送一个空的本地分支到远程分支,即删除掉[远程分支],[本地分支]不会删除

1
git push <repository-name> :<repository-branch-name>

示例:删除远程仓库的b3分支

1
2
3
$ git push origin :b3
To git@github.com:myoss/progit.git
- [deleted] b3

第二种方式,使用--delete选项

1
git push <repository-name> <repository-branch-name> --delete

示例:删除远程仓库的b2分支

1
2
3
$ git push origin b2 --delete
To git@github.com:myoss/progit.git
- [deleted] b2

强制更新,覆盖的方式推送

注意:此操作非常危险,因为能够将历史记录抹除掉,比如远程分支有3个提交:1,2,3,而本地分支有2个提交:4,5,并且和远程分支的提交不一样,如果强制推送了之后,远程分支上原来的3个提交就会丢失掉,变成和本地分支一样。总而言之,请三思而后行!!!

强制更新,会在远程仓库产生一个“非块进式”的合并(non-fast-forward merge)

1
git push <repository-name> <local-branch-name> -f | --force

操作注意:

  1. 在github中,通常会关闭对master分支的强制更新,关闭保护的步骤:在项目的Settings下的Branches中,找到Protected branches,将其关闭掉即可。
  2. 在gitlab中,通常会关闭对master分支的强制更新,关闭保护的步骤:在项目的Settings下,找到Protected branches,将其关闭掉即可。

推送标签

默认情况下,git push并不会把标签推送到远程仓库,只有通过显式命令才能推送标签到远程仓库

1
git push <repository-name> <local-tag-name>

推送所有的标签

将本地所有的标签,推送到远程仓库(远程仓库没有的标签),注意是否有权限

1
git push <repository-name> --tags

推送本地所有的分支,不包含标签

1
git push <repository-name> --all

git init

初始化本地仓库

先在本地创建一个空目录,然后在创建的目录里使用git init初始化仓库

1
2
3
$ mkdir my-work
$ cd my-work
$ git init

一步操作,仓库空目录,并初始化仓库

1
$ git init my-work

git add

添加未跟踪的文件

使用git add <文件名1>[,<文件名2>,<文件名3>]添加未跟踪的文件,这不会提交到本地仓库中。如需要提交,使用git commit -m "提交的注释"

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
$ git status -s test.txt #查看精简状态,显示有一个未跟踪的文件
?? test.txt
$ git status #查看状态,现在有一个未跟踪的test.txt文件
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)
test.txt
nothing added to commit but untracked files present (use "git add" to track)
$ git add test.txt #添加未跟踪的test.txt文件
$ git status #再次查看状态,test.txt文件已经放到暂存区了
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: test.txt
$ git status -s #查看精简状态,显示有一个添加的文件
A test.txt
$ git commit -m"添加文件:test.txt" #提交到本地git仓库中
[master d14545d] 添加文件:test.txt
1 file changed, 1 insertion(+)
create mode 100644 test.txt
$ git status #再次查看状态,刚才添加的文件已经提交,并且领先远程仓库一个提交
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
nothing to commit, working directory clean
$ git status -s #查看精简状态,无内容

添加已经追踪的文件

同样的使用git add <文件名1>[,<文件名2>,<文件名3>]添加已跟踪的文件,这不会提交到本地仓库中。如需要提交,使用git commit -m "提交的注释"

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
$ git status #查看状态,有一个文件的内容修改了
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(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 checkout -- <file>..." to discard changes in working directory)
modified: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
$ git status -s #查看精简状态,显示有一个修改的文件,`M`字符的位置在第二列,颜色为红色
M test.txt
$ git add test.txt #将test.txt文件修改的内容放到暂存区
$ git status #再次查看状态,test.txt文件修改的内容已经放到暂存区了
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: test.txt
$ git status -s #查看精简状态,显示有一个修改的文件,`M`字符的位置在第一列,颜色为绿色
M test.txt
$ git commit -m"new update for test.txt" #提交到本地git仓库中
[master 3a0f2d3] new update for test.txt
1 file changed, 1 insertion(+)
$ git status #再次查看状态,刚才添加的文件已经提交,并且领先远程仓库两个提交
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
nothing to commit, working directory clean
$ git status -s #查看精简状态,无内容

巧用git add,快速回退测试代码

假设已经开发好了某个功能,这时候用git add添加,但不做git commit

场景一:这时候我再去添加些测试代码测试,比如System.out.println这种代码,测试下来发现我刚才git add的代码都是OK的,这时候我用git checkout就能快速安全的删掉刚才添加的测试代码。
场景二:我发现刚才git add的代码不够完美,修改完成之后,再次用git add添加,就会合并到上一次git add的代码中了,然后做为一次git commit


git status

查看当前分支的文件变化状态

1
git status

查看精简状态

1
git status -s

M字符的位置在第二列,颜色为红色,表示工作区当前的文件与暂存区的文件相比有改动

1
2
$ git status -s
M test.txt

M字符的位置在第一列,颜色为绿色,表示版本库中的文件与暂存区中的文件相比有改动

1
2
$ git status -s
M test.txt

同时显示所在的分支

1
2
$ git status -sb
## master...origin/master [ahead 2]

git cherry

查看本地分支领先远程仓库的提交

1
2
3
$ git cherry
+ d14545d3754498d664caea7672761305b59dd162
+ 3a0f2d351733ed9d234e6f0498bc533c11048686

git diff

比较工作区和暂存区

1
git diff

逐词比较,而非默认的逐行比较

1
git diff --word-diff

比较暂存区和HEAD(本地git仓库)

1
2
git diff --cached
git diff --staged

比较工作区和HEAD(本地git仓库)

1
git diff HEAD

比较两个提交

SHA-1值(前7位即可)比较两次提交之间的差异。注意顺序不一样,查看到的结果也会不一样。

1
git diff <$id1> <$id2>

比较两个分支

只比较已经提交的内容

1
git diff <branch1> <branch2>

比较两个远程分支

注意:可能需要先执行git fetch

1
git diff pro/master pro/b1

当前分支比较另外一个分支

1
git diff <branch2> [current-branch]

比较之后,显示简要的增改行数统计

1
2
3
$ git diff --stat
test.txt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

比较二进制文件差异

1
git diff --binary

git stash

储藏当前工作区和暂存区修改的内容

使用场景,当需要紧急处理一个bug时,工作区内容又没有完成,不适合提交,将未完成的修改保存到一个栈上,而你可以在任何时候重新应用这些改动。

1
git stash

示例:

1
2
3
$ git stash
Saved working directory and index state WIP on master: ae1ffd1 merge ...
HEAD is now at ae1ffd1 merge ...

储藏的完整命令

1
git stash [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] [-u|--include-untracked] [-a|--all] [<message>]

如果需要在保存储藏的时候指定说明

1
git stash save <message>
1
2
3
$ git stash save "9 modify"
Saved working directory and index state On master: 9 modify
HEAD is now at ae1ffd1 merge ...

使用参数--patch会显示工作区和HEAD的差异,通过对比差异文件的编辑决定在进度中最终要保存的工作的内容,通过编辑差异文件可以在进度中排除无关内容

1
git stash --patch
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 stash --patch
diff --git a/hello.md b/hello.md
index 70b7830..d866fb0 100644
--- a/hello.md
+++ b/hello.md
@@ -10,4 +10,7 @@
7 modify
branch b1 1 modify branch b1 3 modify
branch b1 2 modify branch b1 4 modify
+10 modify 1
+10 modify 2
+10 modify 3
branch b2 1 modify
Stash this hunk [y,n,q,a,d,/,e,?]? ?
y - stash this hunk
n - do not stash this hunk
q - quit; do not stash this hunk or any of the remaining ones
a - stash this hunk and all later hunks in the file
d - do not stash this hunk or any of the later hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help
@@ -10,4 +10,7 @@
7 modify
branch b1 1 modify branch b1 3 modify
branch b1 2 modify branch b1 4 modify
+10 modify 1
+10 modify 2
+10 modify 3
branch b2 1 modify
Stash this hunk [y,n,q,a,d,/,e,?]?

使用-k--keep-index参数,在保存进度后不将暂存区重置(默认会将暂存区工作区强制重置,即命令:--no-keep-index的效果)

1
git bash -k | --keep-index
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ 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: hello.md
$ git stash -k #储藏
Saved working directory and index state WIP on master: ae1ffd1 merge ...
HEAD is now at ae1ffd1 merge ...
$ 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: hello.md

使用-u|--include-untracked,同时储藏未跟踪文件。

1
git stash save -u|--include-untracked

使用-a|--all来储藏所有的改动,包括未跟踪文件

1
git stash save -a|--all

查看已经保存的储藏列表

1
2
3
$ git stash list
stash@{0}: On master: 9 modify
stash@{1}: WIP on master: ae1ffd1 merge ...

删除储藏

如果没有指定是第几个储藏时,默认会删除最近的一次储藏

1
git stash drop [-q|--quiet] [<stash@{n}>]
1
2
$ git stash drop stash@{0}
Dropped stash@{0} (2fa94640f400006a6ba812b3c02e8b0e91472958)

删除所有的储藏

1
git stash clear

查看[某次]储藏修改的内容

1
git stash show [<stash@{n}>]

示例:

1
2
3
4
5
6
7
$ git stash show #查看最后一次的储藏
hello.md | 2 ++
1 file changed, 2 insertions(+)
$ git stash show stash@{1} #查看某次的储藏
hello.md | 1 +
1 file changed, 1 insertion(+)

恢复保存的工作进度

1
git stash <pop | apply> [--index] [-q|--quiet] [<stash@{n}>]

如果不使用任何参数,恢复最近一次保存的工作进度

1
2
git stash pop #会从储藏列表中删除刚刚恢复的暂存
git stash apply #不会从储藏列表中删除刚刚恢复的暂存

如果使用<stash@{n}>参数,则从该<stash@{n}>中恢复保存的内容

1
2
git stash pop <stash@{n}> #会从储藏列表中删除该`<stash@{n}>`
git stash apply <stash@{n}> #不会从储藏列表中删除该`<stash@{n}>`

--index除了恢复工作区的文件外,还会恢复暂存区的内容

1
2
git stash pop --index
git stash apply --index

使用储藏的内容创建新的分支

<branch-name>不能是已经存在的分支,并切换到创建的新分支,删除掉该储藏

1
git stash branch <branch-name> [<stash@{n}>]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ git stash branch b2
Switched to a new branch 'b2'
<stdin>:9: trailing whitespace.
10 modify 2
warning: 1 line adds whitespace errors.
On branch b2
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: hello.md
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: hello.md
Dropped refs/stash@{0} (3eaa4512757a598193acb1f9f0a7f91dc6c165b1)

查看储藏列表的SHA-1

1
2
3
$ git reflog show refs/stash
5afe096 refs/stash@{0}: On master: 9 modify
a5c6080 refs/stash@{1}: WIP on master: ae1ffd1 merge ...

git commit

仓库中的文件可能存在于这三种状态

  1. Untracked files → 文件未被跟踪
  2. Changes to be committed → 文件已暂存,这是下次提交的内容
  3. Changes not staged for commit → 文件被修改,但并没有添加到暂存区。如果commit时没有带-a选项,此状态下的文件不会被提交。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ git status
On branch b2
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: hello.md
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: hello.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt

提交暂存区的内容

提交状态为Changes to be committed的内容

1
git commit [-m <message>]

提交暂存区和工作区的内容

提交状态为Changes to be committedChanges not staged for commit的内容

1
git commit -a [-m <message>]
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
$ git status
On branch b2
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: hello.md
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: hello.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
$ git commit -a -m"commit with -a"
[b2 48c54d7] commit with -a
1 file changed, 3 insertions(+)
$ git status
On branch b2
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt

修改或者撤销最后一次提交

1
2
git commit --amend #进入交互终端模式
git commit --amend -m <message> #修改最后一次提交的注释

修改最后一次提交,退出编辑器之后会直接commit。如果要取消,则在编辑器里清空掉提交信息再退出即可。使用场景如:修改提交信息、补正刚才修改的内容(需要先git add)、少添加文件了(需要先git add),谨慎操作!!


git checkout

汇总显示(工作区和暂存区)与HEAD的差异

1
2
git checkout [HEAD]
git checkout 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
$ git status
On branch b2
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: hello.md
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
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
$ git checkout #不会统计未追踪的文件
M README.md
M hello.md
$ git checkout HEAD
M README.md
M hello.md

撤销工作区的修改

用暂存区中的内容覆盖工作区的内容,如果使用.,而没有指定具体的file,会替换工作区所有有改动的文件

1
git checkout [--] <. | file>
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 status
On branch b2
Changes to be committed: #暂存区的修改
(use "git reset HEAD <file>..." to unstage)
modified: hello.md
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: hello.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
$ git checkout hello.md #使用暂存区的内容覆盖工作区的内容
$ git status #工作区没有修改的内容
On branch b2
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: hello.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt

HEAD中的内容替换暂存区和工作区的修改

取出最近的一次提交的内容,替换暂存区和工作区的修改,如果使用.,而没有指定具体的file,会替换暂存区和工作区中所有有改动的文件

1
2
3
git checkout [--] <. | file>
git checkout <file>
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
$ 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: hello.md
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: hello.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
$ git checkout HEAD hello.md #撤销暂存区和工作区的修改
$ git status #hello.md在暂存区和工作区没有修改
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)
test.txt

使用其它分支的内容替换当前分支上暂存区和工作区的内容

HEAD的指向不会变化

1
git checkout <branch-name> [--] <file>
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
Administrator@Jerry-WIN8-TP MINGW64 /d/workspaces/test/progit (master)
$ git status #当前分支是:master,hello.md在暂存区和工作区都有修改
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: hello.md
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: hello.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
Administrator@Jerry-WIN8-TP MINGW64 /d/workspaces/test/progit (master)
$ git diff b2 #master分支和b2分支比较hello.md的内容
diff --git a/hello.md b/hello.md
index d866fb0..44d3964 100644
--- a/hello.md
+++ b/hello.md
@@ -8,9 +8,8 @@
6 modify
7 modify by jerry
7 modify
+8 modify^M #master分支上的内容
branch b1 1 modify branch b1 3 modify
branch b1 2 modify branch b1 4 modify
-10 modify 1 #b2分支上的内容
-10 modify 2
-10 modify 3
+master modify 11^M
branch b2 1 modify
Administrator@Jerry-WIN8-TP MINGW64 /d/workspaces/test/progit (master)
$ git checkout b2 hello.md #用b2分支上hello.md的内容替换暂存区和工作区的内容
Administrator@Jerry-WIN8-TP MINGW64 /d/workspaces/test/progit (master)
$ git diff b2 #master分支和b2分支没有差异
Administrator@Jerry-WIN8-TP MINGW64 /d/workspaces/test/progit (master)
$ git status #暂存区还是有hello.md这个文件
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: hello.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
Administrator@Jerry-WIN8-TP MINGW64 /d/workspaces/test/progit (master)
$ cat hello.md #查看master分支上hello.md文件内容,发现b2分支的内容替换过来了
6 modify
1 add
2 modify
3 modify
4 mdofiy --> amend 3
5 modify
6 modify by jerry
6 modify
7 modify by jerry
7 modify
branch b1 1 modify branch b1 3 modify
branch b1 2 modify branch b1 4 modify
10 modify 1 #这3行是b2分支上添加的内容,现在出现在master分支上了
10 modify 2
10 modify 3
branch b2 1 modify

git reset

基本操作

将当前的分支重设(reset)到指定的<commit>或者HEAD(如果不显示指定commit,默认是HEAD,即最新的一次提交),并且根据选项有可能更新暂存区工作区

1
git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]

--soft暂存区工作区中的内容不作任何改变,仅仅把HEAD指向<commit>。这个模式的效果是,执行完毕后,自从<commit>以来的所有改变都会显示为Changes to be committedgit status) 。不会丢失任何改动

1
2
3
git reset --soft <commit>
git reset --soft HEAD^ #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
64
65
66
67
68
69
70
71
72
73
74
75
$ git hist #现在有3个提交
* adba38b v3 commit
* c7ded78 v2 commit
* 45496b8 v1 commit
$ cat readme.md #文件的内容如下
v1
v2
v3
v4
$ 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.md
$ git diff #比较工作区和暂存区
diff --git a/readme.md b/readme.md
index 4b1d4d4..ed7ce12 100644
--- a/readme.md
+++ b/readme.md
@@ -1,3 +1,4 @@
v1
v2
v3
+v4 #工作区修改的内容
$ git reset --soft c7ded78 #将分支重设到第二次提交
$ git status #查看状态,暂存区显示有待提交的内容
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.md
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 diff #比较工作区和暂存区,工作区的内容还是之前的
diff --git a/readme.md b/readme.md
index 4b1d4d4..ed7ce12 100644
--- a/readme.md
+++ b/readme.md
@@ -1,3 +1,4 @@
v1
v2
v3
+v4
$ git diff --cached HEAD #比较暂存区和HEAD的差异
diff --git a/readme.md b/readme.md
index 2139d8b..4b1d4d4 100644
--- a/readme.md
+++ b/readme.md
@@ -1,2 +1,3 @@
v1
v2
+v3 #这一行就是第三次提交的内容
$ git hist #查看提交日志
* c7ded78 v2 commit
* 45496b8 v1 commit
$ cat readme.md
v1
v2
v3
v4

--hard:重设暂存区工作区,自从<commit>以来在工作区中的任何改变都被丢弃,并把HEAD指向<commit>会丢失改动

1
2
3
4
5
git reset --hard <commit>
git reset --hard HEAD^ #HEAD^代表版本库中的最近一次提交的父提交,即丢弃掉最近的一次提交
git reset --hard origin/master #丢弃在本地的所有改动与提交,用远程仓库的提交覆盖
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
$ git hist #现在有3个提交
* adba38b v3 commit
* c7ded78 v2 commit
* 45496b8 v1 commit
$ cat readme.md #文件的内容如下
v1
v2
v3
v4
v5
$ git diff #比较工作区和暂存区
diff --git a/readme.md b/readme.md
index ed7ce12..4d73d61 100644
--- a/readme.md
+++ b/readme.md
@@ -2,3 +2,4 @@ v1
v2
v3
v4
+v5 #工作区修改的内容
$ git diff --cached HEAD #比较暂存区和HEAD的差异
diff --git a/readme.md b/readme.md
index 4b1d4d4..ed7ce12 100644
--- a/readme.md
+++ b/readme.md
@@ -1,3 +1,4 @@
v1
v2
v3
+v4 #暂存区修改的内容
$ git status #查看状态,工作区和暂存区显示有修改的内容
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.md
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 reset --hard c7ded78 #将分支重设到第二次提交
HEAD is now at c7ded78 v2 commit
$ git status #查看状态,工作区和暂存区显示没有修改的内容
On branch master
nothing to commit, working directory clean
$ git hist #查看提交日志
* c7ded78 v2 commit
* 45496b8 v1 commit
$ cat readme.md #查看文件的内容,重设前的工作区和暂存区修改的内容已经丢失掉了
v1
v2

--mixed:仅重设暂存区,但是不重设工作区。这个模式是默认模式,即当不显示告知git reset模式时,会使用--mixed模式。这个模式的效果是,工作区中文件的修改都会被保留,不会丢弃,但是也不会被标记成Changes to be committed不会丢失任何改动

1
2
git reset <commit>
git reset --mixed <commit>
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
$ git hist #现在有3个提交
* adba38b v3 commit
* c7ded78 v2 commit
* 45496b8 v1 commit
$ cat readme.md #文件的内容如下
v1
v2
v3
v4
v5
$ git diff #比较工作区和暂存区
diff --git a/readme.md b/readme.md
index ed7ce12..4d73d61 100644
--- a/readme.md
+++ b/readme.md
@@ -2,3 +2,4 @@ v1
v2
v3
v4
+v5 #工作区修改的内容
$ git diff --cached HEAD #比较暂存区和HEAD的差异
diff --git a/readme.md b/readme.md
index 4b1d4d4..ed7ce12 100644
--- a/readme.md
+++ b/readme.md
@@ -1,3 +1,4 @@
v1
v2
v3
+v4 #暂存区修改的内容
$ git status #查看状态,工作区和暂存区显示有修改的内容
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.md
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 reset --mixed c7ded78 #将分支重设到第二次提交
Unstaged changes after reset:
M readme.md
$ 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.md
no changes added to commit (use "git add" and/or "git commit -a")
$ git diff #比较工作区和暂存区
diff --git a/readme.md b/readme.md
index 2139d8b..4d73d61 100644
--- a/readme.md
+++ b/readme.md
@@ -1,2 +1,5 @@
v1
v2
+v3 #重设前HEAD的内容,出现在工作区了
+v4 #重设前暂存区的内容,出现在工作区了
+v5 #重设前工作区的内容
$ git hist #查看提交日志
* c7ded78 v2 commit
* 45496b8 v1 commit
$ cat readme.md #查看文件的内容,重设前的工作区和暂存区修改的内容没有丢失掉了
v1
v2
v3
v4
v5

重置暂存区

将暂存区添加的内容,回退到工作区,改动不会丢失。如果指定了文件<file>,则只处理此文件

1
2
3
4
5
6
git reset HEAD
git reset --
git reset
git reset HEAD <file>
git reset -- <file>
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
$ git hist #现在有3个提交
* adba38b v3 commit
* c7ded78 v2 commit
* 45496b8 v1 commit
$ git status #查看状态,暂存区显示有修改的内容
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.md
new file: test.md
$ git diff
$ git diff --cached #比较工作区和暂存区
diff --git a/readme.md b/readme.md
index 4b1d4d4..0d6160d 100644
--- a/readme.md
+++ b/readme.md
@@ -1,3 +1,4 @@
v1
v2
v3
+v4
diff --git a/test.md b/test.md
new file mode 100644
index 0000000..e69de29
$ git reset HEAD #重置暂存区
Unstaged changes after reset:
M readme.md
$ 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.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.md
no changes added to commit (use "git add" and/or "git commit -a")
$ git diff
diff --git a/readme.md b/readme.md
index 4b1d4d4..0d6160d 100644
--- a/readme.md
+++ b/readme.md
@@ -1,3 +1,4 @@
v1
v2
v3
+v4

实战:两个提交压缩为一个

使用--soft参数调用重置命令,回到最近两次提交之前

1
git reset --soft HEAD^^

查看版本状态和最新日志,如果需要修改就进行修改

执行提交操作,即完成最新两个提交压缩为一个

1
git commit -m"Message"

git rebase

交互式变基操作

<since><till>代表历史提交的SHA-1值,变基的范围是从<since>(不包括<since>)的下一个提交到<till>,如果<till>没有给出,则变基的范围到当前HEAD

1
git rebase -i <since> [<till>]
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
$ git hist #现在有5个提交
* 1c1e018 v5 commit
* 45d9dfb v4 commit
* adba38b v3 commit
* c7ded78 v2 commit
* 45496b8 v1 commit
$ git rebase -i c7ded78 45d9dfb #变基范围为第三个提交到第四个提交
#稍等一会儿,就会弹出交互式界面,如果范围比较长需要等待的时间会久一些,如下:
pick adba38b v3 commit
pick 45d9dfb v4 commit
# Rebase c7ded78..45d9dfb onto c7ded78 (2 command(s)) #能够操作的提交只有2个
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
  1. 开头的几行由上到下依次对应历史的提交,如果删除掉这些提交保存退出交互式模式,则不执行变基动作
  2. pick,或简写p,默认的动作,即应用此提交
  3. reword,或简写r。在变基时会应用此提交,但是在提交的时候允许用户修改提交说明
  4. edit,或简写e。在变基时会应用此提交,但是在应用后你可以进行内容编辑,需要使用git commit --amend执行提交,以便对提交进行修补。当用户执行git commit --amend完成提交后,还需要执行git rebase --continue继续变基操作。用户在变基暂停状态下可以执行多次提交,从而实现把一个提交分解为多个提交
  5. squash,简写s。该提交会与前面的提交压缩为一个
  6. fixup,简写f。类似动作squash,但是提交的提交说明被丢弃
  7. 可以通过修改变基任务文件中各个提交的先后顺序,进而改变最终变基后提交的先后顺序
  8. 可以通过改变变基任务文件,删除包含相应提交的行,这样该提交就不会被应用,进而在变基后的提交中被删除

在变基遇到冲突而暂停的情况下,先完成冲突解决(添加到暂存区,不提交),然后在恢复变基操作的时候使用该命令

1
git rebase --continue

在变基遇到冲突而暂停的情况下,跳过当前提交的时候使用,谨慎用

1
git rebase –abort

在变基遇到冲突而暂停的情况下,终止变基操作,回到之前的分支时候使用

1
git rebase –skip

将其它分支上的修改应用到当前分支

将远程仓库的更新,合并到当前分支上产生一个新的提交,如果工作区或者暂存区有未提交的更改,变基动作不会执行

1
2
git fetch origin master
git rebase origin/master

git branch

显示所有分支信息

1
2
git branch
git branch -v
1
2
3
4
5
6
7
$ git branch #显示所有分支名称
b1
* master
$ git branch -v #显示所有分支名称,同时显示最后的提交注释,*:代表当前分支
b1 45d9dfb v4 commit
* master 1c1e018 v5 commit

创建新分支

创建新分支之后,不会自动切换到新分支上

1
git branch <new-branch-name>

基于<commit>创建新分支,不会自动切换到新分支上

1
git branch <new-branch-name> <commit>

使用git checkout创建新分支,并自动切换到这个分支上

1
git checkout -b <new-branch-name>

使用git checkout基于标签创建新分支,并自动切换到这个分支上

1
git checkout -b <new-branch-name> <tag-name>

使用git checkout基于标签创建新分支,没有指定<new-branch-name>HEAD会进入到游离指针状态

1
git checkout -b <tag-name>
1
2
3
4
5
6
7
8
9
10
11
12
13
$ git checkout v5
Note: checking out 'v5'.
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 performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at 1c1e018... v5 commit

退出游离指针状态

1
2
git checkout <branch-name> #切换到其它分支
git checkout -b <new-branch-name> #将游离指针状态,切换到创建的新分支上

创建新分支来跟踪远程分支,并自动切换到这个分支上

1
git checkout -b <new-branch-name> <remote-name>/<remote-branch-name>

重命名分支

如果<new-branch-name>已经存在,重命名动作停止

1
git branch -m <old-branch-name> <new-branch-name>

如果<new-branch-name>已经存在,重命名动作继续,请谨慎操作

1
git branch -M <old-branch-name> <new-branch-name>
1
2
3
4
5
6
7
8
9
10
11
12
13
$ git branch -v
b1 1c1e018 v5 commit
b2 1c1e018 v5 commit
* master 1c1e018 v5 commit
$ git branch -m b2 b1
fatal: A branch named 'b1' already exists.
$ git branch -M b2 b1
$ git branch -v
b1 1c1e018 v5 commit
* master 1c1e018 v5 commit

删除分支

如果分支上存在未被合并的提交,删除动作停止

1
git branch -d <branch-name>

如果分支上存在未被合并的提交,删除动作继续,请谨慎操作

1
git branch -D <branch-name>
1
2
3
4
5
6
7
8
9
10
11
12
13
$ git branch -v
b1 2e6423e v6 commit on branch b1
* master 1c1e018 v5 commit
$ git branch -d b1 #提示b1分支上存在没有被合并的提交
error: The branch 'b1' is not fully merged.
If you are sure you want to delete it, run 'git branch -D b1'.
$ git branch -D b1
Deleted branch b1 (was 2e6423e).
$ git branch -v
* master 1c1e018 v5 commit

查看已经合并到当前分支的分支

1
git branch --merged

查看没有合并到当前分支的分支

1
git branch --no-merged
1
2
3
4
5
6
7
8
9
10
11
$ git branch -v
b1 1c1e018 v5 commit
b2 82fa977 v6 commit
* master 1c1e018 v5 commit
$ git branch --merged
b1
* master
$ git branch --no-merged
b2

git merge


有未提交修改情况下,不要执行merge!遵守这条警告,防患于未然

基本操作

1
git merge [<options>] [<commit>...]
  1. 默认合并成功后会自动提交,使用--no-commit选项,则合并后的内容会放入暂存区,需要手动提交
  2. <commit>可以是提交的SHA-1值、分支、标签等
  3. <commit>如果是多个提交,合并成功后,当前HEAD是第一个<commit>的父提交,第一个<commit>是第二个<commit>的父提交,依次类推
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 hist b2 #b2分支上有7个提交,领先master分支2个提交
* ece4a12 v7 commit
* 82fa977 v6 commit
* 1c1e018 v5 commit
* 45d9dfb v4 commit
* adba38b v3 commit
* c7ded78 v2 commit
* 45496b8 v1 commit
$ git hist master #master分支上有5个提交
* 1c1e018 v5 commit
* 45d9dfb v4 commit
* adba38b v3 commit
* c7ded78 v2 commit
* 45496b8 v1 commit
$ git merge 82fa977 ece4a12 #合并b2分支上的2个提交
Updating 1c1e018..ece4a12
Fast-forward
readme.md | 2 ++
1 file changed, 2 insertions(+)
$ git hist master
* ece4a12 v7 commit
* 82fa977 v6 commit
* 1c1e018 v5 commit
* 45d9dfb v4 commit
* adba38b v3 commit
* c7ded78 v2 commit
* 45496b8 v1 commit

不要Fast-Foward合并,会生成merge提交信息

1
git merge [<commit>...] --no-ff

不要Fast-Foward合并,手动生成merge提交信息

1
git merge [<commit>...] --no-ff -m"<Message>"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ git merge 82fa977 --no-ff
Merge made by the 'recursive' strategy.
readme.md | 1 +
1 file changed, 1 insertion(+)
$ git hist
* 2cc5c4c Merge commit '82fa977'
|\
| * 82fa977 v6 commit
|/
* 1c1e018 v5 commit
* 45d9dfb v4 commit
* adba38b v3 commit
* c7ded78 v2 commit
* 45496b8 v1 commit

将分支合并到当前分支

1
git merge <branch>

终止合并操作

冲突时执行中止merge操作,merge manual中说,这条命令会尽力恢复到Merge之前的状态(可能失败!)

1
git merge --abort

git tag

创建轻量级标签

轻量级标签就像是个不会变化的分支,实际上它就是个指向特定提交对象的引用。如果只是临时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题

1
git tag <new-tag-name>
1
$ git tag v1.0.0 #创建一个标签

创建含附注标签

含附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用GNU Privacy Guard (GPG)来签署或验证。一般我们都建议使用含附注型的标签,以便保留相关信息。

1
git tag -a <new-tag-name> -m "<Message>"

如果有自己的GPG私钥,还可以用GPG来签署标签,只需要把之前的-a改为-s

1
git tag -s <new-tag-name> -m "<Message>"

GPG签署工作

首先,在开始签名之前你需要先配置GPG并安装个人密钥。

1
2
3
4
5
6
$ gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
---------------------------------
pub 2048R/0A46826A 2014-06-04
uid Scott Chacon (Git signing key) <schacon@gmail.com>
sub 2048R/874529A9 2014-06-04

如果你还没有安装一个密钥,可以使用gpg --gen-key生成一个。

1
gpg --gen-key

一旦你有一个可以签署的私钥,可以通过设置Gituser.signingkey选项来签署。

1
git config --global user.signingkey 0A46826A

如果已经设置好一个GPG私钥,可以使用它来签署新的标签。所有需要做的只是使用-s代替-a即可:

1
$ git tag -s v1.5 -m 'my signed 1.5 tag'

如果在那个标签上运行git show,会看到你的GPG签名附属在后面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ git show v1.5
tag v1.5
Tagger: Ben Straub <ben@straub.cc>
Date: Sat May 3 20:29:41 2014 -0700
my signed 1.5 tag
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQEcBAABAgAGBQJTZbQlAAoJEF0+sviABDDrZbQH/09PfE51KPVPlanr6q1v4/Ut
LQxfojUWiLQdg2ESJItkcuweYg+kc3HCyFejeDIBw9dpXt00rY26p05qrpnG+85b
hM1/PswpPLuBSr+oCIDj5GMC2r2iEKsfv2fJbNW8iWAXVLoWZRF8B0MfqX/YTMbm
ecorc4iXzQu7tupRihslbNkfvfciMnSDeSvzCpWAHl7h8Wj6hhqePmLm9lAYqnKp
8S5B/1SSQuEAjRZgI4IexpZoeKGVDptPHxLLS38fozsyi0QyDyzEgJxcJQVMXxVi
RUysgqjcpT8+iQM1PblGfHR4XAhuOqN5Fx06PSaFZhqvWFezJ28/CLyX5q+oIVk=
=EFTF
-----END PGP SIGNATURE-----
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
changed the version number

要验证一个签署的标签,可以运行git tag -v [tag-name]。这个命令使用GPG来验证签名。为了验证能正常工作,签署者的公钥需要在你的钥匙链中。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ git tag -v v1.4.2.1
object 883653babd8ee7ea23e6a5c392bb739348b1eb61
type commit
tag v1.4.2.1
tagger Junio C Hamano <junkio@cox.net> 1158138501 -0700
GIT 1.4.2.1
Minor fixes since 1.4.2, including git-mv and git-http with alternates.
gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A
gpg: Good signature from "Junio C Hamano <junkio@cox.net>"
gpg: aka "[jpeg image of size 1513]"
Primary key fingerprint: 3565 2A26 2040 E066 C9A7 4A7D C0C6 D9A4 F311 9B9A

如果没有签署者的公钥,那么你将会得到类似下面的东西:

1
2
3
gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A
gpg: Can't check signature: public key not found
error: could not verify the tag 'v1.4.2.1'

在最新版本的Git中(v1.7.9及以上),也可以签署个人提交。 如果相对于标签而言你对直接签署到提交更感兴趣的话,所有要做的只是增加一个-Sgit commit命令。

1
2
3
4
5
6
7
8
9
10
$ git commit -a -S -m 'signed commit'
You need a passphrase to unlock the secret key for
user: "Scott Chacon (Git signing key) <schacon@gmail.com>"
2048-bit RSA key, ID 0A46826A, created 2014-06-04
[master 5c3386c] signed commit
4 files changed, 4 insertions(+), 24 deletions(-)
rewrite Rakefile (100%)
create mode 100644 lib/git.rb

git log也有一个--show-signature选项来查看及验证这些签名。

1
2
3
4
5
6
7
8
$ git log --show-signature -1
commit 5c3386cf54bba0a33a32da706aa52bc0155503c2
gpg: Signature made Wed Jun 4 19:49:17 2014 PDT using RSA key ID 0A46826A
gpg: Good signature from "Scott Chacon (Git signing key) <schacon@gmail.com>"
Author: Scott Chacon <schacon@gmail.com>
Date: Wed Jun 4 19:49:17 2014 -0700
signed commit

查看所有的标签

1
2
3
git tag
git tag -l
git tag --list
1
2
3
4
5
6
7
8
9
10
11
$ git tag
v1.0.0
v2.0.0
$ git tag -l
v1.0.0
v2.0.0
$ git tag --list
v1.0.0
v2.0.0

标签匹配查询

使用*支持模糊匹配

1
git tag -l | --list "tag-name*"
1
2
$ git tag -l "*1*"
v1.0.0

查看标签的信息

1
git tag show <tag-name>

删除标签

1
git tag -d <tag-name>

文件内容追溯

追溯整个文件的内容

会逐行显示文件,在每一行的行首显示此行最早是在什么版本引入的,由谁引入的

1
git blame <file>
1
2
3
4
5
6
7
$ git blame readme.md
^45496b8 (Jerry.Chen 2016-01-23 18:46:54 +0800 1) v1
c7ded782 (Jerry.Chen 2016-01-23 18:47:08 +0800 2) v2
adba38bc (Jerry.Chen 2016-01-23 18:47:35 +0800 3) v3
45d9dfb1 (Jerry.Chen 2016-01-23 21:52:08 +0800 4) v4
1c1e0188 (Jerry.Chen 2016-01-23 22:13:49 +0800 5) v5
82fa9774 (Jerry.Chen 2016-01-23 23:37:39 +0800 6) v6

追溯指定的行号

n:起始行号;m:结束行号,如果未指定,则使用文件的最后一行的行号

1
git blame -L <n[,m]> <file>
1
2
3
4
5
6
7
8
9
$ git blame readme.md -L 3
adba38bc (Jerry.Chen 2016-01-23 18:47:35 +0800 3) v3
45d9dfb1 (Jerry.Chen 2016-01-23 21:52:08 +0800 4) v4
1c1e0188 (Jerry.Chen 2016-01-23 22:13:49 +0800 5) v5
82fa9774 (Jerry.Chen 2016-01-23 23:37:39 +0800 6) v6
$ git blame readme.md -L 3,4
adba38bc (Jerry.Chen 2016-01-23 18:47:35 +0800 3) v3
45d9dfb1 (Jerry.Chen 2016-01-23 21:52:08 +0800 4) v4

git ls-files

显示暂存区和版本库中的文件

1
git ls-files

查看历史版本的文件列表

1
git ls-files --with-tree=HEAD^

git rm

工作区删除文件

1
git rm <file>
1
2
3
4
5
6
7
8
9
$ git rm readme.md
rm 'readme.md'
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: readme.md

暂存区删除文件

从暂存区删除文件,工作区不会删除。删除的文件在工作区存在,状态变为未追踪

1
git rm --cached <file>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git rm --cached readme.md
rm 'readme.md'
$ git status
On branch master
Changes to be committed: #暂存区有待提交的删除内容
(use "git reset HEAD <file>..." to unstage)
deleted: readme.md
Untracked files: #删除的文件在工作区存在,状态变为未追踪
(use "git add <file>..." to include in what will be committed)
readme.md

git clean

显示将要被删除的目录和文件

针对那些未被跟踪的文件和目录

1
git clean -nd

删除掉未被跟踪的文件和目录

1
git clean -fd

git mv

移动或者重命名文件

如果移动或者重命名文件操作成功会添加到暂存区

1
git mv [<options>] <source>... <destination>

-n | --dry-run:演练移动或者重命名文件,看看操作是否会成功

1
git mv -n | --dry-run <source>... <destination>
1
2
3
4
5
6
7
$ git mv readme.md t1.md -n #演练将readme.md重命名为t1.md
Checking rename of 'readme.md' to 't1.md'
Renaming readme.md to t1.md
$ git status #查看状态,没有修改的内容
On branch master
nothing to commit, working directory clean

-f | --force:强制移动或者重命名文件,不管目标是否已经存在

1
git mv -f | --force <source>... <destination>

git log

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
git log 显示提交历史信息
git log <branch> 显示分支提交历史信息
git log -(n) 仅显示最近的n条提交
git log <file> 显示该文件提交信息
git log -p 显示每次提交的内容差异
git log -p -2 仅显示最近的两次更新
git log --stat 显示简要的增改行数统计
git log --date-order --date=iso --graph --full-history --all --pretty=format:'%x08%x09%C(red)%h %C(cyan)%ad%x08%x08%x08%x08%x08%x08%x08%x08%x08%x08%x08%x08%x08%x08%x08 %C(bold blue)%aN%C(reset)%C(bold yellow)%d %C(reset)%s'
-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(后跟指
定格式)。
format选项说明
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用-date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明
git log --pretty="%h:%s" --author=gitster --since="2008-10-01" --before="2008-11-01" --no-merges
--since, --after 仅显示指定时间之后的提交。
--until, --before 仅显示指定时间之前的提交。
--author 仅显示指定作者相关的提交。
--committer 仅显示指定提交者相关的提交。
git log --oneline --decorate 显示tag信息

忽略文件

工作区创建文件.gitignore

工作区创建文件.gitignore,然后在里面添加忽略规则。.gitignore这个文件本身会提交到版本库中去,用来保存的是公共的需要排除的文件。.gitignore的语法规范如下:

  1. 开头的行是注释
  2. /做为行尾字符,则要忽略的是目录
  3. !做为行首字符,则是取反模式
  4. 空白行不做处理
  5. 支持正则表达模式匹配
    * 匹配零个或多个任意字符
    [abc] 匹配任何一个在方括号中的字符
    ? 匹配任意一个字符
    [0-9a-zA-Z] 在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如[0-9a-zA-Z]表示匹配所有09的数字和所有字母)
    \ 转义字符
    [ ^abc] 匹配不是a, b, c中任一字符即可
    {ab,bb,cx} 匹配ab, bb, cx中任一类型即可
1
2
3
4
5
6
7
8
9
10
11
12
$ touch .gitignore
$ ls -a
./ ../ .git/ .gitignore readme.md
$ vi .gitignore
.a # 忽略所有 .a 结尾的文件
!lib.a # 但 lib.a 除外
/TODO # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
build/ # 忽略 build/ 目录下的所有文件
doc/*.txt # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt

注意:.gitignore只能忽略那些没有被版本库追踪的文件,如果文件已经提交到了版本管理中,则在.gitignore中对这些文件的忽略是无效的,可以使用以下方式进行临时忽略

1
2
git update-index --assume-unchanged /path/to/file #忽略跟踪
git update-index --no-assume-unchanged /path/to/file #恢复跟踪

但此方式在git rebase操作之后,这些被忽略的文件内容如果被修改过了,会被强制恢复到git rebase这个时候的版本内容。如果要从根本上忽略这些文件,请使用git rm删除掉这些文件,然后提交到版本库中

编辑.git/info/exclude

独享忽略列表,不需要提交到版本库中,不会影响其他人

1
$ vi .git/info/exclude

用户目录下的全局.gitignore

在用户目录下创建~/.gitignore文件,以同样的规则来添加哪些文件是不需要版本控制的

1
git config --global core.excludesfile ~/.gitignore #需要执行此命令,才能是全局配置生效

git grep

操作语法

1
git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]
1
git grep "工作区文件内容搜索"

windows平台下的msysGit中shell环境配置


编辑配置文件/etc/inputrc,修改或者添加以下配置,重启后就能在shell界面输入中文

1
2
3
4
5
# disable/enable 8bit input
set meta-flag on
set input-meta on
set output-meta on
set convert-meta off

ls命令显示中文,将alias命令添加到配置文件/etc/profile

1
2
alias ls='ls --show-control-chars --color=auto'
alias gitk='gitk --all' #设置别名

解决gitkGit Gui乱码,在/etc/gitconfig中添加或修改以下配置

1
2
[gui]
encoding = utf-8

/etc/git-completion.bash末尾位置添加cd /d,可以让msysGit打开定位到D


windows平台下的msysGit工具

gitk

显示所有的分支

1
gitk --all

显示某天以来的所有提交

1
gitk --since="[YYYY-MM-dd]|[2 days ago][2 weeks ago]..."

gui

1
git gui
您的赞赏将鼓励我继续分享!
Fork me on GitHub