先看这个图
查看git的某个命令的帮助
git add
学习:
1
2
|
# 添加当前目录的所有文件到暂存区
git add .
|
git rm
要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。
1
2
3
4
5
|
# 删除工作区文件,并且将这次删除放入暂存区
git rm [file1] [file2] ...
# 停止追踪指定文件,但该文件会保留在工作区
git rm --cached [file]
|
git mv
运行 git mv 就相当于运行了下面三条命令:
mv README.md README
git rm README.md
git add README
git log
Git - 查看提交历史
1
2
3
4
5
6
7
8
|
# 以图表形式查看 分支 log
git log --graph
# 精简显示...
git log –pretty=oneline
# 查看当前 仓库 的操作日志
git reflog
|
git log
只能查看以当前状态为终点的历史日志。
git reflog
可查看当前仓库的操作日志。
查看项目分支历史:
git log --oneline --decorate --graph --all
查看某文件的历史:
1
2
3
4
5
6
|
git log -- [filename]
# -p (补丁格式)显示每次提交的内容差异; 你也可以加上 -2 来仅显示最近两次提交
git log -p [filename]
# 如果有的话也可以使用 gitk
gitk [filename]
|
作者 和 提交者 之间究竟有何差别, 其实作者指的是实际作出修改的人,提交者指的是最后将此工作成果提交到仓库的人。 所以,当你为某个项目发布补丁,然后某个核心成员将你的补丁并入项目时,你就是作者,而那个核心成员就是提交者。
git diff
1
2
3
4
5
6
7
8
9
10
11
|
# 显示暂存区和工作区的差异
git diff
# 显示暂存区和上一个commit的差异
git diff --cached [file]
# 显示工作区与当前分支最新commit之间的差异
git diff HEAD
# 显示今天你写了多少行代码
git diff --shortstat "@{0 day ago}"
|
git commit
理解提交见: Git - 分支简介
1
2
3
4
5
6
7
8
9
10
|
# 同时add和commit,但不适用于新文件
#git commit -am "不建议使用-a选项"
# 提交时显示所有diff信息
git commit -v
# 签署提交(使用gpg key) 使用 -S 选项
git commit -S -m 'signed commit签署提交'
# 然后可以使用下面的命令查看和验证签名
git log --show-signature -1
|
在Android Studio中使用GPG对提交进行签名,见本博客的相关笔记 ,或 Signed commits · Wiki · akwizgran / briar · GitLab
该方法是对某工程设置 Signed commits,在该工程中的所有提交都使用GPG签名的方法。也可尝试在git的全局配置文件中进行配置,以用于所有提交。
Git 的提交信息怎么写?(英文)
git commit –amend
Git - 撤消操作:
主要涉及三个方面的撤销:
- 更改了工作区文件后,撤消对文件的修改。
- 运行了命令
git add
后,取消暂存的文件。
- 运行了命令
git commit
后,修改此次提交。
git commit --amend
的两个用法:
- 提交完了才发现漏掉了几个文件没有 add
- 修改上一次提交信息
注意事项: 请勿修改已经发布(push)的提交记录
1
2
3
4
5
6
7
|
# 重做上一次commit,包含包指定文件的新变化
git commit --amend [file1] [file2]
# 工作流程
git commit -m 'initial commit'
git add forgotten_file
git commit --amend
|
1
2
3
|
# 使用一次新的commit,替代上一次提交
# 如果代码没有任何新变化,则用来改写上一次commit的提交信息
git commit --amend
|
使用--amend
参数后,会打开配置文件中设置的文本编辑器,让你修改提交信息。请勿修改已经push到远程仓库的的提交记录(在这里犯了两次错误)。
git restore还原修改
git-restore - Restore working tree files
既然是还原,那么肯定需要有:
源
:源可以是 commit、branch 或 tag 等。如果未明确指定源
则默认源为:
- 工作树(工作目录)的默认源为索引(暂存区)
- 索引(暂存区)的默认
源
为 HEAD
目标
:可以是 暂存区(staged
)、工作目录(worktree
)。如果未明确指定目标
,则默认目标为工作树(工作目录)。如果要同时指定暂存区和工作目录这两个目标
,则还必须使用--source
来指定源
。
它们分别对应了 git-restore 的下面三个主要选项:
--source=<tree>
或 -s <tree>
:这里的<tree>
为源;
--staged
或-S
:
--worktree
或 -W
:
常见格式:
1
2
3
|
git restore [--worktree] <file>
git restore --staged <file>
git restore --staged --worktree --source HEAD <file>
|
常用命令:
1
2
|
# 通过当前HEAD中重置整个暂存区
git restore --staged .
|
另外的合并和覆盖选项请参考 restore 的文档
我们在执行 git add <file>
后,再次使用 git status
查看会发现如下提示:
早期版本:
1
2
3
4
|
$ git add *
$ git status
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
|
git 2.23版本:
1
2
3
4
5
|
$ git add *
$ git status
Changes to be committed: # 要提交的更改
use "git restore --staged <file>..." to unstage
# 使用"git restore--staged<file>.."取消保存
|
git 2.23版本新增了两个新命令,旨在替换 git checkout 的两种常用用法::
- git restore:从给定源(source)中还原变更。
- git switch:切换到指定的分支
文中所指的 index (索引) 表示 暂存区
github - What is Git Restore Command ? what is the different between Git Restore and Git Reset? - Stack Overflow
Git - Undoing changes | Atlassian Git Tutorial 该文中说在使用多人共享的远程存储库时,撤销更改时需要额外考虑:
- The preferred method of undoing shared history is
git revert
. (远程存储库)
- Git reset 通常应该被认为是一种"本地"撤销方法
git revert 抵消提交
上面命令的原理是,在当前提交后面,新增一次提交,抵消掉上一次提交导致的所有变化。
也可添加 --no-commit
参数:只抵消暂存区和工作区的文件变化,不产生新的提交。
如何撤销 Git 操作? - 阮一峰
git reset 丢弃提交
git reset : 将当前的HEAD重置为指定状态。
只要提供目标时间点的哈希值,即可进行回溯至该时间点的状态(包括:HEAD、暂存区、当前工作树)
git reset有多个模式,其中--hard
是模式之一,另还有默认的--mixed
(不改变当前工作树);详见:git help reset
必须注意,--hard
标记是 reset
命令唯一的危险用法,它也是 Git 会真正地销毁数据的仅有的几个操作之一(因为--hard
会同时重置暂存区和工作区,那么工作目录如果有修改将会被完全丢弃,除非你明确要丢弃工作目录的变动)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变
git reset <file>
git reset HEAD <file>
# 回溯到 哈希值 指定的时间点
git reset --hard 某哈希值如82b9b79
# 同时重置暂存区和工作区,与上一次commit保持一致
git reset --hard
# 回溯到上上一个版本,可以一直加 ^
git reset --hard HEAD^^
# 回溯5个版本, 用 ~5
git reset --hard HEAD~5
|
进阶:Git - 重置揭密 这个不错,也讲了reset 和 checkout 的区别
使用reset 撤销暂存的文件使用场景之一是:你先add之后再使用reset取消暂存
graph LR
HEAD --"②git reset file<br>将已提交的文件<br>复制到暂存区"--> Index暂存区
工作目录--"①git add file<br>"-->Index暂存区
使用checkout撤销对文件的修改,使用情况是:你工作目录中的文件进行了错误的修改,你想撤销对该文件的修改,该命令为 git checkout -- <filename>
graph LR
HEAD--"git checkout -- file<br>①将该文件覆盖到"-->暂存区--"②同时覆盖到"-->工作目录
git checkout 撤销
git checkout :切换分支或还原工作树文件
利用git checkout
撤销某文件的修改:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 此时会有两种情况:
# 1. 该文件还没有放入暂存区;此时撤销后内容与版本库中一样
# 2. 该文件已经放入暂存区;此时撤销后内容与添加到暂存区中时的一样(感觉不对)
# 这里 -- 的作用是明确表示后面是某个文件而非某个分支
git checkout -- <filename>
# 放弃某个文件的所有本地修改
git checkout HEAD <file>
# 恢复某个commit 的指定文件到暂存区和工作区
git checkout [commit] [file]
# 恢复暂存区的所有文件到工作区
git checkout .
|
可利用git checkout
分离HEAD
git branch
分支学习:
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 branch
git branch -vv
# 列出所有远程分支
git branch -r
# 列出所有本地分支和远程分支
git branch -a
git branch -av
# 新建一个分支,但依然停留在当前分支
git branch [branch-name]
# 新建一个分支,与指定的远程分支建立追踪关系
git branch --track [branch] [remote-branch]
# 或使用
git branch -u [remote-branch] [branch]
# 删除分支
git branch -d [branch-name]
# git 1.7.0 之后: 删除远程分支.
git push origin --delete [branch-name]
git branch -dr [remote/branch]
# git 1.7.0 之前: 删除远程分支
# 通过推送一个空分支到远程,来达到删除远程分支的目的
# 冒号左边为空,表是空分支
git push origin :<branchName>
|
Git 的分支,其实本质上仅仅是指向提交对象的可变指针。创建分支仅仅是创建一个可移动的指针。
由于 Git 的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,所以它的创建和销毁都异常高效。(该指针是使用一个文件表示?)
创建分支:
图: 两个指向相同提交历史的分支,其中HEAD 指向当前所在的分支
**分支切换:**
做了两个事情,1.是使HEAD指向testing;2.将工作目录变为testing指向的快照内容。
git checkout切换分支
新命令: git-switch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 切换到指定分支,并更新工作区
git checkout [branch-name]
# 创建并切换分支
git checkout -b 分支名称
# 在develop分子的基础上创建一个feature-x分支,并切换
git checkout -b feature-x develop
# 获取远程仓库的feature-D分支
# 并在本地仓库中创建对应的名称同样为feature-D的分支,并切换
git checkout -b feature-D origin/feature-D
# 切换回上一个分支
git checkout -
|
1
|
git checkout 231333head
|
git merge合并
1
2
3
4
5
|
# 合并指定分支到当前分支
git merge [branch]
# 创建合并提交「常用」。在合并的时候进行提交 --no-ff
git merge --no-ff [要合并进来的分支名称]
|
两个分支之间的合并工作流程:
- 使用
git checkout
切换到要进行合并的分支上
- 使用
git merge
将某分支合并到当前分支
- 使用
git branch -d
删除被合并的分支(肯定会删除该指针「文件」;那些只属于该分支的对象是如何处理,也是直接删除?)
merge的使用场景:
- 一台本地电脑,一个远程托管仓库(Github) :此时会有两个分支,一个本地分支一个远程分支。如果这两个分支之间参数冲突则需要使用 git merge 进行合并。
- 其他复杂情况就不说了。
解决合并冲突:
步骤:
- 使用
git status
查看哪些文件中有冲突
- 修改这些文件中出现特殊区段(特殊标记)的地方
- 对每个文件使用
git add
命令来将其标记为冲突已解决
- 使用
git commit
来完成合并提交
出现合并冲突后,此时 Git 做了合并,但是没有自动地创建一个新的合并提交。 Git 会暂停下来,等待你去解决合并产生的冲突。 你可以在合并冲突后的任意时刻使用 git status 命令来查看那些因包含合并冲突而处于未合并(unmerged)状态的文件。
git remote
与远程相关的操作参考:
git remote add 添加远程仓库:
1
2
3
|
git remote add [远程仓库的本地别名(远程引用)] [地址]
# 示例(默认别名为 origin,可省略)
git remote add origin http://127.0.0.1:8081/TestGroup/Test.git
|
使用git remote add
将路径中的仓库设置为本地仓库的远程仓库;并将远程仓库的名称(别名)设置为 origin (默认就是这个名称)
查看远程仓库的地址:
1
2
3
|
git remote
# 使用 -v 同时列出 URL
git remote -v
|
1
2
|
git remote rm [别名]
git remote rename [别名]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
git remote show [别名]
# 该命令的输出示例:
$ git remote show origin-gitlab
* 远程 origin-gitlab
fetch获取地址:[email protected]:faner/faner.gitlab.io.git
push推送地址:[email protected]:faner/faner.gitlab.io.git
HEAD 分支:master
远程分支:
master 已跟踪
为 'git pull' 配置的本地分支:
master 与远程 master 合并
为 'git push' 配置的本地引用:
master 推送至 master (最新)
|
移除远程仓库:
1
|
$ git remote remove origin
|
git clone
1
2
3
4
|
git clone [仓库提供的url]
# 示例
git clone https://github.com/github-book/git-tutorial.git
|
执行该命令后,我们默认是处于master分支(只获取了master分支),同时系统会自动将origin设置成该远程仓库的标识符。
git branch -a
同时查看本地和远程仓库的分支信息。
之后再获取其他分支的命令:
1
2
3
|
# 获取远程仓库的feature-D分支
# 在本地仓库中创建对应名称也为 feature-D 的分支,并切换
git checkout -b feature-D origin/feature-D
|
本地与远程的操作: 从远程仓库获取数据 和 向远程仓库传输数据。
查看一个git clone
克隆下来的仓库,默认做了哪些动作 :
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
|
[fan 15:07:01]/tmp/github/jekyll-demo$ git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/gh-pages
remotes/origin/master
remotes/origin/pr-patch-1
[fan 15:05:25]/tmp/github/jekyll-demo$ git remote show origin
* 远程 origin
获取地址:https://github.com/FanDean/jekyll-demo.git
推送地址:https://github.com/FanDean/jekyll-demo.git
HEAD 分支:master
远程分支:
gh-pages 已跟踪
master 已跟踪
pr-patch-1 已跟踪
为 'git pull' 配置的本地分支:
master 与远程 master 合并
为 'git push' 配置的本地引用:
master 推送至 master (最新)
[fan 15:13:41]/tmp/github/jekyll-demo$ git log --oneline --decorate --graph --all
* 523f0b2 (origin/pr-patch-1) Create pr-test1.md
* f567581 (HEAD -> master, origin/master, origin/HEAD) 修改README.md
* 3ced95a 删除不要的文件,创建README.md
| * e843202 (origin/gh-pages) Update 2017-06-29-本博客搭建过程.md
| * 4603c83 添加第一篇文章
| * 3596f40 update 配置文件,将github图标指向自己
| * 2d3eb8b 构建静态网站
|/
* 09136d3 初始化仓库
[fan 15:23:44]/tmp/github/jekyll-demo$ git log --oneline --decorate --graph
* f567581 (HEAD -> master, origin/master, origin/HEAD) 修改README.md
* 3ced95a 删除不要的文件,创建README.md
* 09136d3 初始化仓库
[fan 15:24:48]/tmp/github/jekyll-demo$ git log --oneline --graph
* f567581 修改README.md
* 3ced95a 删除不要的文件,创建README.md
* 09136d3 初始化仓库
|
git fetch
从远程仓库获取分支或分支的更新。
使用此命令的时候,可多加利用 tab 补全。
1
2
3
4
5
6
7
|
# 取回远程仓库所有分支的更新
git fetch [remote]
# 取回特定分支的更新
git fetch [remote] [分支名称]
# 示例:取回origin主机的master分支
git fetch origin master
|
git fetch 命令会访问远程仓库,从中拉取所有你还没有的数据。 执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。 必须注意 git fetch
命令会将数据拉取到你的本地仓库 - 它并不会自动合并或修改你当前的工作。
所取回的更新,在本地主机上要用"远程主机名/分支名"的形式访问。比如origin主机的master,就要用origin/master
访问。
使用git fetch
获取到整个远程分支或该分支的更新后的下一步:进行 查看 或 合并 。
查看: 取回远程主机的更新以后,可以在它的基础上,使用git checkout
命令创建一个新的分支:
1
|
git checkout -b newBrach origin/master
|
合并: 也可以使用git merge命令或者git rebase命令,在本地分支上合并远程分支:
1
2
3
4
|
# 在当前分支上,合并origin/master
git merge origin/master
# 或者
git rebase origin/master
|
git pull
git pull命令的作用是,取回远程主机某个分支的更新,再与本地的指定分支合并。
完整语法:
1
2
|
# 基本语法:远程 -> 本地
git pull <远程主机名> <远程分支名>:<本地分支名>
|
示例:
1
2
3
4
5
6
7
8
|
# 取回origin主机的next分支,并与本地master分支合并
git pull origin next:master
# 取回远程仓库的变化,并与本地当前分支合并
git pull origin feature-D
# 等同于
git fetch origin
git merge origin/feature-D
|
在某些场合,Git会自动在本地分支与远程分支之间,建立一种追踪关系(tracking)。比如,在git clone
的时候,所有本地分支默认与远程主机的同名分支,建立追踪关系,也就是说,本地的master分支自动"追踪"origin/master
分支。
Git也允许手动建立追踪关系:
1
2
3
4
|
# 指定master分支追踪origin/next分支;--set-upstream也可工作,但是会提示其已经废弃;另参考后文的跟踪分支。
git branch --set-upstream master origin/next
# 推荐方法 (仓库的位置相反,如不确定可运行 git help branch 查看)
git branch -u origin/next master
|
如果当前分支与远程分支存在追踪关系,git pull
就可以省略远程分支名。
1
2
|
# 本地的当前分支自动与对应的origin主机"追踪分支"(remote-tracking branch)进行合并
git pull origin
|
git push
git push 推送到远程仓库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# 基本语法:本地 -> 远程
git push <远程主机名> <本地分支名>:<远程分支名>
# 几种省略写法:
# 1. 省略远程分支名
# 将当前分支master的内容推送给与远程仓库origin存在追踪关系的分支。
git push origin master
# 2. 省略本地分支名
# 推送一个空的分支到远程,相当于删除远程主机上的master分支
git push origin :master
# 3.同时省略本地和远程分支
# 当前分支与远程分支之间存在追踪关系
git push origin
# 4. 全部省略
# 当前分支只有一个追踪分支,那么主机名都可以省略。
git push
# 推送所有分支到远程仓库
git push [remote] --all
|
注意 : git push 不带任何参数时的行为与 Git 的一个名为 push.default
的配置有关。它的默认值取决于你正使用的 Git 的版本,但是在教程中我们使用的是 upstream。 这没什么太大的影响,但是在你的项目中进行推送之前,最好检查一下这个配置。
追踪分支:
1
2
3
4
|
# 追踪分支
git push -u origin master
# 该命令的作用:将本地的master分支推送到origin主机(如果origin远程主机上还没有对应的分支,则会在origin上创建一个master分支),同时指定 origin 为默认远程主机
# -u 参数是 --set-upstream 的简写 将 origin仓库的master分支设置为本地master分支的upstream(上游)。
|
git cherry-pick
1
2
|
# 选择一个commit,合并进当前分支
git cherry-pick [commit]
|
git tag
Git - 打标签
轻量标签:它只是一个特定提交的引用。通常用作临时标签。
附注标签:是存储在 Git 数据库中的一个完整对象。通常建议创建附注标签。
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.8.5系列标签
git tag -l 'v1.8.5*'
# 使用 -a 创建附注标签 v1.4 ,-m 指定标签信息
git tag -a v1.4 -m "my version 1.4"
# 查看标签 v1.4 的信息与对应的提交信息
git show v1.4
# 后期打标签:为过去的某次提交打标签
git tag -a tagName 某次提交
# 默认情况下,git push 命令并不会传送标签到远程仓库服务器上
# 显式地推送某个标签到远程仓库
git push origin别名 tagName
# 将所有标签推送到远程仓库
git push origin --tags
# git 1.7之后,删除远程tag
git push origin --delete tag <tagname>
# git 1.7 之前:删除远程标签。
# 原理是:推送一个空tag到远程来覆盖远程的tag
# 先删除本地tag
git tag -d [tagName]
# 从远程删除
# 冒号左边为空,表是空tag
git push origin :refs/tags/[tagName]
|
git archive
1
2
|
# 生成一个可供发布的压缩包
$ git archive
|
git submodule
使用git管理的项目开发中,如果碰到公共库和基础工具,可以通过submodule来添加子仓库。
可以查看 git submodule 的帮助文件很详细。
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
|
# 这里我把子仓库看作第三方依赖,并且假设你不会更改依赖,保存的目录为 lib
# 第三方依赖一般都会通过版本号来标记你所使用的版本
# 那么这里相当于是把子仓库的某次提交(通过哈希值记录)作为版本号
# 为当前仓库添加子仓库,并拉取文件
git submodule add <仓库地址> <lib>
# .git/config 文件中也添加了 submodule字段
# 现在查看一下状态
git status
# 可以看到增加了.gitmodule文件和lib文件,对是lib文件,该文件记录子模块某次提交时的哈希值
# 感觉就是父仓库并不管理lib目录,该目录由子模块自己管理
## 更新依赖
git submodule update
# 如果依赖是远程仓库则添加 --remote 远程,或者 --merge合并
git submodule update --remote --merge
# 查看子模块状态
git submodule status
## 删除依赖
git rm --cached <本地路径lib>
#删除子仓库目录
rm -rf <本地路径lib>
#删除.gitsubmodule里相关部分
#删除.git/config 文件里相关字段
# 并建议 删除后提交
git commit -a -m 'remove <本地路径lib> submodule'
|
TODO: 待补充,Git init 和 config (特别是别名)
学习资源
Git最佳实践
入门:
进阶:
深入理解Git:
GitHub学习
在线演练