01/12
2016

##Git学习笔记

  1. git push -u origin master:The -u tells Git to remember the parameters, so that next time we can simply run git push and Git will know what to do.
  2. 查看git命令的帮助:

     // 下面两个等价
     git help config  
     man git-config
    
     // 查看简略的参数
     git config -h
    
  3. git中的4个对象:blob object、tree(解决文件接口)、commit object、tag object
  4. git commit对象可以有多个parent,git分支的结构更像是一个单向图。
  5. git分支的本质:一个指向一系列提交之首的指针或者引用ref
  6. git引用:HEAD引用、tag引用、remotes引用
  7. git常用的目录

     .git
     |--HEAD
     |--config
     |--description
     |--logs/
             |--HEAD
             |--refs/
                     |--heads/
                             |--master
                             |--other local branch
                     |--remotes/
                             |--origin/
                                     |--master
                                     |--other remote/origin branch
                             |--other git reposity
     |--hooks/
     |--info/
     |--objects/
             |--pack/
             |--..(sha-1[0-1])/....(sha-1[2-40])
     |--refs/
             |--heads/
                     |--master
                     |--other local branch
             |--remotes
                     |--origin/
                             |--master
                             |--other remote/origin branch
                     |--test/
                     |--other git reposity
             |--tags/
    
  8. git gc:git文件的保存一开始是loose模式,即每个文件单独存放,但在git gc或者git push之前会将所有commit的文件打包,放到.git/objects/pack下,里面的文件会以差异记录的方式保存,更有意思的是旧文件以新文件为基础做差异记录。
  9. git push origin master中各字段的含义:origin是remotes仓库在本地的别名,一般默认使用origin,当然也可以通过git remote命令创建新的remote仓库,如上面的目录图中的/refs/remotes/test就是另外定义的;master是master:master的缩写,第一个master是本地分支(refs/heads/master),第二个master是远程分支(refs/remotes/origin/master)
  10. git有两种传输协议:哑协议(只能拉去数据,无法提交,http实现)和智能协议(拉去数据和提交数据,可以通过ssh、http(s)实现,之中有send-packreceive-pack两个进程进行更新与同步)。更有趣的时,可以通过ssh(send-pack/receive-pack)、http(s)(get/post)来获取或提交关于git中你想要的一切信息,只要自己去解析引用与数据之间的关系。
  11. 我们可以通过修改git引用或者git commit 历史记录引用的任何信息来修改或者恢复git库,常用的命令有git log --pretty=onelinegit log -g有效等同于git reflog(会记录HEAD每次改变的记录)、甚至于git fsck -full(会查找所有object中没被其他对象引用对象(blob、tree、commit、tag),找到这些引用可以用来恢复一些硬操作)
  12. 关于git reset --soft/--hard HEADlinsk--soft不会改变当前工作区的文件,--hard会丢弃当前工作区的文件。HEADHEAD^HEAD~2HEAD~3HEAD@{n}(git reflog 或者git log -g查看),请看下一张图: git reset
  13. In its default mode, git pull is shorthand for git fetch followed by git merge FETCH_HEAD.(git pull就是git fetch + git merge fetch_head)
  14. 在连接gerrit的时候会有git push origin HEAD:refs/for/brancha,这里的refs/for/brancha在本地是没有的,git也会push到server端,介入gerrit的时候,会先由gerrit处理refs/for/brancha,这样会创建一个CR的模块。
  15. git merge:合并指定的分支到当前分支,如果指定的分支和当前分支历史记录不一致,git会自动找到他们最近的共同祖先,然后比较合并指定分支和当前分支,会在当前分支生成最新的merge commit记录,如果中间有同一个文件同一个地方的合并冲突,git会停下commit,将合并过程中产生的修改置于工作区,之后由使用者处理冲突后,并执行git commit。

    git merge,如果两个分支有差异,git merge会把两个分支的commits历史记录按照commit的时间顺序重新排序,在最后生成一个merge commit的历史记录。

  16. git rebase: 将当前分支的所有commits产生的修改存放到一个临时的区域,然后当前的分支被重置到指定的分支(git reset –hard),最后将存放到临时区域的commits记录,一个一个的重新应用到当前分支,产生新的commit记录。如果当前分支分支有一些failure的merge commit,可能会导致rebase自动完成的失败,这时候使用者就要出来这些merge failure,然后执行 git rebase –continue;还有git rebase –onto ,可以用来将branch不在upstream的修改以newbase为基础重构。

    注意注意:什么是git rebase 中当前分支产生的修改,是指当前分支currentBranch与差异的部分,比如当前分支currentBranch与有一个共同的祖先,那么当前分支产生的修改就是从开始(不包括

  17. git fetch : 可以获取远程仓库reposity的refspec的分支的所有commits记录,仅仅是把远程分支fetch下来,并不合并到当前分支。
  18. 注意像git merge、git rebase等都是分支操作,只有git fetch才会与远程reposity进行交互。
  19. git pull :等于git fetch + git merge
  20. git pull –rebase :等于git fetch + git rebase
  21. git remote add/rename/remove/set*:可以将远程仓库在本地做一个别名。强调一下:git是基于是历史记录的,完全可以将不同于当前git库的引用放置到当前git库中,并且完全可以用当前库的代码去提交、修改不同于当前库的其他远程git库。只要你有权限提交、修改,并且最好两个git库的历史记录其实也是相似的(才有意义啊)。
  22. git push :git push用于将本地分支提交到远程分支上,基本上就是overwrite ref,通常git会做检查,如果远程分支不是本地分支的祖先,git会拒绝本次提交。但可以使用git push -f,可以使git跳过这些检查,强行overwrite远程分支的ref,但这就有可能会是远程分支丢失一些commits,慎用。(回滚的时候可以使用)
  23. git cherry-pick :在当前分支重新应用一个commit的修改,并在当前分支产生一个新的commit记录。如果中间有冲突,使用者resolve冲突、并自己commit。
  24. git apply :git apply是应用patch到当前分支的文件上,并不会提交,可以冲stdin中读取。patch可以是通过git diff产生的(git diff > patch-1),也可以通过git format-patch。(未使用过).
  25. git am:可以合并一个文件、或者一个目录下的所有path,或者邮箱目录下的patch,使用git apply,但do more than apply。

    git am uses git apply behind the scenes, but does more work before (reading a Maildir or mbox, and parsing email messages) and after (creating commits).

  26. git submodule: 可以在当前git库中,引入其他的git库作为依赖。会在当前git库中创建.gitmodules文件,submodule可以指定目录等。
  27. git commit对象!!!一开始以为一个commit对象引用另外一个commit对象,就像是List结构链一样,还一直纠结为什么git graph要岔开,岔开的结构是怎么存储的!!!其实commit对象是可以有很多parent的,git的结构设计就是如此,修改记录并不是List结构链,而是更像是一个单向图。
  28. git merge的操作就是一个3-way-merge,就是当前分支commit-A和其他分支commit-B要合并,有一个共同祖先commit-C,就是在以commit-C作为比较的基础上,找到commt-A和commit-B的差异,然后合并处理,生成一个新的commit-D,commit-D有两个parent,第一个parent就是当前分支commit-A,第二个parent就是commit-B。

    three-way merge: https://en.wikipedia.org/wiki/Merge_(version_control)#Three-way_merge

  29. git merge的基础是git merge-base <commit-1> <commit-2> <commit-3> ...<commit-n>,git merge的工作原理是先对执行3-way的合并,得到,然后拿3-way合并的到依次例推。知道最后都合并。
  30. git rebase: 一次auto merge失败,然后人工resolve,merged。如果这次merge commit重新被apply,应该是要重新手动处理冲突的。(经常会出现在branch-a分支过了许久从分支git pull -r origin master,然后又在branch-a上执行git pull -r origin branch-a);

    It is possible that a merge failure will prevent this process from being completely automatic. You will have to resolve any such merge failure and run git rebase –continue. Another option is to bypass the commit that caused the merge failure with git rebase –skip.

注意,经常会遇到几种场景:

  1. 在当前branch-a中,修改commit-a1,提交push到远程仓库remote/branch-a;(于此同时远程remote/master仓库也有了新的commit-m1,比如他们之前共同的祖先是commit-0),然后在本地branch-a中执行’git pull –rebase origin master’,本地的提交历史就会变成’commit-0 commit-m1 commit-na1’,commit-na1表示commit-a1在rebase的时候重新apply了一遍,产生了新的commit记录,如果此时再执行git push origin branch-a,push操作就会拒绝此次push,因为remote/branch-a此时已经不是本地branch-a的祖先了。需要先执行‘git pull’进行merge操作,然后保证remote/branch-a是本地分支的祖先,才能push。
  2. 在本地分支branch-a多次git pull --rebase origin master,其实是没意义的,git rebase只找两个分支之间最近的差异,如果远程分支的差异为0次,则此次rebase是无效的。如果本地分支的差异为0次,只是相当于更新,把本地分支branch-a的引用设置成remote/master分支的引用。
  3. git checkout仅仅仅仅仅仅仅仅只是从本地registry checkout,要是想用remote最新的,应该先git fetch。
  4. 可以使用git checkout sha-hash,获取一个老版本,如果你在老版本上进行了修改,则需要-f push,或者merge
  5. 一次git pull -r origin master之后丢失本地commit的记录:

在本地o1-o2-o3-o4,其中o4是一个(无论是否带有冲突的)merge操作,远程master的记录是o1-o2-m1,在使用git pull -r origin master的时候,处理到o4重新应用的时候,貌似是重新应用没法自动处理这个merge操作,然后导致o4没法重新应用,丢失;

据说:git rebase其实是一组新的git cherrypick,对于cherrypick是查找当前commit和它的parent的差异,重新应用,但如果是一个merge操作,cherrypick就无法确定使用哪一个parent,所有就失败了。(cherrypick可以通过-m参数指定parent)

另外一个git rebase丢失记录的可能是git判断要应用的commit的修改在之前都有,然后就丢失了

作者:teazean 文章地址: