什么是 Git

Git 是一个免费的开源分布式版本控制系统。它被设计用来高效的控制各种各样的工程项目。
Git最初在2005年由 Linus 花费了两周时间用C语言开发而成,并被用来管理 Linux kernel 的源码。
Git很容易学习,所占用空间极小而且效率很高。它比同类型的版本控制工具比如Subversion(SVN),CVS, ClearCase要更加功能强大。

什么是版本控制

简单来说就是项目的历史,项目的代码从无到有,从少到多是有个过程的。在写代码的过程中我们免不了会遇到想知道项目发展过程中的许多事情,比如

  1. 谁提交了多少次代码,提交了多少行代码,谁对这个项目的贡献最大

  2. 某行代码是何时由谁合并到项目中的,为什么他要这样写

  3. 想要回滚当前项目到过去的某个时间段

  4. 两个不同的人都修改了代码,如何能够快速的将两个人的代码diff并合并到项目中去

  5. 想要维护两套代码分别开发不同的功能

  6. 在每次提交代码时想要做一些验证,比如保证代码格式

版本控制系统就是来控制这些事情的工具。Git也可以做到这些。

Git 的特色

Git与其它的版本控制系统有什么不同呢?最大的不同(除了开源)在于Git采用了分布式的版本控制。
在Git之前,版本控制系统都是集中式的,集中式版本控制系统的特色是最基本的版本库是存放在中央服务器的,在开发功能的时候,所有开发者都需要从中央服务器获取代码的最新版本,然后在本地加入自己的功能,最后再将代码合并到中央服务器中,合并的过程中,如果有别人在你之前合入了代码而你的代码和他的有冲突,你就要先处理这些冲突,然后合并入中央服务器。

集中式版本控制系统

集中式版本控制系统是可以工作的,但在某些方面也有自己的不足,比如如果中央服务器出现了问题,所有人都必须等待中央服务器可用。由于地理因素,某些开发者可能互相之间需要同步代码,而不希望和中央服务器同步(比如由于和中央服务器之间的带宽较小)。再比如开发者想要并行开发多个功能,但不想每次都需要同服务器同步获取最新代码。
分布式版本控制系统很好的解决了以上问题。在分布式版本控制系统中,每个版本库都是独立的。换句话说,每个版本库都可以作为“中央服务器”。这样,在开发的时候,任何两个开发者之间都可以互相推送自己的修改,而且由于记录的都是修改的操作,所以在自己本地创建“新版本库”(实际上通常是分支)来开发新功能的代价很小。

分布式版本控制系统

当然,实际上,采用分布式版本控制系统的项目通常也有一个版本库充当“中央服务器”的角色,而这个版本库标记这产品的正式的代码变更,每个开发者从“中央服务器”同步最新的代码相当于获取大家都认可的项目最新的代码。

如何安装 Git

下面列举了在Linux/MacOS/Windows中安装Git的方法:

在Linux上安装Git

1
$ sudo apt-get install git

在Mac OS X上安装Git

安装homebrew,然后通过homebrew安装Git,具体方法请参考 homebrew 的文档。

1
$ brew install

在Windows上安装Git

从Git官网直接 下载安装程序 ,然后双击安装即可。

Git 常用命令

首先我们来了解下Git中的各个区域的含义

区域 解释
stash 存档库 当你去修改别的东西的时候,隐藏(临时保存)当前的修改
workspace 工作区 本地检出
index 暂存区(索引) 索引(暂存区)保存了一份工作(树)的快照,作为下次提交的内容
local repository 本地版本库 .git 文件夹保存版本库需要的全部信息(Git 版本库的骨架),一般包括分支 master, feature-x, bugfix-y
upstream repository 上游版本库 在网络(局域或因特网)上共享给其他开发者的版本库,一般叫”origin”. 一般包括分支master, shared-feature-x, release-y

一个Git命令由 git开始,然后加上命令名,例如 config,然后加上参数,例如--global user.name "your name",中间由空格连接。一个Git命令通常在一个区域或者两个区域之间进行了某些操作。

Git提供了很多的命令,可以自由的实现版本控制的几乎所有的要求,然而在日常工作中,最常用的几乎只有那么几个,你只需要打印出来贴在办公桌上即可,这里提供了一个 Git 命令小抄

如果你不想手打Git命令,也有很好的界面化工具 Sourcetree 支持。

如果想要更全面的了解Git,可以查看 《Pro Git》 ,这本书由 Scott Chacon & Ben Straub 编写,由Apress发布,遵循 Creative Commons Attribution Non Commercial Share Alike 3.0 license

下面列出一些常用的Git命令:

