《Git原理详解及实用指南》笔记

1,936 阅读7分钟

Git 原理详解及实用指南

Git是一个分布式的版本控制系统。

HEAD 、branch、master

  • HEAD指向当前commit的引用。当前commit在哪里,HEAD就在哪里,这是一个永远自动指向当前commit的引用,所以永远可以使用HEAD来操作当前commit。HEAD是Git中一个特殊的引用,他是唯一的。
  • branch也是Git中的一个引用,HAED除了指向commit也可以指向branch,通过branch间接指向commit。当HEAD向前走的时候也会拖动branch一起移动。通俗的理解branch可以理解为从初始commit到当前branch指向的commit的一个串,此外所有的branch之间都是平等的,不会因为是master而区分与其他分支。branch包含了从初始commit到当前commit的所有路径,而不是一条路径,并且这些路径之间也是平等的。
  • master是默认的branch。新创建的仓库是没有commit的,但是当他创建第一个commit的时候,会把master指向它,并把HEAD指向master。当clone仓库的时候,会将master分支checkout,并且将HEAD指向该分支。

branch的操作

  • 创建 -如果想创建一个新的branch只需要git branch xxx即可。
  • 切换 -使用上述命令创建的branch不会自动切换,需要使用git checkout xxx来让HEAD指向branch,从而切换到该branch上。也可以使用git checkout -b xxx来同时创建和切换branch。
  • 删除 -使用git branch -d xxx来删除一个branch。删除branch的时候,HEAD指向的branch不能直接删除;由于Git中的branch是一个引用,所以删除branch操作只会删除这个引用,而不会删除任何的commit。

push的本质

  • push所做的事是把当前branch的位置上传到远端仓库,并把它的路径上的commits一并上传。
  • push的时候如果是本地创建的一个分支需要指定远程仓库名和分支名。
  • push的时候上传当前分支,并不会上传HEAD,远程仓库的HEAD永远指向master。

pull的本质

  • pull的内部操作是把远程仓库fetch到本地,然后再用一次merge将远端仓库的commit合并到本地。

merge

  • merge做的事是:从目标commit和当前commit(HEAD所指向的commit)分叉位置起,把目标commit路径上的所有commit的内容应用到当前的commit,并重新生成一个新的commit。
  • merge的作用主要体现在用来合并branch和pull的内部操作。
  • 当两个分支更改了相同的东西,Git便不知道以谁为标准来合并,所以就会造成冲突。一般来说,出现冲突之后需要做的就两件事,解决冲突和再次提交。当然出现冲突的时候你也可以选择放弃解决冲突,撤销merge,只需要输入git merge --abortGit仓库就会回到merge之前的状态。
  • 当HEAD领先于目标commit的时候,Git什么也不做,空操作。
  • 当HEAD落后于目标commit的时候,Git会将HEAD直接指向目标commit,这种情况也称为:fast-forward(快速前移)。

add

  • add的作用是把改动过的内容放到暂存区。
  • 使用git add .来将所有的改动都放到暂存区内。
  • add添加的是文件改动,而不是文件。当我们改动一个文件并且add之后,由于需求需要再次改动该文件,此时通过git status查看git的状态的时候会发现,该文件的新的改动仍然需要添加到暂存区。

查看改动历史

  • 使用git log可以查看历史记录。
  • 使用git log -p可以查看详细的历史记录,可以看到每一个commit的行级改动。
  • 使用git log --stat可以查看每个commit的简要统计。
  • 使用git show可以查看当前commit。再show后面加上commit的SHA-1或者该commit的引用(HEAD或者branch)可以查看该次commit的详细信息git show sha-1。如果想查看某次commit的某个文件的信息只需要在上述命令基础上加上文件名称即可。
  • 查看暂存区和上一条commit的区别:git diff --stagedgit diff --cached
  • 查看工作目录和暂存区的区别:git diff
  • 查看工作目录和上一条commit的区别:git diff HEAD

rebase

  • rebase的意思是给commit重新设置基础点。就是把指定的commit以及它所在的commit串,以指定的目标commit为基础,依次重新提交一次。
  • rebase是站在被需要rebase的commit上进行操作的。
  • 当我们需要更改倒数第二条commit的时候,可以通过交互式rebase来实现,通过--i参数(git rebase -i HEAD^)可以开启交互式rebase。进入到交互式rebase之后,将需要修改的commit由pick改为edit指定操作类型。此时rebase会等待修改,当我们修改完成之后使用--amend重新提交,最后使用使用git rebase --continue使rebase继续执行。
  • 同样在进入到rebase交互模式的时候如果此时我们想删除某次提交直接将某次commit的记录从交互模式中删除即可。
  • 当然撤销提交除了使用rebase的交互模式之外,可以使用reabse -onto来撤销提交。--onto后面带有三个参数,目标commit、起点commit、终点commit。如果rebase的commits的时候我们可以通过制定起点和终点来排除一些commit从而达到丢弃提交的效果。

amend

  • 在提交的时候加上--amend参数,Git不会在当前commit上增加新的commit,而是将当前commit和暂存区的文件合并之后创建一个新的commit,并且将当前commit替换掉。所以git commit --amend所做的事情就是对当前commit进行修正。

reset

  • reset的本质是移动HEAD以及它指向的branch的位置。
  • 可以通过git reset --head HEAD^来撤销最新的commit。这是由于它把HEAD和它指向branch一起移动到当前commit的上一级commit上了,从而起到的撤销作用。
  • reset除了可以撤销commit之外,也可以移动HEAD指向其他branch。
  • --head参数的作用是重置工作目录的内容,当reset添加了--head参数的时候,会将未提交的修改全部丢弃。
  • 如果想保留工作区域的内容,则需添加--soft参数。该参数会在重置HEAD和branch的时候,保留工作区和暂存区的内容,并把重置HEAD所带来的新的差异放入到暂存区。
  • 如果reset后面不加参数,那么默认(--mixed)的情况是:保留工作目录并清空暂存区。

checkout的本质

  • checkout的本质功能是检出指定的commit。git checkout branch的本质是将HEAD指向branch,并检出branch所对应的commit的工作目录。
  • checkout不止可以切换 branch,也可以直接指定 commit 作为参数,来把HEAD移动到指定的commit
  • **注意:**使用reset的时候HEAD会和它指向的branch一起移动,而checkout不会。
  • 当误删掉一个branch的时候,可以通过git reflog来查看引用日志,默认也就是HEAD的移动记录,我们可以找到branch删除前的那次commit,通过checkout来检出该次commit,然后使用git checkout -b branchname来得到误删调的分支。

stash

  • 当需要把手头工作目录暂时清理干净的时候,可以使用git stash将所有改动都暂时存起来,切换到其他分支做事情。
  • 事情做完之后,需要重新回到该分支继续工作的时候,使用git stash pop将之前存储的内容还原回来。
  • **注意:**没有被add的文件不会被stash起来,因为 Git 会忽略它们。如果想把这些文件也一起 stash,可以加上 -u 参数,它是 --include-untracked 的简写,git stash -u