git 基本操作

概述

本文介绍使用 git 版本控制系统过程中所涉的各种基本命令。

书写 git 命令时,如果参数使用 [] 括起来,则表明其可使用也可不使用;如果参数使用 / 进行分隔,则表示两者等价,可任选一个使用;如果某个参数使用 <> 括起来,则表明此为一个取值。

获取仓库

通常有两种方式用于获取 git 仓库:

  1. 将尚未进行版本控制的本地目录转换为 Git 仓库。

    进入到指定的本地目录,执行 git init 命令即可将当前目录转换为 git 仓库。

  2. 从其它服务器克隆一个已存在的 Git 仓库。

    使用 git clone <url> [folderName] 命令可将位于 url 位置的 Git 仓库克隆至本地,并将其文件夹名更改为 folderName

git init 命令将会在本地创建一个 .git 文件夹,克隆得到的 git 仓库中同样存在 .git 文件夹。对于 git 仓库而言,此文件夹是十分重要的,它存放着当前 git 仓库的各种信息。关于此文件夹的详细信息,具体见 git 基本原理

文件追踪

Git 仓库所在目录中的文件共有五种状态 —— 未追踪 (untracked)、未修改 (unmodified)、已修改 (modified)、已暂存 (staged)、已提交 (commited)。如果一个文件处于未追踪状态,则表明其尚未被 Git 仓库所管理;如果一个文件处于未修改状态,则表明自上次提交后,该文件尚未被修改;如果一个文件处于已修改状态,则表明自上次提交后,该文件已被修改;如果一个文件处于已暂存状态,则表明其位于暂存区;如果一个文件处于已提交状态,则表明其刚被提交。

文件状态间转换关系及所涉操作具体如下图所示 (未修改和已提交其实是一种状态,之所以如此写,希望与上篇文章中所提文件状态保持一致):

图一:文件状态转换

首先,我们介绍用于查看文件状态的两个命令:

  • git status [--short]

    此命令用于查看当前目录中各文件所处状态 (如果文件处于未追踪、已修改、已暂存状态,则会列出其文件名)。添加 --short 参数后,该命令将会以一种简洁方式查看当前目录中各文件所处状态。

    如图所示,输入 git status 命令后,它指出 333.txt 处于未追踪状态、222.txt 处于已修改状态、111.txt 处于已暂存状态。

  • git diff [--staged/--cached]

    此命令用于查看工作区中文件与暂存区中文件的差异、暂存区中文件与仓库区中文件的差异 (此时需要使用 --staged 参数)。

    如图所示,输入 git diff 命令后,它就指出工作区中 222.txt 与暂存区中 222.txt 的差异 ( diff 命令输出中,使用 a/b/ 标识不同区内的文件,a 用于标识具有较旧内容的文件,b 用于标识具有较新内容的文件)。

接下来,我们介绍与状态转换相关的命令:

  • git checkout -- <fileNmae>

    此命令用于将处于已修改状态的文件回退为未修改状态的文件,即撤销工作区中文件的已有修改。如此操作,工作区中文件的已有修改将被完全覆盖,并永远不可恢复

  • git add

    此命令用于将处于未追踪或已修改状态的文件加入暂存区,从而将文件状态转换为已暂存。

    此命令通常有三种用法:

    1. git add . :将当前目录中所有符合条件的文件加入暂存区。
    2. git add *.xxx:将当前目录中满足 *.xxx 通配符匹配规则的文件加入暂存区。
    3. git add xxx:将当前目录中指定文件 xxx 加入暂存区。
  • git reset HEAD <fileName>

    此命令用于将处于已暂存状态的文件放回至工作区,从而将文件状态转换为已修改。该命令含有与 git add 命令含义刚好相反。

  • git commit[-m "xxx"]

    此命令用于将处于已暂存状态的文件提交到仓库区,这便形成一次快照。

    如果直接输入 git commit 命令,则其会跳转到编辑器页面,需要你输入提交说明,随后退出编辑器页面以完成提交;如果输入 git commit -m "xxx" 命令,则 xxx 作为提交说明并完成提交。

    如果我们希望跳过暂存操作,直接执行提交操作,可使用 git comit -am "xxx" 命令,它等价于 git add .,git commit -m "xxx" 命令。

    如果我们提交完一次快照后,发现若干文件忘记提交获取提交说明书写错误,此时可输入 git commit --amend 命令,它会将暂存区中内容提交至最新快照之中,同时启动编辑器页面,以允许你修改最新快照的提交说明。

  • git rm [-r] [--cached] xxx

    如果直接输入 git rm xxx,则其会将文件 xxx 从暂存区中删除,同时删除工作区中文件 xxx

    如果输入 git rm --cached xxx,则其仅会将文件 xxx 从暂存区中删除,此时文件状态变更为未追踪 (将文件从暂存区中删除,此操作将等待被提交给仓库区,故而会出现于 Changes to be commit)。

    参数 -r 表明所删内容为文件夹。

    如果工作区中文件与暂存区中文件不一致,则系统报错,此时可使用 git rm -f xxx 强制删除,这种操作的后果便是:工作区中修改部分将会永远不可恢复

    如果我们直接使用 rm xxx 命令,则其仅会删除工作区中文件 xxx,暂存区中的文件 xxx 仍然保留存在。

  • git mv [-r]

    此命令用于移动或重命名文件或文件夹。

    Git 并不显式跟踪文件移动,为实现跟踪文件移动功能,实际上我们需要借由如下三条命令加以实现:mv old new,git rm old,git add newgit rm 命令则是此三者命令的集合版本,可帮助简化跟踪文件移动操作。

  • git

最后我们介绍一下 .gitignore 文件。

对于一个项目而言,当前目录中的编译结果、日志输出等文件都是无需交由 Git 管理的,而且我们也不会希望它们处于未追踪状态,此时便可使用 Git 提供的 .gitignore 文件记录它们。如此操作,Git 便会忽略其中文件。

文件 .gitignore 的格式规范如下:

  • 所有空行或者以 # 开头的行都会被 Git 忽略。
  • 可以使用标准的 glob 模式(正则表达式的阉割版) 匹配,它会递归地应用在整个工作区中。
  • 匹配模式可以 (/) 开头防止递归。
  • 匹配模式可以 (/) 结尾指定目录。
  • 要忽略指定模式以外的文件或目录,可以在模式前加上叹号 (!),以示取反。

.gitignore 所匹配文件集合应当为:前四条规范所示集合取并集,随后与第五条规范所示集合取交集 (个人理解)。假定存在 .gitignore 文件如下:

1
2
*.a
!lib.a

此文件表示忽略除 lib.a 以外的所有以 .a 结尾的文件。

.gitignore 位于哪个文件夹之中,它就仅对此文件夹中所有文件起作用。

网址 https://github.com/github/gitignore 提供针对数十种项目及语言的 .gitignore 文件模板。

历史记录

我们可使用 git log 命令获取已提交的历史记录,其默认按照时间顺序排列。对于每条历史记录而言,它会列出此次记录的 SHA-1 校验值、提交人、提交人的电子邮件、提交时间及提交说明。

对于 git log 命令而言,它存在很多参数可以选择,我们在此列出若干经常使用的参数:

  • -p/--patch

    使用此参数后,除显示基本记录信息外,它还会显示每次提交所引入的差异 (类似于 git diff 当前记录 上次记录)。

  • -<n>

    此参数用于限制显示的历史记录条数。如果输入 git log -2 命令,它将仅显示最近的两条历史记录。

    另外还有若干用于筛选历史记录的参数,我们在此简单介绍一二:--since=<date>,筛选时间节点 date 之后的历史记录;--until=<date>,筛选时间节点 date 之前的历史记录。

  • --stat

    使用此参数后,除显示基本记录信息外,它还会显示每次提交的一些统计信息。

  • --pretty

    此参数存在若干选值:onlineshortfullfuller 等,选择不同选值,可以不同于默认格式的方式显示历史记录。

    如果输入 git log --pretty=online 命令,它会简化记录信息,并将每个记录放在一行进行显示。

    此参数存在一个特殊选值 format,它允许自定义显示方式 (具体比较麻烦,点到为止)。

  • --graph

    使用此参数后,除显示基本记录信息外,它狐疑在日志旁以 ASCII 图形显示分支与合并历史。