git stash save [<msg>]

所属区域: 从 工作区存档区
保存当前修改到新的存档库,并且执行git reset ‑‑hard来回滚。 msg是可选的用来描述存档。想快速建立存档,可以省略掉savemsg

git stash apply [<stash>]

所属区域: 从 存档区工作区
从某个存档中将改变应用到工作区,默认是最近的存档。

git stash pop

所属区域: 从 存档区工作区
应用最后一个(或指定的)存档中的改动,然后从存档库丢弃它。

git stash list

所属区域: 存档区
显示当前你有的所有存档。

git stash show [<stash>]

所属区域: 存档区
显示存档中记录的改动,对比存档生成时的原来状态;不指定stash则显示最后一个。

git stash drop [<stash>]

所属区域: 存档区
从存储区中删除单个存档;不指定stash则删除最后一个。

git stash clear

所属区域: 存档区
清空存档库。注意相关存档会被清理,此操作不能被恢复

git stash branch <branshname> [<stash>]

所属区域: 从 存档区本地版本库
新建并检出一个新分支branchname, 分支开始于存档建立时的源提交,应用存档的变化作为新的工作区和暂存区。如果成功并且stash是以 stash@{revision}方式给出的,则从存档库删除它。未给出则使用最后一个存档。这在当前分支运行 stash save 导致冲突时很好用,因为存档应用于它生成时的提交一定不会有冲突发生。

git status

所属区域: 在 工作区暂存区 之间
显示状态变化,包括

  1. 暂存区与当前的 HEAD 提交之间(即将提交的)的状态变化
  2. 工作区与暂存区(下次不会提交)的状态变化
  3. 未曾被git追踪(没有历史记录)的状态变化

git diff

所属区域: 在 工作区暂存区 之间
显示未添加到暂存区的不同。

git diff

所属区域: 在 工作区本地版本库 之间
查看工作区与某一提交之间的不同。你也可以使用 HEAD 来对比上一提交,或是用分支名来和分支比较。

git add <file.. or dir…>

所属区域: 从 工作区暂存区
添加当前的新内容或是修改的文件到暂存区,作为下次提交的(部分)内容。用 add --interactive 来交互式操作。

git add -u

所属区域: 从 工作区暂存区
添加当前修改(不包括新文件)到暂存区, 这与 git commit -a 准备提交内容的方式一致。

git rm <file(s)…>

所属区域: 从 工作区暂存区
从工作区和暂存区删除某个文件。

git mv <file(s)…>

所属区域: 从 工作区暂存区
从工作区和暂存区移动文件。

git commit -a [-m ‘msg’]

所属区域: 从 工作区本地版本库
提交上次提交之后的所有修改,

  1. 未追踪的除外(即:所有暂存区有记录的文件);
  2. 从暂存区删除已在工作区删除的文件

git checkout <file(s)… or dir…>

所属区域: 从 暂存区工作区
更新工作区文件或文件夹,不会切换分支。

git reset - -hard

所属区域: 从 本地版本库工作区
恢复工作区和暂存区到上次提交的状态,
警告: 所有工作区修改都会被丢弃。使用这条命令来解决合并错误,如果你想从头开始的话传入 ORIG_HEAD 来撤销该次提交以来的所有改动。

git reset - -soft origin/master

所属区域: 从 上游版本库工作区
恢复工作区和暂存区到上次提交的状态,当前所有工作区的修改都会保留成未提交的文件。

git checkout -b

所属区域: 从 本地版本库工作区
切换分支,更改工作区和暂存区为branch分支的内容,之后HEAD指向branch分支。

git merge

所属区域: 从 本地版本库工作区
从branch name分支合并到当前分支,使用‑‑no-commit可以保持在(已经合并)但未提交状态。

git rebase <upstream>

所属区域: 从 本地版本库工作区
变基:回滚从【当前提交和upstream分支分开处】开始直到当前提交的所有提交,将这些提交一一应用到upstream分支,结果作为upstream的新提交。

git cherry-pick <commit>

所属区域: 从 本地版本库工作区
把某个提交移动到当前分支来。

git revert <commit>

所属区域: 从 本地版本库工作区
回滚commit指定的提交,这需要当前工作区是干净的,即相比于 HEAD 提交没有修改。

git clone <repository>

所属区域: 从 上游版本库工作区
下载repository指定的版本库,并在工作区迁出master分支的HEAD版本。

git pull <remote> <refspec>

所属区域: 从 上游版本库工作区
从远程版本库取得修改到当前分支。 一般来说, git pull 相当于 git fetch 然后做 git merge FETCH_HEAD。

git reset - -hard <remote>|<branch>

