zoukankan      html  css  js  c++  java
  • git实战杂记

    将分支推送到远程仓库通常二个目的:1.多人协作开发 2.远程备份

    其实commit也是一种"备份",只是在本地,可以使用git reflog反悔。如果没有做备份,不要轻易使用git reset HEAD --hard  or git checkout -f这样非常危险的命令
    commit的重要性还体现在git rebase, git diff, git log.....都是围绕commit对象。


    多人协作分支主要分为: 管理员本地创建开发分支并推送到远程  & 开发人员配置开发分支

    1.本地创建分支并推送到仓库
    1.1.保证本地的分支都是可以编译通过的,最好是reset HEAD --hard + git clean -dxf后的代码。
    遇到过这样的情况:本地的master的修改需要通过patch提交到远程的svn上的master,而远程svn的代码会滞后同步到git服务器上的master分支, 滞后会导致: git的master分支暂时更新不到最新的代码,而工作区中的代码仍然存在于stage区域,如果此时想基于本地的master打开发分支,就需要将这些改动去掉,否则将来在开发分支上执行git rebase master的时候,最新的master(经过svn同步到git)会与开发分支中误包含的文件导致冲突。
    如果不想基于本地的master建开发分支,可以使用 git checkout -b login_site  origin/master来创建
    1.2.git checkout -b login_invite   基于本地的base branch创建的新分支并切换到新分支
    1.3.git push branches login_invite:login_invite  推送到远程的仓库,注意远程分支并不会与本地分支关联
    1.4.git branch --set-upstream login_invite branches/login_invite 本地分支关联到远程分支

    2.开发人员的操作:
    2.1 git remote add branches root@app220:/home/git.gold/×××-dev (如果开发人员没有远程库,需要添加,如果有可跳过)
    2.2 git fetch branches ( or git pull branches)
    2.3 git branch --set-upstream login_invite branches/login_invite (本身会创建分支,还可以使用git checkout -b localBranchName repositoryName/remoteBrancheName 或者git checkout --track localBranchName repositoryName/remoteBranchName)
    2.4 git checkout login_invite


    如何基本本地或远程创建分支:
    1.基于本地分支新建一个分支并checkout该分支(管理员push到远程仓库之前使用)
    git checkout -b newbranch master
    2.基于远程分支在本地新建一个开发分支并checkout该分支(开发人员跟踪管理员创建的远程分支)
    git checkout -b localBranchName repositoryName/remoteBrancheName



    开发分支rebase:
    在开发分支git rebase master,原理是将当前分支的HEAD置为master对应的reference,然后将开发分支自独立出来的每个提交逐个打到HEAD上。这样开发分支就是线性提交。
    此时如果想把分支的内容合并到master,只需切换到master,然后git merge dev_branch即可实现快速合并。
    遇到过的情况: 并不做git merge dev_branch,而是git diff master  dev_branch,生成的patch通过reivew后打patch打上svn上的master。
    注意,分支之间的diff顺序。如果生成master的patch,不要使用git diff dev_branch  master。否则生成的patch只能patch -R -p1 < patch来反打。

    rebase与merge的区别:

    1.git merge合并结果不是线性的,会存在多个父提交。与git rebase相比,会多出一次commit.

    2.stackoverflow的一个讨论:

    Assuming you start out with a tree that looks like this

                 A---B---C topic
               /
       D---E---F---G master
    

    You'll want to run 2 commands git rebase master to make it look like this

                                            A'--B'--C' topic
                                           /
       D---E---F---G master
    

    then from master run git merge topic, which will do a fast forward merge, and you'll end up with a tree that looks like this

                                                   topic
                                                 /
       D---E---F---G---A---B---C -- master
    

    Can I just checkout master and run git rebase topic??

    Running that command would result in a tree that looks like this

                                                   topic
                                                 /
       D---A---B---C---E---F---G -- master
    

    This is the (somewhat edited) git documentation for rebase, hopefully this will help:

    All changes made by commits in the current branch but that are not in upstream are saved to a temporary area.

    If you are on topic and run git rebase master, that means commits A, B and C are going into a temporary area.

    The current branch is reset to upstream. This has the exact same effect as git reset --hard upstream.

    At this point the temporary state of the tree looks like this

                              topic
                            /
       D---E---F---G -- master
    

    The commits that were previously saved into the temporary area are then reapplied to the current branch, one by one, in order.

    Commits A, B, C that were saved into the temporary area are "recommitted" to the branch you are currently on topic.

                                                 topic
                                                /
       D---E---F---G---A---B---C -- master
    

    Running git rebase topic from master would perform the exact same steps, except instead of putting commits A, B, C into the temporary area, it would put commits E, F G into the temporary area.

    一个完整开发分支rebase示例:包括新建一个rebase分支,将开发分支的多个commit合并成一个commit打到rebase分支上。如果需要review代码,还需要最终生成一个diff文件。

    假如创建开发分支code的基础是master的m_old节点。

    1.切换到master分支,reset到m_old节点,创建并切换到分支rebasecode. (创建该分支的目的就是将之前开发分支的多个commit一次性打到rebasecode上,避化rebase的操作)

    2.git diff rebasecode code> 1.diff

    3.patch -p1 <1.diff

    4.git commit (打完1.diff只会体现在工作区中,git rebase master合并针对都是commit对象,所以必须提交)

    5.切换到master并pull最新代码

    6.切换到rebasecode并执行git rebase master

    7.resolve conflict  (在rebase的过程中,如果执行git branch,显示的是no branch,如果git rebase --abort或者rebase完成之后,则git branch会显示为rebasecode)

           git add -u

       git rebase continue

    8.git diff master rebasecode > 2.diff (该文件就是用来codereview的diff)

    9.review的修改就可以直接在rebasecode上提交,而不是最初的开发分支code, 如果在code上提交,也可以git diff v_old v_now出一个diff文件,patch到rebasecode上。

    10.review完成后,再次git rebase master,git diff master rebasecode> complete.diff

    11.如果是在svn上提交,可以直接patch -p0 < complete.diff

    git diff与反patch

    经常出现误提交并push到远程分支的情况,遇到这样的情况,可以通过下面由难到易三种方式解决:

    1.新建一个分支(最好保留老分支)

    2.在远程分支上reset到上一版本。

    3.打反patch    比如现状是v1-----v2, 误提交的节点是v2,   通过git diff v2 v1 > rpatch.diff ,再通过pathch -p1 < rpatch.diff清除错误提交。结果是v1-----v2-----v3. v3的状态应该与v1一致。






    git config:

    编辑git ini文件的命令,与vim相比,git config可以在git工程下的任何目录执行。

    git ini文件分为版本库级别的配置(.git/config) > 全局配置(.gitconfig) > 系统级配置文件(/etc下)
    maven的settings.xml也有类似的覆盖策略。
    示例1:
    git config --unset的使用:用来重置git/config中的key-value。
    比如git config --unset remote.origin.url  root@172.16.10.220:/home/git.gold/daodao-site
    git config --unset  remote.origin.url则会直接删除。
    如果要删除节点[remote "origin"]下面的全部内容。git config --unset remote.origin 是错误的,删除节点使用git config --remove-section remote.origin
    示例2:通常user.name user.email设置为global,删除user.name:
    git config --unset --global user.name


    远程仓库查看与添加:
    git remote -v 查看本地配置的远程仓库 git remote add




    什么时候删除分支:分支代码被merge或者分支的patch已经checkin
    1.删除本地分支
    git branch -D branch
    2.删除远程的分支,注意:前面的空格
    git push remote  : branch



    为什么git reset HEAD --hard之后,还不能恢复以前的环境:

    因git reset HEAD --hard只以还原被git跟踪过的文件。而之前patch打过之后包含了新增文件,这些是未被git跟踪过的。

    解决:
    1.rm -rf 新增文件,然后再打patch。否则再打patch的时候,新增的文件会阻止打patch。
    2.git clean -dxf,其实也是执行rm命令,但是该命令会把工作区下untrack的所有文件删除,有些eclipse工程会在执行后受到影响。
    示例如下:
    ygao@pts/ttys000 $ git status
    # On branch master
    # Changes to be committed:
    .......
    # Changes not staged for commit:
    ......
    # Untracked files:
    #    test11


    ygao@pts/ttys000 $ git clean -dxf
    Removing test11

    ygao@pts/ttys000 $ git status
    # On branch master
    # Changes to be committed:
    ......
    # Changes not staged for commit:
    ......





    冲突的场景:
    1.打patch可能冲突,使用linux的patch命令也一样,另外merge也可能冲突
    2.rebase不同的分支冲突

    2.1 rebase冲突内容格式分析
    rebase的过程中,<<<<<的部分应该是current patch打完之后工作区代码======表示patch old打到文件的代码>>>>>>
    比如下面的示例:HEAD表示rebase先打的"add batch api" patch,接着rebase打了一个"refactor" 的patch,二个patch修改的是同一段代码导致冲突,此时<<<<<<< HEAD ...... =======表示refactor打上后,本地工作区的代码。
    而====== ........>>>>>>> add batch api表示之前的patch打的代码,处理的顺序当然是删除旧的patch的代码。

    <<<<<<< HEAD
                          return 0;
    =======
                if (o1.getLocationid() == o2.getLocationid())
                {
                    return 0;
                }
    >>>>>>> add batch api
    
    合并之后结果:
                  return 0;

    2.2 rebase之后,查看是否正确,否则git checkout --conflict=merge file接着重新解决。

    3.复杂冲突解决过程:

    <<<<<<< HEAD
    <<<<<<< HEAD
            DBLocationElement ele = DBLocationStore.getInstance().getLocationElement(locid);        
            
            while (ele != null && !ele.isGeographic())
            {
                ele = ele.getParentLocation();
            }
            
            if(ele == null)
            {
                return -1;
            }
    
    =======
            DBLocationElement ele = DBLocationStore.getInstance().getLocationElement(locid);
    =======
            DBLocationElement ele = DBLocationStore.getInstance().getLocationElement(locid);        
    >>>>>>> commit revise script
            
            while (ele != null && !ele.isGeographic())
            {
                ele = ele.getParentLocation();
            }
            
            if(ele == null)
            {
                return -1;
            }
    <<<<<<< HEAD
    >>>>>>> Revise data migration script
    =======
    
    >>>>>>> commit revise script
    
    变化1:
    
    <<<<<<< HEAD
            DBLocationElement ele = DBLocationStore.getInstance().getLocationElement(locid);        
            
            while (ele != null && !ele.isGeographic())
            {
                ele = ele.getParentLocation();
            }
            
            if(ele == null)
            {
                return -1;
            }
    
    =======
            DBLocationElement ele = DBLocationStore.getInstance().getLocationElement(locid);
            
            while (ele != null && !ele.isGeographic())
            {
                ele = ele.getParentLocation();
            }
            
            if(ele == null)
            {
                return -1;
            }
    <<<<<<< HEAD
    >>>>>>> Revise data migration script
    =======
    
    >>>>>>> commit revise script
    
    变化2:
    
    <<<<<<< HEAD
            DBLocationElement ele = DBLocationStore.getInstance().getLocationElement(locid);        
            
            while (ele != null && !ele.isGeographic())
            {
                ele = ele.getParentLocation();
            }
            
            if(ele == null)
            {
                return -1;
            }
    
    =======
            DBLocationElement ele = DBLocationStore.getInstance().getLocationElement(locid);
            
            while (ele != null && !ele.isGeographic())
            {
                ele = ele.getParentLocation();
            }
            
            if(ele == null)
            {
                return -1;
            }
    
    >>>>>>> commit revise script
    
    
    变化3:
    
            DBLocationElement ele = DBLocationStore.getInstance().getLocationElement(locid);        
            
            while (ele != null && !ele.isGeographic())
            {
                ele = ele.getParentLocation();
            }
            
            if(ele == null)
            {
                return -1;
            }
    
            
            if(ele.isGeographic())
            {
                geoId = ele.getLocationID();
            }
            
            return geoId;





    区分二种不同的冲突是因为冲突的解决方式不一样

    1.打patch冲突或merge冲突,编辑冲突跟平时的修改代码没什么差异。修改完成后,都是要把修改添加到缓存git add,然后git commit。
    2.rebase的冲突解决:
    2.1解决完一个补丁的应用冲突,git add -u用来将已track的文件修改加入stage。
    2.2接着执行git rebase --continue直接另一个补丁制造了冲突
    重复2.1,2.2直至rebase完成,注意,rebase不需要显式地执行commit。commit是git自动完成。
    3.查看冲突可以使用git diff, git diff在不同的场景,使用的含义不同。


    git pull:

    相当于git fetch+git merge,git merge如果属于快速合并,会形成一个提交点。
    git merge对应的commit点可以使用MERGE-HEAD(FETCH-HEAD)指代
    git push对应的commit点可以对应的是HEAD

     git add:

    与传统的SVN不同,add操作不仅仅是把新文件加入到版本控制,对于文件内容的修改,git也需要add,这说明git add并不是象svn一样,针对文件名,而是针对文件的内容。



    git stash  & git stash pop的使用场景:
    示例:
    master分支被别人修改并push,而自己的本地代码并没有commit,此时pull远程的代码会提示本地有未提交的修改文件。

    此时就是git stash很好的使用场合:

    1.如果git stash+git pull+git stash pop没有冲突,就表示自动合并,这很有用,不需要在git pull之前,通过git checkout放弃已有的修改。

    2.如果制造了冲突(通过打Patch制造),解决冲突,但是发生的可能性小,1的场合比较常见。

    3.如果制造冲突,还有一种办法是本地的代码在pull之前,commit到head,但不push(也push不了,因为与远程冲突),git pull远程的代码做merge时,会与本地的代码冲突。实质是merge制造的冲突。

    附:git stash pop与下面的操作类似:
    1.先将本地的代码基于旧的version生成一个diff文件。
    2.git reset HEAD --hard 抛弃本地工作区的修改。
    3.git pull
    4.尝试打diff制造冲突。

    git blame与git log比较

    git blame  细粒度,在类或文件中查看某个方法或代码块的历史。git log主要用来查看文件或类的历史。是粗粒度的。

    示例:git blame -L 10,50  Test.java     -L用来指定要跟踪的代码块或方法的起止范围, 后面必须指定是在哪个类或文件中。

    git log --author ygao -p file   --author可以用来查询某个人的提交记录,-p是显示diff文件,file可以指定跟踪的是哪个文件。

    git+svn模式:git server只能pull,不能push,svn server通过打patch checkin

    git本地检出的master分支不能push,只能pull.
    一旦push之后就会形成一个commit点,当仓库的master分支从svn的服务器同步到了最新的代码时,会发现仓库中的HEAD指向的commit对象并不是上一次同步的时候,pull形成的commit,而是一个领先的commit,此时merge就是Non Fast Forward。如果在Non Fast Forward的情况下做merge操作,就会出现二个parent的情况,也会出现non line history的问题。
    non line history就象是传统的svn提交的commit都是一条直线。通常git merge会形成non line history而推荐使用git rebase。

     
     
     
     
     
  • 相关阅读:
    bzoj3994:[SDOI2015]约数个数和
    数列分块1-9
    luogu P2059 [JLOI2013]卡牌游戏
    luogu P1623 [CEOI2007]树的匹配Treasury
    博弈论与SG函数
    luogu P1169 [ZJOI2007]棋盘制作
    luogu P1623 [CEOI2007]树的匹配Treasury
    [BZOJ4896][THUSC2016]补退选(Trie)
    [BZOJ3745][COCI2015]Norma(分治)
    [BZOJ5006][LOJ#2290][THUWC2017]随机二分图(概率+状压DP)
  • 原文地址:https://www.cnblogs.com/highriver/p/2729207.html
Copyright © 2011-2022 走看看