主要是学习:https://www.yiibai.com/git 的笔记,较之于网上的其他教程,此教程兼顾了原理和用法,同时命令涉及的更加全面。
Workspace:工作区
Index/Stage:暂存区,也叫索引
Repository:仓库区(或本地仓库),也存储库
Remote:远程仓库
传统的代码管理工具,比如SVN,如下图所示,主要是记录变化量:
git不同,直接保存文件,也就是记录文件快照:
为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。反应到上图,虚线表示文件没有修改,对应版本就只需要记录一个指针引用即可。实线表示有修改,git直接新建一个完整的文件进行维护。
工作目录下的每一个文件都不外乎这两种状态:已跟踪或未跟踪。 已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后,它们的状态可能处于未修改,已修改或已放入暂存区。 工作目录中除已跟踪文件以外的所有其它文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有放入暂存区。 通过git add命令将untracted文件转换成tracked文件。
每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较。
在 Git 中任何已提交的东西几乎总是可以恢复的。甚至那些被删除的分支中的提交或使用 --amend
选项覆盖的提交也可以恢复。然而,任何你未提交的东西丢失后很可能再也找不到了。
git 的工作流程:
运行 git status -s
,状态报告输出如下:
$ git status -s
M README.md
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt
新添加的未跟踪文件前面有 ?? 标记
新添加到暂存区中的文件前面有 A 标记
修改过的文件前面有 M 标记。 你可能注意到了 M 有两个可以出现的位置,出现在右边的 M 表示该文件被修改了但是还没放入暂存区,出现在靠左边的 M 表示该文件被修改了并放入了暂存区。
.gitignore
文件列表,你可以在 http://github.com/github/gitignore 找到它。
git diff 默认显示的是工作区和缓存区文件修改的差异,但diff还可以显示其他:
git diff --cached 查看已经缓存起来文件和上次提交的快照HEAD之间的差异
git diff HEAD显示工作区和HEAD的区别
git diff master dev 显示master和dev分支的区别
git diff dev...master 显示从master分出dev分支后和现在master之间的差异
git diff dev 显示当前分支和dev分支的区别
git diff HEAD HEAD^ 显示上次提交和上上此提交的差别
git diff SHA1 SHA2 显示两次提交的差异
git diff --stat 仅仅显示统计信息,而不是全部信息
也可以直接使用命令 git reset HEAD file_name 达到相同的目的
-p显示每一次提交的修改信息
--graph 图形化显示
--oneline 简短显示
-2 只显示最近2次提交
其他选项见:https://www.yiibai.com/git/git_log.html#article-start
git log --graph --oneline 的结果如下:
什么含义呢?所谓的graph主要由-和*构成,总体上可以看出分支的分离和聚合,每一个*对应于一条commit信息。反过来,每一条commit信息对应的*在哪一个分支上,就是哪一个分支的提交。
git checkout 会新建分支,出现上图所示的分叉。两个不同的用户从同一个仓库 git clone也会分叉。其实git简化了问题,只要可能产生不同的文本,都是分支。对于两个人git clone的情况,可以这样理解:远程origin/master映射到了user1的origin/master分支和user2的origin/master分支。
git pull origin dev 将远程的origin仓库的dev分支拉取到当前分支
git pull origin dev:master 将远程的origin仓库的dev分支拉取到master分支
显示的的信息都非常有用:
当前处于master分支,如果是push,本地分支会被推到哪些远程分支,如果pull,从远端拉取哪些分支与本地分支进行融合。
git checkout SHA1 file_name将文件通过SHA1恢复到指定提交版本的样子,也可以用HEAD代替最近一次提交的SHA1值
表示本地的dev分支对应于远程的origin/dev分支,本地的master分支对应于远程的master分支。最后的信息表示commit信息。
如果有多个人一起对同一分支进行开发,则可能出现冲突。开始两人基于同一commit位置进行开发,甲完成后git push成功。乙后面完成,git push会报错:
使用git的要义在于不要心急,仔细阅读提示信息,按照提示去做不会有大问题。如果一时猴急,后面会有无尽的血与泪!!!
上面的报错讲的很清楚了:报错原因在于远端包括了本地没有的工作,也就是乙的本地没有包括甲已经push上去的工作。解决方法也说的很清楚了,要先整合远端的修改。具体做法就是在再次push之前先执行git pull:
如上图所示,git pull后git会自动merge,但是merge冲突失败了,我们需要手动解决冲突:
<<<<<和=====之间的是本地文本,=====和>>>>>>之间的是远程文本,根据需要做出修改即可:
之后git commit ,git push成功。
第二种情况是乙在甲push之后,在工作区做出了部分修改,在git add之前pull:
重要的事情说三遍,一定要仔细阅读git信息,尤其是操作失败的时候。error信息讲的明明白白:乙本地修改的文件可能被覆盖(丢失)。git建议我们使用stash或者commit:
先试一下git stash:
如上图所示,使用stash后,git隐藏了本地修改,数据和上一次提交版本一样,所以git pull顺利完成。之后使用stash pop把隐藏的数据pop出来:
如上图显示,有冲突,我们cat看一下test文件的变化:
正如git提示所说的那样,我们除了使用stash之外,也可选择先commit,之后再pull,具体做法在前面已经详细介绍了。虽然stash和commit两种方法都可以,但是个人觉得使用先commit的方法要更为妥当一些,因为在git中,只要是commit过的文件,就意味有永久快照,始终可以找回来。
git本质上是一个工具而已,看的再多也不如动手操练一下,冲突解决的学习可以直接git clone同一个库两份,模拟两个人对同一个文件进行修改后push。多操作几次就烂熟于胸了。
如上图所示,通过git status可以看到,对工作区和缓冲区没有任何影响,git log一下:
如上图所示,只是将分支dev从master分离后的所有commit拼接在了master分支上。
现在构造合并分支冲突并介绍解决方法:
从分支master分出dev分支,然后分别在dev和master分支中对相同的文件做出相应的修改,分别add,commit,之后切换到master分支,执行命令git merge dev:
如上图所示,发生了冲突,git status查看哪里有问题:
git信息讲的很清楚,也提供了相应的解决方案。先试一下git merge --abort:
master分支完全回到merge之前,没有任何变化。再来试一下解决冲突:
执行命令git merge dev:
如上图所示,依然报错。之前说什么来着?仔细阅读git 信息,git可没说冲突解决后再次执行git merge进行合并,而是执行git add或者git rm来标识为已解决冲突,然后commit:
在看一下日志:
合并已经成功了。
当然我们也可以直接 git rm test,删除冲突文件后提交。但是这显然在实际情况中比较少见。
git reset 回滚git add操作,工作区保持不变,缓冲区还原。就是git add的逆操作
git reset HEAD^ --soft 回滚最后一次提交,但是工作区和缓冲区保持不变
git reset HEAD^ --hard 回滚最后一次提交,工作区,缓冲区全部还原到上一次提交。通常不建议使用hard选项,这意味着会永久丢失工作区的更改(如果没有提交的话)。对于git而言,只要commit过,永远都有方法找回来。如果commit过,通过git reflog或者git log -g找到对应SHA1 就可以 git reset回来,如果直接git reset SHA1,默认是mixed,也就是恢复到缓冲区,工作区不会有变化,还需要git checkout -- file_name还原到工作区,如果git reset SHA1 --hard就可以直接还原到工作区。
其他情况需要时参看:https://www.yiibai.com/git/git_reset.html#article-start
git rebase 命令用于简化commit有一定意义,但是修改了历史记录比较危险。建议如下:
1、无论如何不能rebase已经push到远程的commit
2、最好只将较长的commit单线rebase成较短的commit,也就是不要rebase分支