Git 基础命令 =============== 本章节介绍 Git 仓库的基本操作。 仓库创建 ------------- 要开始创建一个本地仓库,用户可以选择从零初始化一个仓库,或者从一个给定的远程仓库克隆。 初始化仓库:init ^^^^^^^^^^^^^^^^^^^^^^^ 常规的初始化仓库,可以新建一个文件夹,然后在其中执行 `git init` 来初始化: .. code-block:: sh $ git init Initialized empty Git repository in project/.git/ 设置远程仓库:remote ^^^^^^^^^^^^^^^^^^^^^^^ 如果你有远程的仓库(比如 Github 仓库)需要推送文件,往往需要用 `git remote` 添加对应的远程仓库地址: .. code-block:: sh # 格式:git remote add $ git remote add origin https://github.com/wklchris/wklchris.github.io 其中,`origin` 是最常用的远程仓库名称;用户当然也可以选择其他的名称。 要管理当前仓库配置的远程仓库,可以使用 `git remote -v` 来查看: .. code-block:: sh $ git remote -v origin https://github.com/wklchris/wklchris.github.io (fetch) origin https://github.com/wklchris/wklchris.github.io (push) 上述返回的信息表示,当前仓库配置了名为 `origin` 的远程仓库;后缀的 fetch/push 表示取回与推送时使用的地址。 从远程仓库克隆:clone ^^^^^^^^^^^^^^^^^^^^^^^^ 用户也可以从指定的远程仓库网址(可能是 https, git, 或者 SSH 协议)取回整个仓库,便于在本地展开工作。比如将本站仓库克隆到本地: .. code-block:: sh git clone https://github.com/wklchris/wklchris.github.io 以上命令会自动在当前目录创建一个与远程仓库同名的文件夹(即 `wklchris.github.io` 文件夹),并将内容拖取到其中。远程仓库中的所有分支都会被同步,且远程仓库会在本地记录中被自动命名为 `origin` (就像默认的分支名叫 master 一样)。 提交文件 ------------- 在本地仓库中的修改,需要提交到版本控制记录。 检查文件状态:status ^^^^^^^^^^^^^^^^^^^^^^^^^ 在初始化仓库后,立刻使用 `git status` 命令会返回以下结果: .. code-block:: sh $ git status On branch master nothing to commit, working directory clean 这表示自从上次提交以来,Git 追踪的文件都没有发生变化;同时,也没有任何新的文件被检测到。 如果新建一个 `README` 文档,再运行此命令: .. code-block:: sh $ git status On branch master Untracked files: (use "git add ..." to include in what will be committed) README nothing added to commit but untracked files present (use "git add" to track) 这告诉我们发现了一个新的文件(untracked file),它还从没被 git 版本仓库记录过。 该命令还可以使用 `-s` 参数,生成一个简略列表。关于暂存、修改、追踪,可以参考 :ref:`git-status` 一节。 .. code-block:: sh $ git status -s M README MM Rakefile A lib/git.rb M lib/simplegit.rb ?? LICENSE.txt 上例中,偏右的 `M` 表示修改了尚未暂存,偏左的 `M` 表示修改并已暂存。`A` 表示一个新加入追踪的文件,最后 `??` 表示新检测到的未追踪的文件。你也可以使用 `-sb` 参数,这会显示你当前的分支信息。 暂存文件:add ^^^^^^^^^^^^^^^^^ 利用 `git add` 命令来将新文件(untracked)或未暂存(unstaged)文件提交到暂存区。下例 .. code-block:: sh $ git add README $ git status On branch master Changes to be committed: (use "git reset HEAD ..." to unstage) new file: README modified: test.py 你也可以通过 `git add *` 来暂存所有文件: .. code-block:: sh $ git add * 其他: * 通常版本控制只针对文本文件;例如 `.pdf` 或 `.jpg` 这类文件一般不加入暂存。 * 在暂存时使用 `-i` (或 `--interactive` )选项,可以进入交互式暂存界面。 忽略文件 (.gitignore) ^^^^^^^^^^^^^^^^^^^^^^^^^^ 当目录中有许多文件或者子目录无须交付 Git 进行版本控制时(比如 `.ipynb_checkpoints` ),新建一个 `.gitignore` 文件: .. code-block:: sh $ touch .gitignore 向其中添加内容来忽略匹配的文件: * `.gitignore` 文件特性: * 空行或以 '#' 开头的行会被忽略 * 使用 glob 模式进行匹配 * 以 `/` 开头防止匹配时递归 * 以 `/` 结尾确保匹配目录 * 以 `!` 开头表示取反 * **glob 模式特性** :glob 模式是 shell 使用的简化后的正则表达式。 * 用 `*` 表示匹配字符 0 到无穷次 * 用 `?` 表示匹配单个任意字符 * 用 `[...]` 匹配任意一个方括号内的字符(例如 `[acd]` 可以是 `a` `c` 或 `d`),用 `[x-y]` 匹配任意一个字符 `x` 与 `y` 之间的字符(例如 `[0-9]` 匹配任意一个阿拉伯数字) * 用 `**` 匹配任意中间目录,例如 `a/**/b` 可以匹配 `a/c/b` 与 `a/c/d/b`。 一个简单的例子: .. code-block:: sh *.a # 忽略所有扩展名为 .a 的文件 /A # 忽略当前目录下名为 A 的文件 A/ # 忽略文件夹 A 内的所有内容 B/*.pdf # 忽略文件夹 B 下的(不包括子文件夹) pdf 文件 B/**/*.pdf # 忽略文件夹 B 及其子文件夹中的 pdf 文件 如果你想要将一些后缀加入全局的忽略列表,可以在 `~` 目录下新建一个 `.gitignore_global` 文件,并使用命令: .. code-block:: sh $ git config --global core.excludesfile ~/.gitignore_global 这里有一个 `Github 仓库 `_ ,收录了许多编程语言的 `.gitignore` 文件样式,可以参考。 内容比对:diff ^^^^^^^^^^^^^^^^^^^ 如果你有修改了但尚未暂存的文件,使用 `git diff` 来查看 **尚未暂存的改动** : .. code-block:: sh $ git diff [] 如果不指定文件名,那么会查看两次版本快照所有文件的差异。 如果加入 `--staged/--cached` 选项,则可以查看暂存区与版本库中最新版本之间的差异: .. code-block:: sh $ git diff --staged [] diff 命令提供了众多的参数,我时而会用到的有: * `--name-only` 参数:用来显示哪些文件与比对的版本不同。如果无,则不返回任何信息。 * `--dirstat` 参数:用来显示分别是哪些子文件夹发生了改动,改动量的占比分别是多少。另外,也存在一个按子文件计算占比的 `--dirstat-by-file` 参数。 * `--stat` 参数:用来快速显示文件的更改数目(插入与删除的行数)。 比如利用 `--name-only` 参数,显示在子文件夹 `folder` 中哪些文件已修改并暂存了: .. code-block:: sh $ git diff --name-only --staged folder/ 该参数的输出结果可以帮助脚本来判断文件的更改情况。 提交更新:commit ^^^^^^^^^^^^^^^^^^^^^^^ 使用 `commit` 命令来提交**暂存区的所有内容**: .. code-block:: sh $ git commit 这时,需要你使用编辑器(默认是 Vim)来输入提交的说明文本。对于不熟悉 Vim 操作的用户,在输入内容后按 Esc 切换到 Normal 模式,再输入 `:wq` 命令即可保存并退出。 你也可以使用 `-m` 选项来避免打开编辑器: .. code-block:: sh $ git commit -m "Input text here." 提交后,控制台终端会显示该次提交的 SHA-1 校验、提交到的分支(关于分支的内容会在下文介绍)、修改的文件数量,以及修改的行数量。 最后,git 还提供了一种将工作区内所有被修改的文件(不包括新文件)直接暂存然后提交的选项 `-a` : .. code-block:: sh $ git commit -a -m "Input text here." 版本变更与回退 ---------------- 我们简单提到过 git 使用 HEAD 指针指向最新的一次提交。每一次提交的之前的紧邻提交称为父提交。比如次新的提交就是 `HEAD~` ,父提交的父提交是 `HEAD~2` (确切地说,`~` 指代的是第一父提交,第一父提交的第二父提交需要使用 `HEAD~2^2` )。 * 更多关于分支的内容,参考 :ref:`branch` 章节。 * 如果要回退的提交是一个合并提交,请参考 :ref:`revert-merge-commit` 部分的内容。 从工作目录回退:reset --hard ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 你对工作目录的内容做了修改,但尚未 `add` 到暂存区。现在你想放弃这些修改,回到上次 `commit` 之后的状态: .. warning:: 这是个危险的命令;由于被放弃的内容从未被提交,因此无法再找回。 .. code-block:: sh # 危险的命令! $ git reset --hard HEAD 这里 `HEAD` 是缺省值,可以省略;你也可以用 SHA-1 值(或其前 7 位)来指定要回退到的版本。本质是放弃并销毁上次 `commit` 以来所有的更改。 从暂存区回退:reset --mixed ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 你的修改已经 `add` 到暂存区,现在你想把暂存区清空,但在本地文件中仍保留这些更改: .. code-block:: sh $ git reset --mixed HEAD 这里的 `--mixed` 选项是缺省值,可以省略。该命令相当于取消了 `add` 命令,更改仍存在于文件中。 从版本回退:reset --soft ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 你的文件已 `commit` 到仓库记录中去,现在你想将 HEAD 指针移动到上一个 commit 的位置: .. code-block:: sh $ git reset --soft HEAD~ `HEAD~` 表示 HEAD 指针的父节点。 此时你的暂存区、工作目录并未被回退,仍保留着你的改动。本质是撤销了最近的一次 `commit` 命令。 .. warning:: **如果你要应用回退的版本已经推送到远程仓库,那么不要使用 reset 命令** ,因为 reset 命令会更改日志。请使用 `revert` 命令来新建一个提交,这个提交的内容将与你指定的版本一致: .. code-block:: sh $ git revert HEAD~ `revert` 命令在还原合并提交中也有作用,可以参考 :ref:`revert-merge-commit` 部分的内容。 修改提交:commit --amend ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. important:: 不建议在提交已被推送到远程的情况下对其进行更改。 如果在提交时忘记了 `add` 某个文件,或者其他需要修改提交的场合,使用 `--amend` 参数。例如: .. code-block:: sh $ git commit -m 'initial commit' $ git add forgotten_file $ git commit --amend 它会将暂存区内的修改追加到上个提交中去。如果没有任何修改,它允许你更改提交的说明文本。 如果修改时也不更改上次的 commit 信息,可以追加 `--no-edit` 选项: .. code-block:: sh $ git commit --amend --no-edit 查看提交历史:log ----------------------- 命令是 `log`,不过有很多有趣的参数细节: .. code-block:: sh $ git log 基本参数 ^^^^^^^^^^^^^^^^^^^^^ 这里将 `log` 命令的参数分为输出参数与过滤参数两种。输出参数主要有: * `-p` :查看提交内容的差异。 * `--abbrev-commit` :只显示简洁 SHA-1,一般是其前 7 个字符。 * `--color` :启用颜色。常用的颜色包括:red, green, yellow, blue, magenta, cyan, black, white, normal; 以及可在以上颜色之前加上格式 bold, dim, ul, blink, reverse. 例如:`%C(bold blue)`。 * `--graph` :用图像的方式显示你的分支历史。 * `--stat` :列出提交修改的文件以及一些基本修改的信息。 * `--shortstat` :只列出修改的文件数量和修改的行数。 * `--relative-date` :显示相对日期,即 "2 days ago" 这种格式。 * `--pretty=