Git 基础命令
Git 基础命令介绍
Git 使用起步
安装完成后,在命令行中输入 git 便可以在帮助信息中看到帮助文档:
usage: git [--version] [--help] [-C <path>] [-c name=value]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
<command> [<args>]
These are common Git commands used in various situations:
start a working area (see also: git help tutorial)
clone Clone a repository into a new directory
init Create an empty Git repository or reinitialize an existing one
work on the current change (see also: git help everyday)
add Add file contents to the index
mv Move or rename a file, a directory, or a symlink
reset Reset current HEAD to the specified state
rm Remove files from the working tree and from the index
examine the history and state (see also: git help revisions)
bisect Use binary search to find the commit that introduced a bug
grep Print lines matching a pattern
log Show commit logs
show Show various types of objects
status Show the working tree status
grow, mark and tweak your common history
branch List, create, or delete branches
checkout Switch branches or restore working tree files
commit Record changes to the repository
diff Show changes between commits, commit and working tree, etc
merge Join two or more development histories together
rebase Reapply commits on top of another base tip
tag Create, list, delete or verify a tag object signed with GPG
collaborate (see also: git help workflows)
fetch Download objects and refs from another repository
pull Fetch from and integrate with another repository or a local branch
push Update remote refs along with associated objects
'git help -a' and 'git help -g' list available subcommands and some
concept guides. See 'git help <command>' or 'git help <concept>'
to read about a specific subcommand or concept.
获取帮助
输入 git help <command> git <command> -h git <command> --help man git-<command> 均可查询某个命令的帮助文档。
初次运行 Git 前的配置
创建 Git 仓库前必须使用 git config 完成用户信息配置。
git config --global user.name "<Your Name>"
git config --global user.email "<youremail@example.com>"
一些优化设置:
git config format.pretty oneline 显示历史记录时,每个提交的信息只显示一行
git config color.ui true 彩色 git 输出
git config format.pretty oneline 显示历史记录时,只显示一行注释信息
这些配置分为三个级别:
- –local 默认 具有最高优先级 只影响本仓库
.git/config - –global 中级优先级 影响到所有当前用户的仓库
~/.gitconfig - –system 最低优先级 影响到全系统的仓库
/etc/gitconfig
Git 默认的文本编辑器为 vim ,可通过 git config --global core.editor emacs 命令配置默认文本编辑器为 emacs。
配置完成后可通过 git config --list 检查配置信息。
获取 Git 仓库
初始化一个本地仓库
git init 命令会在现有目录中初始化一个仓库。该命令在当前目录创建一个名为 .git 的子目录,这个子目录含有你初始化的 Git 仓库的版本信息和本地设置文件(.git/config)。
git init [path] 命令可以指定要初始化的目录。

初始化一个本地的远程服务器
git init [path] --bare 命令可以初始化一个本地远程仓库。生成的仓库

克隆远程仓库到本地
git clone [url] 命令会获得一份已经存在了的 Git 仓库的拷贝。
忽略文件
创建一个名为 .gitignore 的文件可以忽略匹配的未跟踪文件。 .gitignore 文件格式规范如下:
- 所有空行或者以 # 开头的行都会被 Git 忽略。
- 可以使用标准的 glob 模式匹配。
- 匹配模式可以以(/)开头防止递归。
- 匹配模式可以以(/)结尾指定目录。
- 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
GitHub 为各个类型项目和操作系统提供了忽略文件模板,可以在 这里 找到。
查询状态
git status 命令可以帮助开发者在下面三对关系中找出文件状态的变化。
- 未跟踪 <–> 跟踪
- 工作目录 <–> 暂存区
- 暂存区 <–> 最新提交
如果当前分支有关联远程分支,会显示当前所在分支同远程服务器上对应分支偏离情况。

git status -s 或 git status --short 命令会以紧凑的格式输出状态信息,?? 标记表示文件未被跟踪; A 标记表示文件被新添加到暂存区;R 标记表示文件被重命名;D 标记表示文件被删除; M 标记表示文件被修改,出现在左边表示文件被修改且被放入暂存区,出现在右边表示文件被修改但未放入暂存区。

跟踪新文件 / 添加文件到暂存区
git add [file] 命令开始跟踪一个文件并将新文件暂存到暂存区。如果文件已暂存,git add 会将修订的文件重新暂存起来。

