Git 是 linux内核创始人 Linus Torvalds 为了维护 linux 源码便利而开发的一款工具。发展到今天,已经成为全球通用的分布式版本控制系统。在 Git 中,每一份复制出来的库都可以独立使用和维护,并且,任意两个不同的库都可以合并。
GitHub 是一个开源库和私有软件项目的托管平台,因为只支持 Git 作为唯一的版本库格式进行托管,所以叫 GitHub。现在,开源代码几乎都是放在 GitHub 上。另外,还有从 GitHub fork 出去独立开发出的收费版叫 Gitlab,支持企业内项目的托管平台,相信用过的人一定明白有多便利。
简介
git当前最新版本为:2.12.2,官网地址
Git图形化软件:git是开源的命令行工具,随着git的普及,市面上也出现了很多。最著名的有:TortoiseGit、Source Tree、GitUp、SmartGit等。参考
TortoiseGit 是 TortoiseSVN 的 Git 版本,Windows平台下的Git图形化软件;
SourceTree是Windows和Mac系统下的git客户端,支持绝大多数git常用操作,界面美观,很方便管理多个项目;
GitUp 是最新出来的 Mac OS X 下的全功能开源 Git 客户端,提供快速、安全的操作,可以直接在硬盘上与 Git 仓库交互,不过,操作库的是图而不是操作命令;
SmartGit是一款古老的git图形化软件,支持的系统包括Windows、Mac OS以及Linux系统;
当然还有很多其他的Git图形化软件,基本没玩过就不介绍了。代码托管平台:随着使用git的开发者越来越多,代码托管平台基本成了大大小小项目的标配。参考
Github,很多著名的开源软件像Apache Tomcat/Maven/ant/hadoop/、Eclipse,浏览器内核webkit/v8等、当然了还有git。
Gitlab,跟GitHub类似,相比于GitHub,Gitlab可以免费创建私有项目,拥有更高的安全性和灵活性,所以它更适合企业开发团队托管项目。可以说,Gitlab是企业版的GitHub。
Bitbucket,支持免费创建私有项目,但同一个私有项目最多支持5个开发者。
OSChina、CODING、CSDN是国内的项目托管平台,这几年发展也不错。
Git 设计思想和原理
Git 版本控制系统中,其通过文件对象记录所有文件,并通过缓存所有历史和元数据,实现对代码版本的控制。
Git 中的对象类型
使用 git 命令操作过程中,你会经常遇到长度为40字符的串,像这样:
你第一反应是,这些不就是 commit id 嘛~是的,没错,但不准确。其实它真正表示的是一个项目历史信息的文件对象,这个值是通过对文件内容进行 SHA1 哈希计算得到的,就像一个 md5 值唯一标识一个文件内容一样,任意两个不同的文件
SHA1 值一定不同。
每个文件对象包括三个部分:类型,大小,内容。有四种类型对象:blob、tree、commit、tag,每一种类型的对象你都可以通过 git show xxxx 命令查看详细信息。
- “blob”:存储文件数据的类型,通常记录一个文件的内容;
- “tree”:存储一些“tree”或 “blob”类型文件的类型(类似目录,便管理子目录和文件),git ls-tree xxxxxx 可以查看详细的 tree 对象;
- “commit”:存储"tree"类型文件数据的类型,同时包含一些描述信息,还标记了项目某一个特定时间点的状态。它包括一些关于时间点的元数据,如时间戳、最近一次提交的作者、指向上次提交的指针等,也就是大家常说的 commit id;
- “tag”:指向“commit” 对象类型的类型,实际就是标记了一个特殊的 commit 对象。与其他几种类型还有一个区别,一个 tag 对象还可能包含了签名创建者签名相关的信息,你可以通过 git cat-file tag xxx 来查看。
每次提交都会给每个文件创建 blob 类型的对象,给每个目录创建 tree 类型的对象,最后创建一个 commit 对象用于指向根 tree 对象,因此,每个一个 “commit id” 就表示了一次提交的所有内容。
因此,上面举例的字符串实际上是指向 commit 类型的文件对象。从这些类型的定义或叫说明上也能看出,blob 类型是最基础的文件对象类型,而 tag 是记录某一个次重要节点的文件对象类型。
Git 如何存储对象
Git 中存在两种对象,一种是松散对象,一种是打包对象。
- 松散对象:每一个对象对应磁盘上的一个文件,每个文件存储着压缩的数据。
例如,一个 git 对象的 SHA1 值e0a0df73be6cb4e841de84c0450058ab927b60,那么,在 .git 目录的 object 子目录下,就会存在这样的一个文件。 - 打包对象:Git 会把每个文件对象的每个版本都作为一个单独的对象,如果所有的 git 对象都使用松散对象的形式存储,就会导致效率低下,所以,为了节约空间 Git 通过两个对象指针来表示一个修改的文件对象,一个指针指向第二个文件中改变的部分,一个指针指向相似的那个文件。
SHA1
SHA1(Secure hash algorithm),即安全的hash算法,用于计算文本的校验和,常作为摘要算法对签名进行校验。它的特点是计算出的SHA1值是不可逆性切不重复的,也就是根据SHA1值无法反解出原始内容,并且对于相同的内容,每次计算得到的SHA1都相同。正因为这样的特性,它常用于验证数据的完整性以及消息的验证。SHA1算法可以得到160位二进制数,也就是40位16进制数。我们做一个简单的实验。mac下通过这个命令可以安装一个sha1算法的工具:
brew install md5sha1sum
操作如下:
# A.txt:A:version 1
sha1sum A.txt
# result:
# 2d8954f9fbf90f6da74b728bf3dfe4ad459865fe A.txt
- 摘要加密算法
- 特性:不可逆,不重复
- 用途:验证数据完整性与消息验证
- 160位二进制数(或40位16进制数)
Git 基本用法
git init
以当前目录为根目录,初始化一个新的仓库,实际这条命令主要创建一个名为 .git 的隐藏目录。
git clone https://github.com/iThinkerYZ/GPUImgeDemo.git
从已有仓库中复制/拷贝一份到本地,因此我们需要知道一个项目的仓库地址,Git 支持多种协议,因此,这些 url 可以是ssh://、http(s)://,、git:// 等。一般情况,clone 下来的仓库根目录名就是 url 最后的文件名。例如上面就会得到名为
GPUImgeDemo 的目录。
git status
查看当前 Git 目录(.git)的状态,可以列举出所有已经修改文件的状态。
git branch
查看本地所有分支,*号表示所在分支,-r 参数可以指定查看远程的所有分支。
git checkout some-branch/some-tag/some-commit
切换分支,其实就是切换到某个 commit 对象(根节点)
git add some-file
创建指定修改文件内容的文件对象(SHA1),同时添加文件对象到暂存区。可以 add 一个文件,也可以通过","分隔添加多个文件,还可以通过"."来匹配本地的所有修改文件。
git commit -m 'xxxxx'
提交暂存区的修改到本地仓库,同时添加注释内容。可以使用 git commit -am 'xxxxx' 进行add和commit
git pull
拉取远程到本地,其实完整的命令是 git pull origin current-branch。不过需要注意,如果本地文件有修改,拉取的内容可能跟本地的内容有冲突需要处理一下。
git push
推送本地提交到远程仓库。完整的命令是 git push origin current-branch。注意,如果远程有更新而本地没有拉取,会阻止本地的 push 动作并提示。
git reset
重置 HEAD 部分的修改到指定状态,通常该命令结合3种参数使用,--soft/--mixed/--hard。--hard 将会删除本地所有修改但没有提交的记录。
git revert
回滚本地的代码到指定某个提交的状态,同时将回滚内容作为一个新的修改进行提交。效果跟
reset相同,但是跟 reset 不同点在于,reset 是直接删除某个提交记录,但是 revert 不会。
git merge some-branch
将某个分支内容与当前分支合并。
git rebase
将从开始 merge 的那个状态以来的所有提交,以补丁的形式一个一个重新达到目标分支上,看起来就像一条线一样的工作流。
git stash
暂存当前分支的所有修改到临时区域,保持 HEAD 为最后一次 commit 状态。
git show
显示某个git 对象的详细信息
git diff
对比工作区与暂存区内容的不同,方便对比做了哪些修改。一般,在 commit 内容前最好检查一下,确保没有问题。
git tag
展示本地所有的 tag,另外,也可以通过 git tag new-tag 来新建一个 tag,通过 git tag -d some-tag 来删除本地的 tag。通过命令 git push --tag(s) 来推送本地更新的 tag 到远程仓库。一般,我们会在发版前更新 tag 作为稳定版本。
Git 中级用法
.gitignore
开发中,经常需要忽略一些文件的修改,既不追踪,也不会被加入到暂存区中,即保证 git commit、git add、git status 命令都不会处理这些文件的变化,保证高效完成版本文件管理。.gitignore文件就是用于解决这个问题。该文件需要手动创建和添加(有的git系统会自动创建并且是初始化好的),并加入现文件来告诉 Git 系统会略哪些文件。
GitHub 官方提供了各种语言对应项目的通用.gitignore模板,大家可以直接复用。
点这里
该文件的存放位置决定该目录及其子目录使用的忽略文件,因此,可以针对不同目录定制对应的.gitignore文件,当然了,一般做法是只在根目录(与 .git 目录同级)保存一份。
rebase
由于 git 是分布式的版本控制系统,因此少不了要处理不同并行分支合并的场景。在初级用法中简单介绍了该命令。
与 merge 的主要区别:merge 最终将合并的结果作为一次新的commit提交,当前分支修改以及已经提交的部分作为历史保留,rebase 首先将当前分支的提交以补丁的形式缓存,然后更新本地分支到最新,最后将缓存的补丁依次合到当前分支,最终分支历史看起来就像没有经过合并一样。
当遇到冲突的时候,rebase 命令会被暂停,等待冲突解决,解决玩冲突后,你只需要 git add 而无需 git commit,直接执行 git rebase --continue 继续处理;另外,任何时候,开发者都可以执行 git rebase --abort 命令终止 rebase 的动作,然后恢复至 rebase 开始前的状态。
rebase 除了让 Git 系统自动完成合并外,还可以交互式的进行。git rebase -i origin/master,输入命令后如下图:
图中表示当前分支的暂存区只有一次提交,并且格式满足:
(action) (partial-sha) (short commit message)
pick对应的是 git 将采用并合并这次提交,如果用 squash 表示与上一个提交合并为一次提交,如果 为 edit,则当 git 处理到此次提交之后,返回命令行让你对提交进行修改,比如将提交拆分为多个等。最后,如果不采用任何一个 action 而是直接把某次提交删除,那么 git 就会直接从历史中移除该提交。
stash
git stash
git stash apply
git stash pop
git stash list
git stash pop stash${id}
git stash clear