git
[Git][https://en.wikipedia.org/wiki/Git] 是一个分布式的管理系统,作者 Linus Torvalds 2005 创建它,主要是维护linux内核,现在很多人用它作为大型项目的版本控制软件,来管理源代码,或是一些纯文本的笔记或者文档。本篇文字,很多的文本描述,或者图片资源来自互联网,我只是互联网的搬运工,本文仅作为个人学习的一个记录跟总结。我将在最下方列出部分链接,感兴趣的朋友可以去看看。
基本介绍
特点
Git 有如下几个特点:
1.直接记录快照,而非差异比较
2.近乎所有操作都是本地执行,所以速度很快
3.时刻保持数据的完整性,在保存到 Git 之前,所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引。换句话说,不可能在你修改了文件或目录之后,Git 一无所知。这项特性作为 Git 的设计哲学,建在整体架构的最底层。所以如果文件在传输时变得不完整,或者磁盘损坏导致文件数据缺失,Git 都能立即察觉。
Git 使用 SHA-1 算法计算数据的校验和,通过对文件的内容或目录的结构计算出一个 SHA-1 哈希值,作为指纹字符串。该字串由 40 个十六进制字符(0-9 及 a-f)组成,看起来就像是:
1 | 24b9da6552252987aa493b52f8696cd6d3b00373 |
Git 的工作完全依赖于这类指纹字串,所以你会经常看到这样的哈希值。实际上,所有保存在 Git 数据库中的东西都是用此哈希值来作索引的,而不是靠文件名。
4.多数操作仅添加数据,不用担心数据丢失
5.文件的三种状态,对于任何一个文件,在 Git 内都只有三种状态:已提交(committed),已修改(modified)和已暂存(staged)。已提交表示该文件已经被安全地保存在本地数据库中了;已修改表示修改了某个文件,但还没有提交保存;已暂存表示把已修改的文件放在下次提交时要保存的清单中。
由此我们看到 Git 管理项目时,文件流转的三个工作区域:Git 的工作目录,暂存区域,以及本地仓库。
每个项目都有一个 Git 目录(如果 git clone
出来的话,就是其中 .git
的目录;如果 git clone --bare
的话,新建的目录本身就是 Git 目录。),它是 Git 用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。
从项目中取出某个版本的所有文件和目录,用以开始后续工作的叫做工作目录。这些文件实际上都是从 Git 目录中的压缩对象数据库中提取出来的,接下来就可以在工作目录中对这些文件进行编辑。
所谓的暂存区域只不过是个简单的文件,一般都放在 Git 目录中。有时候人们会把这个文件叫做索引文件,不过标准说法还是叫暂存区域。
工作流程
基本的 Git 工作流程如下:
- 在工作目录中修改某些文件。
- 对修改后的文件进行快照,然后保存到暂存区域。
- 提交更新,将保存在暂存区域的文件快照永久转储到 Git 目录中。
所以,我们可以从文件所处的位置来判断状态:如果是 Git 目录中保存着的特定版本文件,就属于已提交状态;如果作了修改并已放入暂存区域,就属于已暂存状态;如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。
技术说明
文件内容并没有真正存储在索引(.git/index)或者提交对象中,而是以blob的形式分别存储在数据库中(.git/objects),并用SHA-1值来校验。 索引文件用识别码列出相关的blob文件以及别的数据。对于提交来说,以树(tree)的形式存储,同样用对于的哈希值识别。树对应着工作目录中的文件夹,树中包含的 树或者blob对象对应着相应的子目录和文件。每次提交都存储下它的上一级树的识别码。
如果用detached HEAD提交,那么最后一次提交会被the reflog for HEAD引用。但是过一段时间就失效,最终被回收,与git commit --amend
或者git rebase
很像。
基本命令
Git 命令是一些命令行工具的集合,它可以用来跟踪,记录文件的变动。比如你可以进行保存,比对,分析,合并等等,这个过程被称之为版本控制。
git config
git的基本配置,配置文件在用户目录下的**.gitconfig**
user 设置当前系统的git用户的名称跟邮件
alias 设置一些别名,简化命令,提高效率,当然本文档为了介绍git的命令的使用,在下文的shell中都没有使用别名。
http 跟 https 设置shadowsocks的http代理,避免有的仓库被GWF墙,拉取不了。
1 | git config --global user.name "jianchengwang" |
git init
Initialized empty Git repository, 在目录下为出现一个**.git**目录文件夹,是为你的项目存储所有历史和元信息的目录 - 包括所有的对象(commits,trees,blobs,tags),这些对象指向不同的分支。
1 | mkdir git-tutorial |
git status
可以看到当前目录的git状态,比如所处分支,提交记录,未追踪的文件等,还有一个很好的命令提示作用,提示你可以使用哪些命令进行操作。
1 | git status |
git add
未追踪的文件,我们通过这个命令告诉git将这个文件进行版本控制,添加到暂存区 staged,暂存区的记录在**.git/index**
1 | git add README.md |
git mv
移动或者重命名文件,目录,或符号链接,类比linux mv
1 | git mv a.txt dir |
如果是window,mac
系统对大小写不敏感,一般git config ignorecase true
,这时候也可以用git mv
修正文件大小写问题,
1 | git mv -f a.js A.js |
git commit
通过以下命令将暂存区的内容提交到本地仓库,会生成一个版本快照。
1 | git commit -m "add README.md" |
git remote
如果我们需要跟别人进行协作开发,就要把本地的代码推送到远程的服务器的仓库上,比如这里我在github上创建一个远程仓库,然后将本地仓库添加一个远程仓库地址,名字命名为origin 一般,这个名字是约定俗称的,不建议去修改,但是假如你有多个远程仓库,可以自定义命名。
1 | # 添加远程仓库 |
git push
我们可以将本地仓库当前分支的代码推送到远程仓库的分支上,**-u** 表示set upstream for git pull/status 即设置当前远程分支为默认上游仓库,下次直接使用git push即可
1 | 格式 |
git clone
克隆远程仓库到本地文件夹git-demo,然后进行一些简单的修改,然后推送到远程仓库,现在我们有两个本地仓库了,其中git-demo的代码已经发生改变,并且推送到远程仓库,远程仓库的代码也发生改变,但是之前的本地的git-tutorial代码还是旧的,这时候我们切换到之前的git-tutorial文件目录,拉取远程仓库的代码。
1 | git clone https://github.com/jianchengwang/git-tutorial.git git-demo |
git fetch | pull
取回远程主机某个分支的更新,再与本地的指定分支合并
1 | git pull <远程仓库> <远程分支名>:<本地分支名> |
git branch
在实现一些新功能或者修复 BUG 的时候,我们希望所有的修改环境都是独立的,Git 给我们提供了分支,它可以保证稳定版本的代码不会被破坏、不同的功能可以由不同开发者同时开发、开发者可以专注于自己的分支,不用担心被其他人破坏了环境。
HEAD -> 当前分支的最后一次更新,通常默认是指向master的最后一次提交。我们可以移动这个指针或者叫坐标,就可以变更使用的分支。
提交时使用(tilde)和^(caret)就可以指定某个提交的相对位置。最常用的就是相对于HEAD的位置。HEAD后面加上(tilde)可以指定HEAD之前的提交记录。合并分支会有多个根节点,您可以用^(caret) 来指定使用哪个为根节点。
STASH -> 还未提交的修改内容以及新添加的文件,留在索引区域或工作树的情况下切换到其他的分支时,修改内容会从原来的分支移动到目标分支。
但是如果在checkout的目标分支中相同的文件也有修改,checkout会失败的。这时要么先提交修改内容,要么用stash暂时保存修改内容后再checkout。
stash是临时保存文件修改内容的区域。stash可以暂时保存工作树和索引里还没提交的修改内容,您可以事后再取出暂存的修改,应用到原先的分支或其他的分支上。
我们在所在分支创建一个分支的时候,新建的分支会拥有当前分支的一份拷贝。
1 | 创建分支 |
git tag
为软件发布创建标签是推荐的。这个概念早已存在,在 SVN 中也有。你可以执行如下命令创建一个叫做 1.0.0 的标签
1 | git tag 1.0.0 1b2e1d63ff |
git log
通过日志,我们可以很方便的查看并且追溯各个版本快照,并且stackoverflow有人展示了很多漂亮的日志格式,我们可以借鉴下
https://stackoverflow.com/questions/1057564/pretty-git-branch-graphs
1 | git log |
git merge | rebase
在做一些新特性或者修复 bug 的时候,我们通常会建立一个新的分支,而完成后我们需要做一个合并分支的操作。
合并分支默认使用fast-forward(–ff)模式,这种模式不会产生一个新的commit提交,如果你要使用**–no-ff**,如果是与他人协作开发,建议采用这种模式,因为它会原有的开发分支整个提交链的完整性。
使用rebase 会重写项目历史,能让我们的项目提交日志保持一条竖线,但是这破坏协作的工作流,所以有一条黄金法则,绝对不要在公共分支上使用rebase
merge 是合并操作,它会将两个分支的操作合并在一起,它关注点在真实的提交历史记录上
rebase 没有合并操作,它只是将当前分支的修改复制到目标分支的最后一次提交上面,它的关注点在开发的过程上面
如果两个分支同时对一个文件进行修改,并且commit,那么合并的时候会产生conflict ,我们要解决冲突后才能进行合并操作。解决冲突一般会使用一些可视化的工具进行解决,命令行的话即使是使用vimdiff感觉也比较麻烦。
1 | merge |
git cherry-pick
git cherry-pick 可以选择某一个分支中的一个或几个commit(s)来进行操作。例如,假设我们有个稳定版本的分支,叫v2.0,另外还有个开发版本的分支v3.0,我们不能直接把两个分支合并,这样会导致稳定版本混乱,但是又想增加一个v3.0中的功能到v2.0中,这里就可以使用cherry-pick了,其实也就是对已经存在的commit 进行再次提交.
命令集合:
- git cherry-pick
:单独合并一个提交 - git cherry-pick -x
:同上,不同点:保留原提交者信息。
Git从1.7.2版本开始支持批量cherry-pick,就是一次可以cherry-pick一个区间的commit。 - git cherry-pick
.. - git cherry-pick
^..
前者表示把
后者有”^”标志的表示把
其中,
注:以上合并,需要手动push代码。
1 | 从其他分支复制指定的提交,然后导入到现在的分支 |
git reset | revert
时光不能倒流,但是 Git 可以做时光机让你的提交记录回到过去。git revert 撤销某次操作,此次操作之前和之后的 commit 和 history 都会保留,并且把这次撤销作为一次最新的提交。git revert是提交一个新的版本,将需要revert的版本的内容再反向修改回去,版本会递增,不影响之前提交的内容。相比 git reset,它不会改变现在的提交历史。因此,git revert 可以用在公共分支上,git reset 应该用在私有分支上。
在reset可以遗弃不再使用的提交。执行遗弃时,需要根据影响的范围而指定不同的模式,可以指定是否复原索引或工作树的内容。
除了默认的mixed模式,还有soft和hard模式。欲了解受各模式影响的部分,请参照下面的表格。
模式名称 | HEAD的位置 | 索引 | 工作树 |
---|---|---|---|
soft | 修改 | 不修改 | 不修改 |
mixed | 修改 | 修改 | 不修改 |
hard | 修改 | 修改 | 修改 |
主要使用的场合:
- 复原修改过的索引的状态(mixed)
- 彻底取消最近的提交(hard)
- 只取消提交(soft)
1 | 回退到之前一次提交 |
git stash
git stash 可用来暂存当前正在进行的工作, 比如想pull 最新代码, 又不想加新commit, 或者另外一种情况,为了fix 一个紧急的bug, 先stash, 使返回到自己上一个commit, 改完bug之后再stash pop, 继续原来的工作。
1 | git statsh |
.gitignore
记录为.gitignore的文件是Git的非管理对象,但是需要提交.gitignore本身。
可以到 Gitignore网站上找忽略模板。
fock | pull request
我们经常fock其他上游仓库,来pull request做贡献等,首先是保证本地仓库跟上游仓库的同步。关于pr相关操作,可以看相关链接的工作流,这里不多做介绍。
1 | # 添加上游仓库 |
SSH 免密登录
我们会发现每次进行git push的时候都要输入密码,这样比较麻烦,所以我们可以通过ssh进行免密登录。
这里只列出了linux的简单命令,其他系统或是更详细的帮助信息请自行查看github的 相关帮助文档
1 | # 创建 ssh key |
Hub
hub is an extension to command-line git that helps you do everyday GitHub tasks without ever leaving the terminal
我一般比较常用hub create
命令,你也可以设置别名 git -> hub
1 | indicate that you prefer HTTPS to SSH git clone URLs |