感谢 廖雪峰的Git教程

有关git的发展历史

基本操作

git status

查看当前git仓的状态
一个可脱离黑箱操作的操作

git log

显示提交日志

--oneline 将commit记录简化成一列显示(只有前七位SHA和commit message)
--stat 显示更改了多少文件,以及添加/删除的行数
-p-path 显示默认补丁信息,但是如果使用了 –stat,将不显示补丁信息,因此传入 -p 以再次添加该信息
-w 忽略空格变化
--decorate 在log中启用查看tag的功能
--graph 在logo中查看当前分支结构
--all 显示所有分支的分支结构

git show

显示最近一次提交,或者显示特定SHA的提交详情

1
$ git show 8d3ea36

输出的内容和git log -p命令完全一样,也可以
--stat 显示更改了多少文件,以及添加/删除的行数
-p-path 显示默认补丁信息,但是如果使用了 –stat,将不显示补丁信息,因此传入 -p 以再次添加该信息
-w 忽略空格变化

git add

将工作区的文档移到暂存区(跟踪)

1
$ git add <file1> <file2>...<fileN>

git rm --cached <file> 将文档从暂存区中删除
git add . 将当前目录所有文件移到暂存区

git commit

将暂存区的文档提交,封档

此命令:

1
$ git commit

将打开配置中指定的代码编辑器

在代码编辑器中:

  • 必须提供提交说明
  • 以 # 开头的行是注释,将不会被记录
  • 添加提交说明后保存文件
  • 关闭编辑器以进行提交

也可以使用-m选项绕过编辑器

1
$ git commit -m '<message>'

良好的提交说明

建议

  • 消息篇幅简短(少于 60 个字符)
  • 解释提交的作用(不是如何更改或为何更改!)

禁忌

  • 请勿解释为何做出了这些更改(下文会深入讲解这一点)
  • 请勿解释如何进行了更改(这是 git log -p 的目的!)
  • 请勿使用单词”and”
  • 如果你必须使用 “and”,则你的提交说明可能进行了太多的更改,将这些更改拆分为独立的 commit
    • 例如 “make the background color pink and increase the size of the sidebar”

Udacity的Git提交信息样式指南

git diff

git diff 命令可以用来查看已被加入但是尚未提交的更改。

此命令会显示:

  • 已经修改的文件
  • 添加/删除的行所在的位置
  • 执行的实际更改
1
2
3
4
5
6
# 查看已经修改的文件
$ git diff

# 查看不同标签、分支之间的差别
$ git diff <branch-name1> <branch-name2>
$ git diff <tag-name1> <tag-name2>

使用.gitignore文件忽略文件

可以在根目录中添加一个.gitignore文件,里面写上能匹配到要忽略文件的名字

1
2
3
4
5
6
7
# .gitignore文件中

# 忽略所有pdf文件
*.pdf

# 以ignore为开头的文件
ignore*

一些通配符的使用方法

  • 空白行作为空格
  • # - 将行标记为注释
  • * - 与 0 个或多个字符匹配
  • ? - 与 1 个字符匹配
  • [abc] - 与 a、b 或 c 匹配
  • ** - 与嵌套目录匹配 - a/**/z 与以下项匹配
    • a/z
    • a/b/z
    • a/b/c/z

标签

可以给某一个提交增加一个标签,用来区分其他不同的提交

git tag 命令用来标记特定的commit。当添加新的commit是,标签不会移动

-a 使用a选项,创建一个带注释的标签
-d 使用d选项,删除一个标签

此命令将:

  • 向最近的commit添加标签
  • 如果提供了SHA,则向具体的commit添加标签
1
2
3
4
5
# 向最近的提交添加标签
$ git tag <tag-name>

#向特定的提交添加标签
$ git tag -a <tag-name> <commit-SHA>

分支操作

git 中的分支非常灵活,使你能够实现一些很强大的功能。

git branch 命令用来管理git中的分支:

  • 列出本地分支
  • 创建新的分支
  • 删除分支
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 列出所有分支 带*的为当前分支
$ git branch

# 创建新的分支
$ git branch <branch-name>

# 在特定的commit上创建分支
$ git branch <branch-name> <commit-SHA>

# 删除分支 不能删除当前分支,也不能删除有commit的分支
$ git branch -d <branch-name>

# 强制删除分支 忽略commit
$ git branch -D <branch-name>

切换分支

git checkout 在不同的分支中切换
工作方式:

  • 从工作目录中删除 git 跟踪的所有文件和目录
    • (git 跟踪的文件存储在仓库中,因此什么也不会丢失)
  • 转到仓库,并提取分支指向的 commit 所对应的所有文件和目录
1
2
3
4
5
6
7
8
# 切换到其他分支
$ git checkout <branch-name>

# 创建分支并切换到这个分支
$ git checkout -b <branch-name>

# 创建和某个分支起点一样的分支并切换过去
$ git checkout -b <branch-name> <other-branch-name>

