Git基本概念

Git与传统的版本控制软件不同,它保存的不是文件之间的diff,而是文件的snapshot,文件的snapshot组成了不同的版本:

image.png

Git管理的文件有三大状态:Commited,Modified和Staged.

根据文件的三个状态,Git维护了下面三个组件Working Directory, Staging Area, 和 Git Directory:

image.png

基本上所有的命令都是在操作管理这三个组件:

这三个概念很重要,下面结束reset的时候对理解很有帮助

Git基本操作

Working Directory下的文件有两种可能的状态:tracked和untracked。tracked的文件是之前snapshot里就有的文件,untracked的文件是即不在之前版本里存在,也不在Staging Area的文件。下面是一个文件的所有可能的生命周期:

image.png

Log

Undo

Remote

git clone会默认添加一个叫origin的remote,origin没有什么特别之处,只是个名字而已,和默认的master分支一样,master也和其他分支本质上没什么区别。

Git分支

Git分支本质上就是个指向commit object的指针(实际就是个保存了commit hash的41byte文件)。那Git怎么知道你当前在哪个分支上呢?Git保存了一个特殊的叫做HEAD的指针,它指向的永远是你本地当前所在的分支。

  1. 假设当前在master分支。
  2. git branch testing会新建一个叫testing分支,但不会切过去:
    image.png
  3. 要切过去,要用checkout:git checkout testing,这个命令干了两件事儿,第一个是更新HEAD指向,二是更新Working Directory下的文件。
    image.png
  4. 如果这时你在testing上commit了,HEAD也会跟着更新:
    image.png
  5. 再切回master:git checkout master:
    image.png 5.如果再在master上commit,这时这两个分支就diverged了,下面讲merge的时候会涉及这个diverged的概念:
    image.png

git checkout -b testing 相当于 git branch testing + git checkout testing

远程分支也没啥特别的,就是个指针,只不过不能随意改动,通常只能通过push命令改变。拉取远程分支信息用fetch就行了。如果想让本地分支跟踪远程分支,可以用下面几个命令:

想查看各个分支跟踪信息可以用git branch -vv

image.png

Merge

Git有两种可能的merge方式。

如果你不想merge的时候出现一个多余的merge commit,就要需要用另外一个工具:rebase。但是rebase有点容易把自己操作迷了,不推荐新手使用,这篇入门文章就不介绍了。

Git常用工具

RefLog

image.png

reflog保存了近几个月HEAD所有的历史记录:

这个结合reset可以很方便我们恢复到某个状态,比如你一顿操作后状态乱了不知道咋恢复了,可以先查看reflog,然后git reset HEAD@{N}

Ancestry References

Git有很多方便的引用,比如:

Stashing

有时你开发的很开心,突然测试啪一个问题单过来,得,需要切分支修bug,但又不想commit,咋办咧,就可以用stash把所有的变动存起来,修完bug又可以应用stash恢复之前的状态(类似把所有的改动复制出去保存着,完事儿在复制回来):

Cleaning

RESET!!

还记得三大组件吗,这时候就有用了。先看图:

git reset做的就是改变HEAD指向的commit,这和改变HEAD本身不一样,那是checkout干的事儿;比如如果你在master上,git reset 9e5e64a会让master指向9e5e64a。下面是git reset --soft HEAD~之后的状态:

image.png

基本上就是undo了最后一次git commit。无论git reset加了什么参数,改变HEAD永远是它干的第一件事儿,如果参数是--soft的话就到此结束了,此时git status会有一个绿色的文件,它显示的就是Index和HEAD的区别。

如果git reset HEAD^不加参数,那默认就是git reset --mixed HEAD,它会把Index也更新到和HEAD一样:

image.png

翻译一下就是把文件unstage了。

你应该猜到还有一个参数把Working Directory也回滚了,是的它就是--hard:

image.png

注意reset命令只有加了--hard才是危险操作,如果v3的file.txt没有commit的话,它就永远丢失了,这里v3 commit过了我们可以用reflog来恢复。

开发日常

场景1,测试提单要切分支修bug了:

  1. 先stash:git stash
  2. 切分支:git checkout some_other_branch,然后修bug
  3. git add . & git commit -m "some awesome message"
  4. 切回来并应用stash:git checkout some_original_branch & git stash apply

场景2,MR里有审核意见,要改代码了,但不想新建新的commit:

  1. 改代码,然后 git add . & git commit --amend --no-edit
  2. push到你的fork仓:git push -f fork

这个只能改上个commit,想改多个commit,请搜索git rebase

场景3,有些需要ignore的文件但没写在.gitignore文件里(比如junit下的配置)

可以修改私有的ignore文件,路径是.git/info/exclude

场景4,有些commit信息要改,但是好几个commit之前了,而且已经push了

git diff origin/dev_6.10.1 dev_6.10.1 > ../patchfile
git checkout -t dev_6.10.1_ok
git apply ../patchfile
git commit 'some_new_message'

这样相当于把MR里的代码提出来合成一个commit。

场景5,还有就是

你要是commit了,别没事pull,你pull一下就是个merge commit,要是没事儿还好,有事儿这玩意改起来麻烦的很。

OK

Git日常使用的话这些内容足够了,欢迎更新/指正。