Please enable Javascript to view the contents

Git常用命令

 ·  ☕ 16 分钟

先看这个图

查看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 抵消提交

1
$ git revert HEAD

上面命令的原理是,在当前提交后面,新增一次提交,抵消掉上一次提交导致的所有变化。

也可添加 --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 值字符串)的文件,所以它的创建和销毁都异常高效。(该指针是使用一个文件表示?)

创建分支:

1
git branch testing

运行git branch testing后

图: 两个指向相同提交历史的分支,其中HEAD 指向当前所在的分支
**分支切换:** 做了两个事情,1.是使HEAD指向testing;2.将工作目录变为testing指向的快照内容。
1
git checkout 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 进行合并。
  • 其他复杂情况就不说了。

解决合并冲突:

步骤:

  1. 使用git status查看哪些文件中有冲突
  2. 修改这些文件中出现特殊区段(特殊标记)的地方
  3. 对每个文件使用 git add 命令来将其标记为冲突已解决
  4. 使用 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:

  • 《Git权威指南》

GitHub学习

在线演练

  • Learn Git Branching非常好的在线练习网站,可选择中文版。以此网站为基础进行学习。
您的鼓励是我最大的动力
alipay QR Code

Felix
作者
Felix
如无必要,勿增实体。

3

目录