上图中,我们使用 git add 命令跟踪 README.md 文件并将文件从工作区添加到暂存区,文件状态从未跟踪变成已跟踪。修改文件后再次使用 git add 命令将暂存区中的文件替换为新版本。
git add . / git add * 命令批量增加当前目录下全部文件。

删除文件
使用 Git 管理文件后,如果想要移除某个文件,就必须要从暂存区域移除,然后提交。
git rm <file> 可以从暂存区和工作目录中删除文件,后跟你想要删除的文件(夹),可以使用 glob 模式。如果文件之前已经 add 到暂存区域并且修改过的话,则必须要用强制删除选项 git rm <file> -f。

如果希望仅在存暂存区中删除(文件保留在磁盘),使用 git rm --cached <file> 。

如果希望删除所有被跟踪但在工作区已经被删除的文件,使用 git rm $(git ls-files --deleted) 。

移动 / 重命名
git mv <file_from> <file_to> 命令可以移动和重命名文件,相当于 mv、git rm、git add 三条命令的组合。

提交更新
直接使用 git commit 会根据暂存区的内容创建一个提交,并启动文本编辑器以便输入本次提交的说明。在编辑器中填入提交信息,退出编辑器完成提交。退出时编辑器中以 # 开头的注释行会丢掉。
git commit -m <message> 可以不启动编辑器直接创建一个提交,-m 选项后就是本次提交的提交信息。

git commit -a -m <message> 会跳过使用暂存区域,直接提交工作区的已跟踪内容。Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤。
重新提交
如果你提交完发现漏掉几个文件没有添加,或者 想修改上个提交的提交信息,可以使用 git commit --amend 命令。

提交历史
在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。 完成这个任务最简单而又有效的工具是 git log 命令。这个命令会列出每个提交的 SHA-1 校验和、作者的名字和电子邮件地址、提交时间以及提交说明。

常用的选项
-p: 显示每次提交的内容差异-2: 仅显示最近两次提交--oneline: 单行显示--graph: 查看分支合并图

格式化的分支合并图
git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit

较长的命令可以用 Git 中 alias 命令设置: git config alias.<shortname> <fullcommand> 。设置完成后直接使用 git <shortname> 代替长命令使用。
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
查看差异

工作目录、暂存区、版本库之间的差异
添加文件到暂存区(git add)之前,你可以使用 git diff [-- <filename>] 命令显示 工作目录 与 暂存区 的差异,git diff 会通过文件补丁的格式显示具体哪些行发生了改变。
提交更新(git commit)之前,你使用 git diff --cached [<reference>] [-- <filename>] / git diff --staged [<reference>] [-- <filename>] 查看 暂存区 与 某次提交 的差异(默认为 HEAD)。
git diff HEAD [-- <filename>] 查看 工作区 和版本库里面 HEAD 的区别。

两个分支的差异
在合并改动之前,使用 git diff <source_branch> <target_branch> 预览两个分支的差异。
两次提交之间的差异
此外,你还能通过 git diff <reference> 显示工作目录和某次提交间的差异,使用 git diff <reference> <reference> 查询两次提交之间的差异。
图形化差异查看工具
Git 内建有图形化工具:gitk,可以更方便清晰地查看差异。另外 Github 客户端也很不错。
撤消操作
工作区文件

如果想将工作区的文件还原到上次 add 的状态,可以使用 git checkout -- <file>... , 此命令将文件从暂存区覆盖到工作目录,不可恢复。
暂存的文件
如果不小心 add 了额外的文件到暂存区,可以使用 git rm --cached <file>... 删除文件暂存状态。

如果想撤销对暂存区文件的修改,可以使用 git reset HEAD <file> 。 该命令会使用版本库 HEAD 指向的文件覆盖暂存区中的文件。

撤销全部修改