所属区域: 从 上游版本库工作区
重置本地版本库,让它与远程版本一致;用 reset ‑‑hard origin/master 来丢弃所有的本地改动;用这个来处理失败的合并,直接从远程开始。

git clean

所属区域: 从 工作区工作区
从当前文件夹开始递归清理不受版本管理的内容。

git reset HEAD <file(s)…>

所属区域: 从 暂存区暂存区
从下次提交中移除指定文件。重置暂存区记录但是不处理工作区(即: 文件改动被保留但不会被提交),同时报告没有被更新的文件。

git reset - -soft HEAD^

所属区域: 从 本地版本库暂存区
恢复上一次提交,保留暂存区的改动。

git diff - -cached [<commit>]

所属区域: 在 暂存区本地版本库 之间
查看已经暂存的内容和上次提交的区别,也可指定某一提交。

git commit [-m ‘msg’]

所属区域: 从 暂存区本地版本库
暂存区中的当前内容连同提交信息储存为新提交。

git commit - -amend

所属区域: 从 暂存区本地版本库
用当前暂存区的内容修改最近一次的提交,也可以拿来修改提交信息。

git log

所属区域: 本地版本库
显示最近的提交,新的在上边。

参数 含义
‑‑decorate 显示分支和tag名字到对应的提交
‑‑stat 显示状态 (文件修改, 添加, 删除)
‑‑author=author 只显示某个作者
‑‑after=”MMM DD YYYY” 如(“Jun 20 2008”) 只显示某个日期之后的提交
‑‑before=”MMM DD YYYY” 只显示某个日期之前的提交
‑‑merge 只与当前合并冲突有关的提交

git diff <commit> <commit>

所属区域: 本地版本库
显示两个提交之间的不同。

git branch

所属区域: 本地版本库
显示所有(本地)存在的分支。参数 -r 显示远程追踪分支,参数 -a 显示全部。

git branch -d <branch>

所属区域: 本地版本库
删除某个分支,使用—D来强制删除。

git branch - -track <new> <remote/branch>

所属区域: 从 上游版本库本地版本库
添加一个本地分支来跟踪某个远程分支。

git fetch <remote> <refspec>

所属区域: 从 上游版本库本地版本库
从远端版本库下载对象和引用(即版本信息)。

git push

所属区域: 从 本地版本库上游版本库
从本地提交推送分支改变到远程,分支为所有推送过的分支。

git push <remote> <branch>

所属区域: 从 本地版本库上游版本库
向远端版本库推送新的(已存在的)分支。

git push <remote> <branch>:<branch>

所属区域: 从 本地版本库上游版本库
向远端版本库推送分支,但是从不同的(本地)分支名。

git branch -r

所属区域: 上游版本库
显示远程端分支。

git push <remote>:<branch>

所属区域: 上游版本库
删除一个远程分支,通过向远程分支推送空内容。

git config [<options>]

Git 配置相关信息

参数 含义
- -global 使用全局配置文件
- -system 使用系统配置文件
- -local 使用版本库配置文件
-f, - -file <file> 使用给定路径的配置文件
- -get 获取Git配置的值
-l, - -list 列出所有的配置

可以通过 git config alias.<command alias> <command> 设置别名

其它注意事项

平常我会使用 SourceTree 来省却自己手打git命令的烦恼,但有时在提交到 Github 的时候,会在 Github 的 commit history 里看到如下一句

1
Mangon authored and Xiang committed 16 minutes ago

其实在SourceTree 的提交历史中也能看到类似如下信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fix: fix desc error
提交:
1bcf9d0d3c9df73e52b38a71402d4b3943698287 [1bcf9d0]
父级:
371e1e0379
作者:
Mangon <*************@***.com>
日期:
2019年2月14日 GMT+8 下午1:57:33
提交者:
Xiang <**********@*****.com>
提交日期:
2019年2月14日 GMT+8 下午2:06:47
标签:
HEAD -> develop origin/develop

显然是作者与提交者不一致导致的。经查阅,Git中一个commit有作者和提交者的差别。作者(author)指的是实际作出修改的人,提交者(committer)指的是最后将此工作成果提交到仓库的人。所以,当你为某个项目发布补丁,然后某个核心成员将你的补丁并入项目时,你就是作者,而那个核心成员就是提交者。

在SourceTree中,可以在提交时 commit message 输入框左上方改变自己的 Author 属性,在当前项目窗口的右上角设置里选择高级,然后修改用户信息“全名”及“电子邮件地址”来改变 committer 属性。

将两者改为一致的自己想要用的Github账户名就可以保证在Github提交历史中显示正确的名字。