Git 命令总结 - 第一篇
注:本系列文章内容均来自于 “Pro Git, Scott Chacon and Ben Straub. Second Edition”,未经允许,禁止转载。
初次运行 Git 前的配置
Git 自带一个 git config
的工具来帮助设置控制 Git 外观和行为的配置变量。 这些变量存储在三个不同的位
置:
/etc/gitconfig
文件: 包含系统上每一个用户及他们仓库的通用配置。 如果在执行git config
时带上--system
选项,那么它就会读写该文件中的配置变量。(由于它是系统配置文件,因此你需要管理员或超级用户权限来修改它。)~/.gitconfig
或~/.config/git/config
文件:只针对当前用户。 你可以传递--global
选项让 Git 读写此文件,这会对你系统上 所有 的仓库生效。- 当前使用仓库的 Git 目录中的
config
文件(即.git/config
):针对该仓库。 你可以传递--local
选项让 Git 强制读写此文件,虽然默认情况下用的就是它。(当然,你需要进入某个 Git 仓库中才能让该选项生效。)
Note: 每一个级别会覆盖上一级别的配置,所以 .git/config
的配置变量会覆盖 /etc/gitconfig
中的配置变量。
通过以下命令查看所有的配置以及它们所在的文件
$ git config --list --show-origin
用户信息
安装完 Git 之后,要做的第一件事就是设置你的用户名和邮件地址。 这一点很重要,因为每一个 Git 提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改:
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
再次强调,如果使用了 --globa
l 选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git 都会使用那些信息。 当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运行没有 --global
选项的命令来配置。
文本编辑器
如果你想使用不同的文本编辑器,例如 Emacs,可以这样做:
$ git config --global core.editor emacs
在 Windows 系统上,如果你想要使用别的文本编辑器,那么必须指定可执行文件的完整路径。 它可能随你的编辑器的打包方式而不同。
$ git config --global core.editor "'C:/ProgramFiles/Notepad++/notepad++.exe' -multiInst -notabbar -nosession -noPlugin"
检查配置信息
$ git config --list
你可能会看到重复的变量名,因为 Git 会从不同的文件中读取同一个配置(例如:/etc/gitconfig
与 ~/.gitconfig
)。 这种情况下,Git 会使用它找到的每一个变量的最后一个配置。
你可以通过输入git config <key>:
来检查 Git 的某一项配置。
$ git config user.name
John Doe
由于 Git 会从多个文件中读取同一配置变量的不同值,因此你可能会在其中看到意料之外的值而不知道为什么。 此时,你可以查询 Git 中该变量的原始值,它会告诉你哪一个配置文件最后设置了该值:
$ git config --show-origin rerere.autoUpdate
file:/home/johndoe/.gitconfig false
获取帮助
若你使用 Git 时需要获取帮助,有三种等价的方法可以找到 Git 命令的综合手册(manpage):
$ git help <verb>
$ git <verb> --help
$ man git-<verb>
例如,要想获得 git config
命令的手册,执行
$ git help config
此外,如果你不需要全面的手册,只需要可用选项的快速参考,那么可以用 -h
选项获得更简明的 “help”
输出:
$ git <verb> -h
Git 基础
获取 Git 仓库
通常有两种获取 Git 项目仓库的方式:
- 将尚未进行版本控制的本地目录转换为 Git 仓库;
- 从其它服务器 克隆 一个已存在的 Git 仓库。
两种方式都会在你的本地机器上得到一个工作就绪的 Git 仓库。
在已存在目录中初始化仓库
在 Linux 上:
$ cd /home/user/my_project
在 macOS 上:
$ cd /Users/user/my_project
在 Windows 上:
$ cd /c/user/my_project
之后执行:
$ git init
该命令将创建一个名为 .git
的子目录,这个子目录含有你初始化的 Git 仓库中所有的必须文件,这些文件是 Git 仓库的骨干。 但是,在这个时候,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪。
如果在一个已存在文件的文件夹(而非空文件夹)中进行版本控制,你应该开始追踪这些文件并进行初始提交。可以通过 git add
命令来指定所需的文件来进行追踪,然后执行 git commit
:
$ git add *.c
$ git add LICENSE
$ git commit -m 'initial project version'
克隆现有的仓库
克隆仓库的命令是 git clone <url>
。 比如,要克隆 Git 的链接库 libgit2
,可以用下面的命令:
$ git clone https://github.com/libgit2/libgit2
如果你想在克隆远程仓库的时候,自定义本地仓库的名字,你可以通过额外的参数指定新的目录名:
$ git clone https://github.com/libgit2/libgit2 mylibgit
记录每次更新到仓库
检查当前文件状态
$ git status
跟踪新文件
使用命令 git add
开始跟踪一个文件。 所以,要跟踪 README
文件,运行:
$ git add README
暂存已修改的文件
现在我们来修改一个已被跟踪的文件。 如果你修改了一个名为 CONTRIBUTING.md
的已被跟踪的文件,通过以下命令来暂存已修改的文件:
$ git add CONTRIBUTING.md
要暂存这次更新,需要运行 git add
命令。 这是个多功能命令:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。 将这个命令理解为 “精确地将内容添加到下一次提交中” 而不是 “将一个文件添加到项目中” 要更加合适。
运行了 git add
之后又作了修订的文件,需要重新运行 git add
把最新版本重新暂存起来。
状态简览
$ git status
$ git status -s 或者 git status --short 你将得到一种格式更为紧凑的输出。
忽略文件
一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以创建一个名为 .gitignore
的文件,列出要忽略的文件的模式。
GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore
文件列表, 你可以在 https://github.com/github/gitignore
找到它。
在最简单的情况下,一个仓库可能只根目录下有一个 .gitignore
文件,它递归地应用到整个仓库中。 然而,子目录下也可以有额外的 .gitignore
文件。子目录中的 .gitignore
文件中的规则只作用于它所在的目录中。 (Linux 内核的源码库拥有 206 个 .gitignore
文件。)
多个 .gitignore
文件的具体细节详见命令 man gitignore
。
查看已暂存和未暂存的修改
如果 git status
命令的输出对于你来说过于简略,而你想知道具体修改了什么地方,可以用 git diff
命令。
要查看尚未暂存的文件更新了哪些部分,不加参数直接输入 git diff
. 此命令比较的是工作目录中当前文件和暂存区域快照之间的差异。 也就是修改之后还没有暂存起来的变化内容。
若要查看已暂存的将要添加到下次提交里的内容,可以用 git diff --staged
或者 git diff --cached
命令。 这条命令将比对已暂存文件与最后一次提交的文件差异。
提交更新
在此之前,请务必确认还有什么已修改或新建的文件还没有 git add
过, 否则提交的时候不会记录这些尚未暂存的变化。 这些已修改但未暂存的文件只会保留在本地磁盘。 所以,每次准备提交前,先用 git status
看下,你所需要的文件是不是都已暂存起来了, 然后再运行提交命令 git commit
,这样会启动你选择的文本编辑器来输入提交说明。
$ git commit
你也可以在 commit
命令后添加 -m
选项,将提交信息与命令放在同一行,如下所示:
$ git commit -m "Story 182: Fix benchmarks for speed"
跳过使用暂存区域
尽管使用暂存区域的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。 Git 提供了一个跳过使用暂存区域的方式, 只要在提交的时候,给 git commit
加上 -a
选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add
步骤。
$ git commit -a -m 'added new benchmarks'
移除文件
要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。可以用 git rm
命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。
如果只是简单地从工作目录中手工删除文件,比如:$ rm PROJECTS.md
,运行 git status
时就看到 “Changes not staged for commit”
。然后再运行 git rm
记录此次移除文件的操作。下一次提交时,该文件就不再纳入版本管理了。 如果要删除之前修改过但是还没有放到暂存区的文件,则必须使用强制删除选项 -f
(译注:即 force
的首字母)。 这是一种安全特性,用于防止误删尚未添加到快照的数据,这样的数据不能被 Git 恢复。
另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。 换句话说,你想让文件保留在磁盘,但是并不想让 Git 继续跟踪。 当你忘记添加 .gitignore
文件,不小心把一个很大的日志文件或一堆 .a
这样的编译生成文件添加到暂存区时,这一做法尤其有用。 为达到这一目的,使用 --cached
选项:
$ git rm --cached README
移动文件
$ git mv file_from file_to
其实,运行 git mv
就相当于运行了下面三条命令:
$ mv README.md README
$ git rm README.md
$ git add README
查看提交历史
在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。 完成这个任务最简单而又有效的工具是 git log
命令。
$ git log
不传入任何参数的默认情况下,git log
会按时间先后顺序列出所有的提交,最近的更新排在最上面。 正如你所看到的,这个命令会列出每个提交的 SHA-1
校验和、作者的名字和电子邮件地址、提交时间以及提交说明。
$ git log -p 或者 $ git log --patch 会显示每次提交所引入的差异(按 补丁 的格式输出)
$ git log -2 只显示最近的两次提交
$ git log --stat 看到每次提交的简略统计信息
$ git log --pretty=oneline 会将每个提交放在一行显示,在浏览大量的提交时非常有用
$ git log --pretty=short 它们展示信息的格式基本一致,但是详尽程度不一
$ git log --pretty=full 它们展示信息的格式基本一致,但是详尽程度不一
$ git log --pretty=fuller 它们展示信息的格式基本一致,但是详尽程度不一
git log
的常用选项
选项 | 说明 |
---|---|
-p | 按补丁格式显示每个提交引入的差异 |
--stat | 显示每次提交的文件修改统计信息 |
--shortstat | 只显示 --stat 中最后的行数修改添加移除统计 |
--name-only | 仅在提交信息后显示已修改的文件清单 |
--name-status | 显示新增、修改、删除的文件清单 |
--abbrev-commit | 仅显示 SHA-1 校验和所有 40 个字符中的前几个字符 |
--relative-date | 使用较短的相对时间而不是完整格式显示日期(比如 “2 weeks ago”) |
--graph | 在日志旁以 ASCII 图形显示分支与合并历史 |
--pretty | 使用其他格式显示历史提交信息。可用的选项包括 oneline、short、full、fuller 和 format (用来定义自己的格式) |
--oneline | --pretty=oneline --abbrev-commit 合用的简写 |
限制输出长度
限制 git log
输出的选项
选项 | 说明 |
---|---|
-<n> | 仅显示最近的 n 条提交 |
--since, --after | 仅显示指定时间之后的提交 |
--until, --before | 仅显示指定时间之前的提交 |
--author | 仅显示作者匹配指定字符串的提交 |
--committer | 仅显示提交者匹配指定字符串的提交 |
--grep | 仅显示提交说明中包含指定字符串的提交 |
-S | 仅显示添加或删除内容匹配指定字符串的提交 |
$ git log --since=2.weeks 列出最近两周的所有提交
另一个非常有用的过滤器是 -S
(俗称“pickaxe”
选项,取 “用鹤嘴锄在土里捡石头” 之意), 它接受一个字符串参数,并且只会显示那些添加或删除了该字符串的提交。 假设你想找出添加或删除了对某一个特定函数的引用的提交,可以调用:
$ git log -S function_name
隐藏合并提交:按照你代码仓库的工作流程,记录中可能有为数不少的合并提交,它们所包含的信息通常并不多。 为了避免显示的合并提交弄乱历史记录,可以为 log
加上 --no-merges
选项。
撤消操作
有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 --amend
选项的提交命令来重新提交:
$ git commit --amend
例如,你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend
最终你只会有一个提交— —第二次提交将代替第一次提交的结果。当你在修补最后的提交时,并不是通过用改进后的提交 原位替换 掉旧有提交的方式来修复的, 理解这一点非常重要。从效果上来说,就像是旧有的提交从未存在过一样,它并不会出现在仓库的历史中。修补提交最明显的价值是可以稍微改进你最后的提交,而不会让 “啊,忘了添加一个文件” 或者 “小修补,修正笔误” 这种提交信息弄乱你的仓库历史。
取消暂存的文件, git status
会提示
$ git restore --staged <file> 新的 Git 版本(不清楚具体哪个版本开始更改的)
$ git reset HEAD <file> 旧的 Git 版本
撤消对文件的修改, git status
会提示
$ git restore <file> 新的 Git 版本(不清楚具体哪个版本开始更改的)
$ git checkout -- <file> 旧的 Git 版本
远程仓库的使用
查看远程仓库
$ git remote 会列出你指定的每一个远程服务器的简写
$ git remote -v 会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL
添加远程仓库
$ git remote add <shortname> <url>
$ git remote add pb https://github.com/paulboone/ticgit 现在你可以在命令行中使用字符串 pb 来代替整个 URL
$ git fetch pb 拉取 pb 仓库中有但你没有的信息
从远程仓库中抓取与拉取
$ git fetch <remote>
这个命令会访问远程仓库,从中拉取所有你还没有的数据。 执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。必须注意 git fetch
命令只会将数据下载到你的本地仓库——它并不会自动合并或修改你当前的工作。当准备好时你必须手动将其合并入你的工作。
$ git pull 会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支
$ git pull <remote> 自动抓取后合并该远程分支到当前分支
$ git clone 自动设置本地 master 分支跟踪克隆的远程仓库的 master 分支(或其它名字的默认分支)
自动抓取后合并该远程分支到当前分支。
推送到远程仓库
$ git push <remote> <branch>
$ git push origin master
查看某个远程仓库
$ git remote show <remote>
$ git remote show origin 会列出远程仓库的 URL 与跟踪分支的信息
这个命令列出了当你在特定的分支上执行 git push
会自动地推送到哪一个远程分支。 它也同样地列出了哪些远程分支不在你的本地,哪些远程分支已经从服务器上移除了, 还有当你执行 git pull
时哪些本地分支可以与它跟踪的远程分支自动合并。
远程仓库的重命名与移除
$ git remote rename name_from name_to 修改一个远程仓库的简写名
$ git remote remove reponame 移除一个远程仓库
$ git remote rm reponame 移除一个远程仓库
打标签
列出标签
$ git tag
$ git tag -l 或者 $ git tag --list 按照通配符列出标签
$ git tag -l "v1.8.5*"
创建标签
附注标签
$ git tag -a v1.4 -m "my version 1.4" -a 表示 annotated (附注), -m 选项指定了一条将会存储在标签中的信息
$ git show v1.4 显示标签信息和与之对应的提交信息
轻量标签
$ git tag v1.4 创建轻量标签,不需要使用 -a、-s 或 -m 选项,只需要提供标签名字
$ git show v1.4 只会显示出提交信息, 会看到额外的标签信息
后期打标签
$ git log --pretty=oneline 先查看提交历史, 会显示校验和以及 commit 描述
$ git tag -a v1.2 9fceb02 9fceb02 表示某次 commit 时的的部分校验和(或校验和)
共享标签
默认情况下,git push
命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上。 这个过程就像共享远程分支一样。
$ git push origin v1.5 origin 可以替换为你自己的远程仓库简名
$ git push origin --tags 要一次性推送很多标签
删除标签
$ git tag -d <tagname> 删除掉你本地仓库上的标签
$ git tag -d v1.4 示例
注意上述命令并不会从任何远程仓库中移除这个标签,你必须用
$ git push <remote> :refs/tags/<tagname>
$ git push origin :refs/tags/v1.4 示例
来更新你的远程仓库
或者直接使用
$ git push origin --delete <tagname>
来删除远程标签
检出标签
$ git checkout 2.0.0 查看某个标签所指向的文件版本
这会使你的仓库处于 “分离头指针(detached HEAD
)” 的状态——这个状态有些不好的副作用。在 “分离头指针” 状态下,如果你做了某些更改然后提交它们,标签不会发生变化, 但你的新提交将不属于任何分支,并且将无法访问,除非通过确切的提交哈希才能访问。 因此,如果你需要进行更改,比如你要修复旧版本中的错误,那么通常需要创建一个新分支:
$ git checkout -b version2 v2.0.0
Switched to a new branch 'version2'
如果在这之后又进行了一次提交,version2
分支就会因为这个改动向前移动, 此时它就会和 v2.0.0
标签稍微有些不同,这时就要当心了。
Git 别名
可以通过 git config
文件来轻松地为每一个命令设置一个别名。
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit 这意味着,当要输入 git commit 时,只需要输入 git ci
$ git config --global alias.st status
$ git config --global alias.unstage 'reset HEAD --' 添加你自己的取消暂存别名
这会使下面的两个命令等价:
$ git unstage fileA
$ git reset HEAD -- fileA
$ git config --global alias.last 'log -1 HEAD'
这会使下面的两个命令等价:
$ git last
$ git log -1 HEAD
$ git config --global alias.visual '!gitk' 想要执行外部命令,而不是一个 Git 子命令
这会使下面的两个命令等价:
$ git visual
$ gitk