git checkout HEAD -- <file> 可以直接将内容从上次的提交复制到工作区。回退版本
版本回退
使用 git reset 可以将当前分支回退到历史中的某个版本,移动 HEAD 和 Branch,有三种回退方式:
mixed
git reset --mixed <commit> / git reset <commit>
默认的回退方式,暂存区内容被覆盖,工作区内容不变。
soft
git reset --soft <commit>
暂存区和工作区内容不变。
hard
git reset --hard <commit>
暂存区和工作区内容被覆盖。
简写
commit^表示commit上的父提交,多个^可表示以上的多个级别(commit^^^)。commit~n则表示在commit之前的第 n 次提交。
reset 之后原有的提交已经无指针指向成为无索引的提交,会在过期之后被回收。
引用变化记录
reflog 是 git 用来记录引用变化的一种机制,比如分支的变化或者是 HEAD 引用的变化等。
git reflog <reference> / git reflog show <reference> 会显示 <reference> 的引用变化,当命令不指定引用的时候默认列出 HEAD 的 reflog。
git reflog 会根据仓库的提交顺序按顺序来排列,其中包括无索引的提交。可以这里使用 HASH 值来进行一些操作,但无索引的提交可能已经丢失。
- 对 git 误操作进行数据恢复
如不小心用 git commit --amend 当成 git commit 覆盖当前的 commit,或不小心把当前的 commit 给搞没了(reset --hard)。都可以通过 git reflog 恢复。
Git 记录每次修改 HEAD 的操作,git reflog / git log -g 可以查看所有的历史操作记录,然后通过 git reset 命令进行恢复。
git reflog 有时候可以帮助你找到丢失掉的 commit,比如你在某个 detached HEAD (即不在任何分支只是在某个历史的 commit 的节点上)的时候进行了一次 commit,然后你切换到另一个分支想把刚才的东西合并进来,这个时候突然意识到刚才的那次提交找不到了,这时你就可以通过 HEAD@{1} 引用到刚才的提交了,或者通过 git reflog 找到对应 commit 的 sha1 值,然后进行 merge。
reflog 文件格式
HEAD 对应的 reflog 文件路径为 .git/logs/HEAD,文件是一个纯文本文件。分支的 reflog 文件都放在 .git/logs/refs 目录下的子目录中。
每一个 reflog 的 entry 都包含了变化前 commit 节点的 sha1 值以及变化后的 commit 节点的 sha1 值,如果你阅读了 git 的源码你会看到这两个值对应的变量名为 osha1(old sha1)和 nsha1(new sha1),第一次 commit 对应变化的 old sha1 值为全 0 的特殊值;每一个 entry 还包含了用户名,email ,变化的时间戳以及变化的具体内容。
悬空对象
在 git 中一个对象一般都会被别的对象或者引用(reference or object)引用到。如果一个对象不再被任何对象或者引用直接引用到,那么这个对象就成了一个悬空对象(dangling object)。Dangling object 在 git 库中基本没有大的作用了,如果你不去管它,它会根据过期失效的策略最终被垃圾回收机制清理掉。
如果从某个引用或者对象开始去遍历对象的有向图,无法达到某个指定对象,那么可以说这个指定对象 unreachable from this reference or object。 如果一个对象 unreachable from 任何其他对象和引用,那么它便是悬空对象了。
- 清理无用的对象
之前我们提到,reflog entry 会记录 old sha1,new sha1 的值,所以 reflog 也算作对这两个 sha1 对应的对象的引用者。所以有时候虽然某个 commit 对象无法从任何一个引用引用到,但是它却不是真正的悬空对象,原因就是还有 reflog 对它的引用。所以有时候为了清理无用的对象,需要删除一些 reflog entry。
git reflog delete ref@{specifier} 可以用来删除指定的 reflog entry,使得它从历史中消失,这个时候 reflog 并不是引用变化的真实历史了。前面讲了,每一个 reflog entry 都包含两个 sha1,老 sha1 和新 sha1,如果删除中间的某条 entry 的时候,就相当于断开了这种新老的关联,产生了 gap,如何解决这个问题呢?可以在 delete 的时候加上 –rewrite 选项,它的作用就是使得删除掉的记录后面的 entry 的 old sha1 为现在前一条 entry 的 new sha1 值。
还有一种删除 reflog entry 的方式是使用其过期机制,git reflog expire [--expire=<time>] [--expire-unreachable=<time>] <refs>,如果不指定其中的相关时间的话,则使用 git 配置的 gc.reflogExpire 和 gc.reflogExpireUnreachable 如果配置没有显式的配置则使用默认值,分别为 90 天和两周。
reset 与 checkout 区别
两种方法都有两个作用范围,一个是分支操作(commit 操作),另一个是文件操作(file 操作)。
| 命令 | 范例 | 移动 HEAD/Branch | 注释 |
|---|---|---|---|
git reset [commit] |
git reset HEAD^ --soft |
是/是 | 完全回退到某个提交(之前所在的位置将失去索) |
git reset [file] |
git reset README.md |
否/否 | 恢复暂存区到某个提交状态(不移动指针) |
git checkout [commit] |
git checkout master |
是/否 | 移动当前指针 HEAD 到某个提交(并复制内容到工作目录) |
git checkout [file] |
git checkout -- README.md git checkout HEAD -- xx.log |
否/否 | 恢复工作目录到某个状态 |
Git 远程仓库
远程仓库是指托管在因特网或其他网络中的你的项目的版本库。远程操作可以将本地仓库推送至远程仓库服务器。Git 支持许多主流的通信协议,包括 Local、HTTP、SSH、还有Git。服务器只应该是作为同步之用(被动接受既可)。
查看远程仓库
git remote 可以查看已配置的远程库信息。使用 git clone 命令克隆的仓库,默认远程仓库名为 origin。
git remote -v 会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL。
git remote show <shortname> 会显示名为 <shortname> 的远程仓库的 URL 与跟踪分支等信息。