当切换到另一个分支时,因为每一个分支的commit可能会不同,所以文件也会有不同,那些内容并不是被清除了,而是存储在另一个分支里,对你当年前的这个分支不可见而已。

合并分支

将分支组合到一起称为合并
git中的两种合并:普通合并和快速合并
分支被合并了之后,并不是说它就消失了,如果必要,你还是可以回到那个分支,继续延伸(即使他被合并了)。

值得注意的是:合并分支会提交commit

发生合并时,git将:

  • 查看将合并的分支
  • 查看分支的历史记录并寻找两个分支的 commit 历史记录中都有的单个 commit
  • 将单个分支上更改的代码行合并到一起
  • 提交一个 commit 来记录合并操作
1
2
# 将其他分支合并到当前分支
git merge <other-side-branch>

快进合并

  • 要合并的分支必须位于检出分支前面。检出分支的指针将向前移动,指向另一分支所指向的同一 commit,这就是传说中的快进合并(Fast-forward merge)

普通合并

  • 当两个分支完全不一样的时候,进行合并时,就成为普通合并
  • 这样的两个分支合并会产生一个commit

合并冲突
大部分情况下,git 将能够成功地合并分支。但是,有时候 git 无法完全自动地进行合并。合并失败时,就称为合并冲突。

如果出现合并冲突,git 将尝试尽可能合并多的内容,然后将留下特殊选项(例如 >>><<<),告诉你(没错,告诉作为程序员的你!)需要从何处手动修复。

如下图所示:都修改了h1的标签内容

合并冲突指示符解释
编辑器具有以下合并冲突指示符:

  • <<<<<<< HEAD 此行下方的所有内容(直到下个指示符)显示了当前分支上的行
  • ||||||| merged common ancestors 此行下方的所有内容(直到下个指示符)显示了原始行的内容
  • ======= 表示原始行内容的结束位置,之后的所有行(直到下个指示符)是被合并的当前分支上的行的内容
  • >>>>>>> heading-update 是要被合并的分支(此例中是 heading-update 分支)上的行结束指示符

解决合并冲突
git 使用合并冲突指示符来告诉你两个不同分支上的哪些行导致了合并冲突,以及原始行是什么。要解决合并冲突,你需要:

  1. 选择保留哪些行
  2. 删掉所有带指示符的行

commit 合并冲突
删掉所有包含合并冲突指示符的行并选择保留哪个标题后,直接保存文件,并将其添加到暂存区,然后 commit!就像普通合并一样,代码编辑器会弹出,并让你提供 commit 消息。和之前一样,我们经常会使用自动生成的合并 commit 消息,因此在编辑器打开后,直接关闭编辑器并使用自动生成的 commit 消息。

值得一提的是,如果你解决冲突的时候,忘记删除冲突指示符,git也是会让你commit文件的,因为它们只是普通的字符而已

合并冲突小结
当相同的行在要合并的不同分支上做出了更改时,就会出现合并冲突。git 将在合并途中暂停,并告诉你存在冲突,以及哪些文件存在冲突。要解决文件中的冲突:

  • 找到并删掉存在合并冲突指示符的所有行
  • 决定保留哪些行
  • 保存文件
  • 暂存文件
  • 提交 commit
  • 注意一个文件可能在多个部分存在合并冲突,因此检查整个文件中的合并冲突指示符,搜索 <<< 能够帮助你找到所有这些指示符。

回溯操作

更改最后一个commit

利用git commit --amend命令可以对最后的一次commit进行修改
这个方法不是创建多一个commit的,而是对你最近的一次commit进行更新

确保一个commit实现一个功能,如果是新的功能,就没必要更新了,直接commit多一个就好了

1
2
# 更新最近的一次commit
$ git commit --amend

还原commit

利用git revert <commit-SHA>命令还原之前创建的commit

此命令:

  • 将撤消目标 commit 所做出的更改
  • 创建一个新的 commit 来记录这一更改

重置commit

git reset命令用来清除commit

它可以用来:

  • 将 HEAD 和当前分支指针移到引用的 commit
  • 使用 –hard 选项清除 commit
  • 使用 –soft 选项将 commit 的更改移至暂存区
  • 使用 –mixed 将commit 的更改移至工作目录中(默认)
    我们通常会用到祖先引用来指代之前的 commit。祖先引用包含:

  • ^ – 表示父 commit

  • ^<number> - 表示第<number>个分支的父节点
  • ~ – 表示前一个父 commit
  • ~<number> - 表示前<number>个节点

💡 回到正常状况💡
如果你在重置任何内容前创建了 backup 分支,那么你可以轻松地让 master 分支指向 backup 分支所指向的同一 commit。你只需:

  1. 从工作目录中删除未 commit 的更改
  2. 将 backup 合并到 master(这将导致快进合并并使 master 向上移到和 backup 一样的点)
1
2
$ git checkout -- index.html
$ git merge backup