Git学习
git原理
理解原理之后亲手实践对遇到的各种问题才能更快解决。并且了解原理之后再去玩那个学习git的游戏,效果才会更好。Learn Git Branching
git四个区域
远程仓库,本地仓库,暂存区(Index/stage),工作区。
注意:工作区就是我们能看到的文件夹内容,剩下的三个区都是在本地文件夹无法直接看到的
举个例子:在工作区新建一个文件,使用add放到暂存区,然后把该文件删除,然后commit、push
此时,工作区没有该文件,(后者是因为已经提交了)。但是本地和远程仓库中有该文件。我曾经误以为工作区(本地能看到的文件夹)就是本地仓库,很疑惑为什么远程仓库和本地仓库不一样但是pull的时候却说两边是一样的。
文件的四种状态
中间两种很好理解。
Untracked
就是添加到工作区但是没有add到暂存区的,这种文件没有被git跟踪记录tracked
Staged
是即将用于提交的文件状态,可以是新创建文件,也可以是修改删除了文件。换句话说,如果我进行了修改,但是该文件没有用add
或者rm
添加(或者从暂存区移除)到暂存区,下一次提交的时候仓库中不会更新他们的修改。
注意:为什么说是从暂存区移除,因为每次commit之后暂存区不会清空。如果想要把从仓库中删除文件还是得从暂存区提交,那么此时rm暂存区的文件然后commit就可以提交了。当然,在commit之前,也可把加入暂存区的修改取消,使用
git reset filename
不加名字就是取消全部加入的修改另外,Git使用SHA-1哈希值来标识和比较文件内容。每个文件和每个提交对象都有一个唯一的SHA-1哈希值。
一张好图,来自于ref1
git分支
git用指针HEAD表示当前所在的分支的某个提交,每次git branch <branchname>
创建分支后,使用git checkout <branchname>
可以切换到该分支,本质上就是HEAD指向了该分支头部。
分支主要的操作是切换(checkout, switch(safer)
)合并merge
,他们存在一些问题
切换
可以用checkout
或者switch
,后者看起来更规范更好理解。作用相同
注意:如果当前工作或暂存区有修改尚未提交,并且修改的文件和要切换的目标分支不同,git会阻止切换(警告如下),因为切换之后,当前的未提交的修改会被覆盖。(当然,如果两边文件一样是可以直接切换过去的,但是如果在另一个分支进行提交,回到本分支的时候修改就没了,不建议这么干)
error: Your local changes to the following files would be overwritten by checkout:
new.txt
Please commit your changes or stash them before you switch branches.
Aborting
如果不想提交修改,也可以使用git stash save <暂存名字>
也可以git stash
,但还是建议给个名字。
此操作将当前工作区和暂存区未提交的修改储藏起来,等到下一次回到此处git stash pop
恢复现场。
当然,也可以在别的分支pop,如果工作区和暂存区一模一样的话(比如新分出来的分支),就会成功pop出去,不一样不会成功pop,不知道一般会不会出现这种用法。
合并
使用git merge <branchname>
合并,出现冲突的时候,Git会标注出来,需要自己解决,然后手动add, commit。
标注一般长这样
<<<<<<< HEAD
这是当前分支的更改
=======
这是被合并的分支的更改
>>>>>>> branch-name
还可以用rebase
,这个名字的主要原因是,如图,分支feature从B分出来,后来要和master合并,但是master已经有了新的提交,此时,feature将修改合入master就不是基于B而是M了,所以有了rebase这个名字。注意,合并的时候依然可能出现冲突需要手动解决。解决之后,和merge不一样,这个指令需要输入git rebase --continue
看ref中的那个文章好像不太推荐用rebase
cherry-pick
其实这个不太算合并。举个例子,上面的图中,我们在master上可以用git cherry-pick C D
可以达到同样的效果。他的效果是选出n提交中, 。
删除
git branch -d [branch-name],-D(大写)强制删除
撤销
注:
git log
可以查看提交历史
git reflog
可以查看HEAD引用历史
主要有三种方式撤销,checkout, reset, revert
,常用后两种
checkout
例如git checkout 63c87bb1
或者git checkout 63c87bb1 new.txt
前者对当前工作区是安全的。他工作的步骤是:
- 首先向HEAD指向63c87bb1,
- 更新工作区和暂存区,做简单合并,如果失败,中止checkout
后者会直接覆盖两个区域,而且HEAD不改变。
也不一定要哈希值,可以使用相对引用,
例如HEAD^n,指的是HEAD第n个父提交,父提交可以又0-n个,0个说明是初始提交,多个说明是由其他多个分支合并而来
HEAD~n指得是前n个提交
reset
git resest <参数> id
,参数可以省略,id就是提交的hash
参数可以是--soft, --mixed, --hard
三种覆盖的程度不同,如下图
HEAD和分支头部会同时移动,如果用git log
查看会发现找不到被reset的commit了,不过可以用git reflog
看到。
注意:不要对有多人协作的分支使用这个,reset之后,被reset的提交相当于消失了,其他成员的HEAD如果没有指向过被reset的提交,就永远找不到那次提交了。远程的可以用revert
revert
此指令会新建一个提交用于反转之前的提交,从而达到回滚的目的。不会对之前的有影响提交,步骤如下
- 将反转指定提交的更改合并到工作区
- 将更改添加到暂存区
- 创建新的提交
可以用--no-commit
参数,在撤销之后不自动提交,我们自己做进一步更改,然后手动提交。
restore
这个并不用于分支,可以用于文件删除的恢复。
例如git restore new.txt
或者git restore --staged new.txt
前者是从暂存区恢复到工作区,后者从HEAD恢复到暂存区
git远程
fectch 和 pull
pull = fetch + merge
, pull
的时候可以指定分支git pull origin 远程分支:本地分支
如果不指定本地分支则默认将拉的远程分支合并到当前分支,如果什么都不指定,默认拉当前同名分支。
配置相关
此处配置是用于在提交记录中显示的
$ git config --global user.name "John Doe"
$ git config --global user.email [email protected]