远程仓库的关联
要关联一个远程仓库,使用 git remote add <shortname> <url> 命令;
要移除一个远程仓库,使用 git remote rm <shortname> 命令;
要更改一个远程仓库 url 地址,使用 git remote set-url <shortname> <url> 命令;
要修改一个远程仓库的简写名,使用 git remote rename 命令。

从远程仓库中抓取与拉取
git fetch <shortname> 命令会访问远程仓库,从中拉取所有本地没有的数据到本地仓库。执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。
如果当前分支设置为跟踪一个远程分支,使用 git pull 命令会自动抓取然后合并远程分支到当前分支。
默认情况下,git clone 命令会自动设置本地 master 分支跟踪克隆的远程仓库的默认分支。
可以先使用 git fetch + git merge 来解决冲突的问题。
git pull 就等同于 fetch 加 merge。
推送到远程仓库
git push 将当前的全部版本推送至远程仓库,其完成了提交历史的完全复制并同时移动复制版本的 HEAD 与 Branch。
在 push 时使用 -u 参数可以关联远程分支: git push -u <shortname> <branch> 。
使用 git branch --set-upstream <branch-name> <shortname>/<remote-branch-name> 也可以建立起本地分支和远程分支的关联。
关联的分支可以直接使用 git pull 从远程抓取更新,每次本地提交后,也可以使用 git push 命令推送最新修改。
克隆远程仓库
使用 git clone [url] 可以克隆远程仓库,并将克隆地址默认设为 origin。Git 克隆的是该 Git 仓库服务器上的几乎所有数据(没有服务器端的挂钩设置)。
Git 支持多种数据传输协议。https:// 协议, git:// 协议或者 SSH 传输协议。
标签操作
Git 可以给历史中的某一个提交(比如发布结点)打上标签,以示重要。标签是某一提交的一个静止的标示,设置了标签后就可以直接使用标签名来代替它所指代的版本提交了。
列出标签
git tag 命令会以字母顺序列出现有标签,使用 -l 可以使用特定的模式查找标签。
git show <tagname> 显示标签信息

创建标签
Git 使用两种主要类型的标签:轻量标签(lightweight)与附注标签(annotated)。
创建轻量标签只需要提供标签名字即可。一个轻量标签只是一个特定提交的引用。
git tag <tagname> 新建一个指向当前 HEAD 所在提交的标签。
git tag <tagname> [<commit>] 对指定的 commit id 打标签。
创建附注标签需要使用 -a、-s 或 -m 选项。附注标签是存储在 Git 数据库中的一个完整对象。通常建议创建附注标签。
git tag -a <tagname> -m 'message' 新建带注释标签。
删除标签
使用 -d 参数可以删除本地标签: git tag -d <tagname>。
如果想要删除远程标签,需要使用 git push <shortname> :refs/tags/<tagname>。
推送标签
默认情况下,git push 命令并不会推送标签到远程仓库服务器上。推送单个标签到共享服务器上,需要使用 git push <shortname> <tagname> 命令。如果想要一次性推送全部尚未推送到远程的本地标签,可以使用 git push origin --tags 命令。
检出标签
使用 git checkout <tagname> 切换到标签所在的提交。
在 Git 中你并不能真的检出一个标签,因为它们并不能像分支一样来回移动。
如果你想要工作目录与仓库中特定的标签版本完全一样,可以使用 git checkout -b [branchname] [tagname] 在特定的标签上创建一个新分支。
Git 分支操作
分支模型是 Git 的杀手级特性。使用 git branch 可以对仓库分支进行增删查改的操作,
一份分支的引用只是一个文本文件,里面只有一个 SHA 编码。它保存于 .get/refs/heads/master 中。
分支查看
git branch查看所有分支,带有*的为当前分支(HEAD 指向的分支)。git branch -v显示所有分支和信息git branch -vv显示所有分支和信息