远程仓库

远程仓库是指托管在因特网或其他网络中你的项目的版本库。借助于远程仓库,我们可方便实现多人合作项目开发。

为完成协作开发,我们需要管理此远程仓库。此节首先给出管理仓库命令之间的关系,随后详细介绍他们。

图二:远程仓库相关操作

  • git remote [-v]

    此命令用于查看你所配置的远程服务器名。如果使用参数 -v,则不仅可看到远程服务器名,也可看到远程服务器的具体 URL。

    git remote 命令除查看配置的远程服务器信息外,也可执行与远程服务器配置相关的若干功能。

    1. git remote add <serverName> <serverUrl>:添加远程服务器。
    2. git remote show <serverName> :显示指定远程服务器的详细信息。
    3. git remote rm <serverName>:删除指定远程服务器。
    4. git remote rename <oldserverName> <newserverName> :指定远程服务器重命名。
  • git fetch <serverName>

    此命令用于从远程服务器 serverName 中拉取本地仓库中所没有的数据。需要注意的是:该命令只会拉取数据至本地仓库,而不会将其合并至工作区

    git pull 命令不仅会拉取数据至本地仓库,同时将其合并至工作区。

  • git push <serverName> <branch>

    此命令用于将本地仓库当前分支推送至远程服务器 serverNamebranch 分支。

其他操作

与其他版本控制系统一样,Git 可以给本地仓库历史记录中的某一个提交打上标签,以示其重要性。实际使用中,人们常用此功能标记发布的不同版本。

我们这里介绍与标签相关的若干命令:

  • git tag [-l/--list "通配模式"]

    此命令用于查看已有标签,其按照字母顺序列出。如果添加参数 -l,则其会列出与通配模式匹配的标签。

  • git tag ...

    Git 支持两种标签 —— 轻量标签和附注标签。轻量标签只是某个特定提交的引用;附注标签则是存储于 Git 数据库中的完整对象,它包含打标签者的姓名、打标签者的电子邮件、打标签日期及标签信息 (可以看到,附注标签信息与提交快照信息基本一致)。通常情况下,建议使用附注标签。

    git tag <tagName> 命令用于创建最新提交的轻量标签;git tag -a <tagName> -m <tagMessage> 命令则用于创建最新提交的附注标签。

    如果需要对以往提交打标签,则需使用 git tag <tagName> <以往提交的校验和>git tag -a <tagName> -m <tagMessage> <以往提交的校验和> 命令。

  • git show <tagName>

    此命令用于显示指定标签对应的详细信息。

  • git push <serverName> [<tagName>/--tags]

    默认情况下,git push 命令并不会将标签信息传送至远程服务器。创建完标签后,我们需要显式推送标签至远程服务器。git push <serverName> <tagName> 便可做到这件事。

    如果希望一次性推送很多标签,可使用 git push <serverName> --tags 命令,它会将远程服务器中不存在的标签全部传送过去 (两种标签都会传送过去)。

  • git tag -d <tagName>

    此命令用于删除指定标签。

    此命令仅会删除本地仓库中的标签,而不会删除远程服务器中的标签 (如果远程服务器中有此标签的话),为删除远程服务器中的标签,我们可使用 git push <serverName> :refs/tags/<tagName> (将冒号前的空值推送至远程服务器中的指定标签名) 或 git push <serverName> --delete <tagName>

  • git checkout <tagName>

    此命令用于查看特定标签所指代的文件版本。此命令会使得本地仓库处于 detachecd HEAD 状态,它会存在一定副作用。

由于某些命令比较复杂,具体使用比较繁琐,Git 提供 别名 功能以允许用户自定义简洁命令以替代复杂命令。我们在此举一个例子:git config --global alias.unstage 'reset HEAD',它将 unstage 定义为 reset HEAD -- 的别名。以往取消暂存区文件至工作区,需要使用命令 git reset HEAD <fileName>,现在可使用命令 git unstage <fileName>