分支创建
git branch <branch>创建<branch>分支git checkout -b <branch-name> <shortname>/<branch-name>在本地创建和远程分支对应的分支(本地和远程分支的名称最好一致)
分支切换
git checkout 通过移动 HEAD(指向当前的提交)检测出版本,也可用于切换分支。其会把当前的工作目录和暂存区移动到提出分支的版本。
常用命令有:
git checkout <branch>切换到<branch>分支git checkout -b <branch>创建<branch>分支,然后切换到<branch>分支:git checkout <branchname>,使指针指向目标分支git checkout -b <branchname>,创建目标分支并切换分支git checkout <reference>,可以指向任何一个版本
当 HEAD 指针与具体的分支分离时,我们将其称之为 detached head。
如果 HEAD 在分离状态则因尽量避免在此状态下进行提交,只做内容的查看。
分支的删除
-
git branch -d <branch>删除本地<branch>分支。 -
git push <shortname> --delete <branch-name>删除远程分支
分支的合并
git merge <branch>合并<branch>分支到当前分支(master)上git merge --no-ff <branch>用普通模式合并,--no-ff选项的作用是保留原分支记录git push <shortname> <branch>将<branch>分支推送到远端仓库
储藏和恢复 stash 的作用
突然需要切换到其他分支,工作区和暂存区还有在当前分支没完成的任务。那么 stash 就使用 .git 中的特殊区(Stash 区)来帮你解决这个问题(因为强切回丢失当前的工作区和暂存区的内容)。
stash 可以把当前工作区和暂存区的状态以栈(Stack)的形式保存起来(每次保存都会推一个内容到 stash 栈中),并返回一个干净的工作空间(工作区和暂存区)。
git stash 储藏当前工作
git stash list 查看储藏的工作现场
git stash apply 恢复工作现场,stash内容并不删除
git stash pop 恢复工作现场,并删除stash内容
NOTE:stash pop = stash apply + stash drop 类似于 JavaScript 中的 pop 操作。
分支合并(merge)
使用 git merge 可用于合并分支。
解决 merge 冲突
当一个文件被同时修改时(更多情况为同时修改相同的一行代码时)则极有可能产生合并冲突。
git merge next master
# Autom-merging README.md
# CONFLICT (content): Merge confilict in README.md
# Automatic merge failed; fix confilict and then commit the result
git status
# On branch master
# You have unmerged paths.
# (fix confilict and run 'git commit')
# ...
# both:modified: README.md
# no changes added to commit (use "git add" and/or "git commit -a")
在解决完合并冲突后可以使用 git add . 然后 git commit -m 'resove merge confilict' 来完成合并冲突解决并提交一个新的版本。
NOTE:git cat-file -p HEAD 可用于显示 git 中某个对象的具体信息。
NOTE+:<<<<<<<< HEAD 与 ========= 之间为 HEAD 所在的内容。
fast-forward
也并不是所有的合并操作都会造成合并从图(merge confilic)。最简单的一种合并是 fast-forward 仅仅只是变化 HEAD 指向的位置(不产生新的合并节点)。
如果需要生成新的合并节点可以使用 git merge next --no-ff 意思是合并但不使用 fast-forward。
merge 不足
当参与的人阅读分支越多其分支结构就越复杂和难以被理解。如何实现在任何状态下的线性提交?
如需完成线性提交可以使用 git rebase,其可以修剪提交历史的基线。它会将不同分支的提交在所选节点上进行重演(重演并重新创造新节点)这里 HEAD/Branch 均会发生移动。
git rebase master
但有时并不需要将其他分支上的全部提交节点统统进行重演。则可以使用 git rebase --onto 来选择需要重演的提交节点。
git rebase --onto master 5751363
rebase 与 merge 区别
rebase 会产生线性的提交历史,merge 则会产生多个不同分支的合并节点。所以具体没有好坏之分,可根据使用的需求来决定。
注意!不要在共有分支上使用 rebase(例如 master 分支)这会导致其他开发者在进行拉取(Pull)时,必须进行合并且合并中包含重复的提交。