美文网首页
git 使用小结

git 使用小结

作者: 艾伦先生 | 来源:发表于2016-12-05 15:34 被阅读136次

    写在前面

    有关Git的诞生故事以及Git的强大,这里无须赘述。写这篇文章的原因是因为,习惯了用Git桌面工具向Github提交代码的我,换了一台笔记本后突然发现我连最基本的Git命令行都不会了。。。羞愧至极,所以就开始我的学习之路。

    我的学习方法是,先大概了解Git的相关指令,然后简单试验之后,开始搜集各类博客资料,汇总之后,再次对试验过程进行整理,我整理了一些高质量的博文在附录中或者在文中。

    本文是一篇阶段性的记录文章,只记录我容易遗忘的知识点,引用的文章图片都标注了出处,好了下面开始本文

    为什么很多人推荐从svn转向git

    从使用者角度分析:

    1. svn下载源代码慢。在git中一个几个G的版本库,一般一二十分钟就能下载完毕,但是在svn中要一个小时左右;
    2. svn随时都得要与服务器交互,无论是查看log,还是查看以往的版本你必须跟服务器相连,并且速度奇慢无比,而git做这些几乎是瞬间的事;
    3. 各个分支之间的补丁迁移麻烦,在git上只要两三个命令就可以完事的(其实一个命令,因为需要查找与分支切换),但是在svn上你必须要下载每个分支的代码,然后比较修改,再上传;

    从服务器角度说为什么要用git:

    1. git版本库占用空间小(几乎是svn的分支数之一也就是说如果有四个分支,svn的版本库的体积将接近git的四倍),SVN每个分支都是一份代码的copy,而git每个分支只是各个提交点的hash值的集合。分支几乎不占用什么空间;
    2. git是分布式管理系统,完全可以不对代码进行备份,但SVN不行,一旦服务器的硬盘挂掉整个代码库就完了;
    3. git不用时时联网查询,并且对文件进行压缩,使得文件体积大大减小,并且传输速度快,svn是单个文件,git是压缩后的,在使用svn时我已经碰到过好几次服务器无响应了。由于git很多都可以在本地操作的,所以大大降低了客户端对服务器的连接,出现这种情况的概率会大大减小;
    4. 如果客户端离服务器端非常远,在网速糟糕的情况下,用svn下载代码速度远不上git.

    推荐文章 http://www.cnblogs.com/common1140/p/3952948.html

    git 工作原理

    git跟踪并管理的是修改,而非文件。而且git只能文本信息的修改和恢复,对于二进制文件,比如word或者图片,只能监听到改动却无法对改动进行恢复。

    git 分支概念

    每次提交(git commit),git都会把这些提交串成一个时间线(后文中管这个时间线叫做分支)

    默认情况下,使用git init初始化的项目只有一条分支,叫做master分支。可以理解成有一个叫做master的指针指向着当前最新一次提交。还有一个概念就是存在一个HEAD指针,而且请记住HEAD永远指向当前的分支。HEAD指向的是哪个分支,当前我们就向那分支提交代码。

    下图展示了一个普通git项目的分支结构

    可见,当前项目只有一个分支,就是master分支,有三次提交。然后HEAD指针,指向当前的master分支。

    因分支必须指向某一次commit,所以必须先有commit才有后来的分支。某个分支必须至少有一次提交git commit之后,才会在git branch指令中显示出来

    随着你的每次提交,master分支都会向前移动,HEAD指针也同样跟着向前移动。

    当某个场景中,我们从当前分支master创建了新的分支,比如叫做dev。下面这条指令是创建并切换到dev分支。

    git branch -b dev
    

    背地里,Git会新建一个指针叫做dev,指向master指向的相同的提交,然后再把HEAD指向dev,就表示当前分支在dev上。

    可以看出,git创建一个分支很快,因为除了增加一个dev指针,改动HEAD指针指向以外,工作区的文件没有变化。

    从现在开始,对工作区的修改和提交就是针对dev分支了。比如新提交一次后,dev指针就会往后移动一步,但是注意,master指针不变。

    git分支合并

    假如我们在dev分支上的开发工作完成了,就可以吧dev分支上的代码合并到master分支上。有两种情况

    • 快进模式 Fast forward 简称FF模式
      本模式下的前提是:master分支,在dev分支拉出后,没有过提交。这样的合并,Git直接就把master指向dev指向的当前分提交即可,完成了快速合并

        git checkout master
        git merge dev
      

    合并完dev分支后,dev分支可以删掉了,删掉dev分支的操作,其实就是把dev指针给删掉就好了。现在就只剩下一个master指针指向当前提交了

        git branch -d dev
    

    如果没有提交,就删除分支,git会提示你,你需要先提交在删除。但是可以强制删除,使用

        git branch -D dev
    
    • 非快进模式

    本模式发生的情况就是,分支dev对某个文件,比如readme.txt文件修改后并提交,此时切换回master,发现master分支也修改了readme.txt并提交。 这样master分支和dev分支都对同一个文件进行了修改并提交,在master分支合并dev分支时,会提示冲突。如下所示

    这个时候Git无法实现快速合并,只能先尝试着将各自的修改合并后提交,但是这个时候经常伴随着冲突,比如readme.txt文件出现了冲突,文件被修改成了如下内容

        <<<<<<< HEAD
        Creating a new branch is quick
        Creating a new branch is quick & simple.
        =======
        Creating a new branch is quick AND simple.
        >>>>>>> dev
    

    === 分割冲突代码,上面HEAD标记的是当前分支的内容,下面dev标记的是dev分支合并过来的内容,自己进行取舍

    解决完冲突后,将冲突文件 git add git commit提交,这时我们的分支图变成这个样子

    最后查看一下分支的合并情况

    git log --graph --pretty=oneline --abbrev-commit
    

    上面的截图可以看出,解决冲突的部分被单独划归出来了

    禁用快速合并模式(简称no-ff模式)保留分支commit信息

    前面提到过快速合并,快速合并的场景下,快速合并没有冲突。但是有个缺点就是,在合并后的master的log日志中看不到本次合并的dev分支的commit的id和描述信息

    即,合并完成后,一旦删除了dev分支,我们既无法知道分支存在过,也无法区分那些修改是在分支上进行的。下面看一个禁止掉快速合并的情况

    git merge --no-ff -m "merge with no-ff" dev
    

    上面可以看到 分支合并历史中记录了合并过来的分支的commitid

    如果不适用no-ff模式,单纯使用git reflog查看一下日志,你能看出来的,那些分支合并过来了吗

    git stash

    当前情况:此时有两个分支,master和dev,dev编辑到一半,并未成功,所以不能提交。但此时master有一个bug需要马上去修复,但因为dev无法提交,所以用stash保存现场。

    git stash
    

    转去master去把bug修复完后,checkout到dev开发分支,应该先merge master 然后再

    git stash pop 
    

    恢复dev开发现场

    多人协作

    当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支关联起来,并且,远程仓库的默认名称是origin。

    • 查看远程仓库的名称

        git remote
      
    • 推送分支

      推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上:

        git push origin master
      
    • 抓取分支

        git clone git@github.com:yourname/learngit.git
      

      从远程库中获取分支时,默认只能获取到master分支,使用下面指令创建并关联远程的dev分支

        git checkout -b dev origin/dev
      

      然后提交dev分支

        git push origin dev
      
    • 解决远程冲突

      如果你提交之前,恰好有小伙伴对当前的dev的分支也做了某些修改,则可能提示你提交失败,这个时候,你需要使用

        git pull
      

      来把最新的提交从origin/dev上抓取下来,然后本地合并,解决冲突中,再次提交

    • 删除远程库

      git push origin :<branch name>

    因此,多人协作的工作模式通常是这样:

    1. 可以试图用git push origin branch-name 推送自己的修改;

    2. 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;

    3. 如果合并有冲突,则解决冲突,并在本地提交;

    4. 没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功!

    注:如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to=origin/dev dev

    更多内容参考阮一峰老师的这篇讨论git工作流程的文章
    经典文章

    git 后悔药系列

    git diff 分析代码差异

    diff --git a/readme.txt b/readme.txt
    index 46d49bf..9247db6 100644
    --- a/readme.txt
    +++ b/readme.txt
    @@ -1,2 +1,2 @@
    -Git is a version control system.
    +Git is a distributed version control system.
     Git is free software.
    

    git diff #是工作区(work dict)和暂存区(stage)的比较
    git diff --cached #是暂存区(stage)和分支(master)的比较

    git 错误回退

    • 将还在工作区内未添加到缓存区的文件恢复到上次add之前的状态

        git checkout -- filename
      
    • 将提交到缓存区的文件回退到工作区,将缓存区的该文件恢复到上次commit之前的状态

        git reset HEAD filename
      
    • 已经commit了,想回退到之前的某次提交

      • 回退至某次快照(commit提交)

          git reset --hard 3628164(此数字为某次commit的log日志前七位)
        
      • commitid可以通过如下命令查找,翻看历史操作记录

          git reflog
        
    • 已经提交到远程库了

      无法回退

    git 删除文件

    使用 git rm filename 删除指定文件

    执行完上面这条指令,相当于先删掉了文件,然后执行了git add,将删除的结果添加到了暂存区,所以如果想恢复文件的话,需要进行如下操作

    git reset HEAD filename #恢复暂存区
    git checkout -- filename #恢复工作区
    

    删除历史文件(它会从HEAD所在的分支历史中查询并删除<FILE>文件)

    git filter-branch --tree-filter 'rm -f <FILE>' HEAD
    

    关联github

    先建立本地库,关联远程库

    • 生成秘钥

        ssh-keygen -t rsa -C "youremail@example.com"
      

      以在用户主目录里找到.ssh目录,里面有id_rsa和id_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。

    • 在github填写公钥

      登陆GitHub,打开“Account settings”,“SSH Keys”页面:然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容:

    • 将本地git项目关联远程仓储

        $ git remote add origin https://github.com/yourname/learngit.git
      
    • 将本地改动推送到远程仓库

        git push -u origin master    #将当前分支推送到远端,第二次开始不用使用-u
      

    先建立远程库,用本地库关联

    • 克隆远程库

        git clone https://github.com/yourname/learngit.git
      

    GitHub给出的地址不止一个,还可以用https://github.com/yourname/gitskills.git这样的地址。实际上,Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议。

    使用https除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh协议而只能用https。

    git 标签处理

    发布版本时,我们通常会给版本库打一个标签(tag)。标签是以一个更让人理解的方式来标记commit,

    比如说请给我找出4.5.6版本的提交,而不是请给我找出commit id为5f54f5sd的提交。“找到了:git show v4.5.6”

    类似IP和域名的关系。

    git中标签虽然是版本库的快照,也是一个指向当前commit的指针。(和分支很像,但是分支可以移动,标签不能移动),所以创建和删除标签都是瞬间完成的。

    • 在当前分支直接使用如下指令创建tag

        git tag v1.0#可以是任意有意义的字符串
      

    注:然后使用 git tag查询所有标签。tag不是按时间顺序排布的,而是按照名字排序的

    • 标签是默认打在最新一次提交上的,添加commitid来对任意提交打tag

        git tag v1.0 commitid
      
    • 使用下面的指令可以用来创建带有说明的tag

        git tag -a v0.1 -m "version 0.1 released" 3628164
      
    • 使用 git show <tagname>可以查看tag的说明

    • git push origin <tagname>可以推送一个本地标签(标签默认存储在本地)

    • git push origin --tags可以推送全部未推送过的本地标签

    • git tag -d <tagname>可以删除一个本地标签;

    • git push origin :refs/tags/<tagname>可以删除一个远程标签。

    提高效率的小指令

    • 彩色的git输出

        git config color.ui true
      
    • 让快照的log信息紧凑输出

        git log --pretty=oneline
      
    • 以图表的形式查看提交历史

        git log --graph
      
    • 只查看commit id的精确位数(一般前7位就能识别)

        git log --abbrev-commit
      
    • 给常常的指令设置别名

        git config --global alias.st status
        git st
      
        git config --global alias.unstage 'reset HEAD'
        git unstage test.py
      
    • 只查看最近一次的log

        git log -1
      
    • 获取本地公钥的快捷方式(文本软件打开有可能出错!)

      mac
      pbcopy < ~/.ssh/id_rsa.pub

      windows
      clip < ~/.ssh/id_rsa.pub

      linux
      sudo apt-get install xclip
      xclip -sel clip < ~/.ssh/id_rsa.pub

    精彩理解

    我觉得这svn和git两个工具主要的区别在于历史版本维护的位置

    Git本地仓库包含代码库还有历史库,在本地的环境开发就可以记录历史
    而SVN的历史库存在于中央仓库,每次对比与提交代码都必须连接到中央仓库才能进行

    这样的好处在于:
    1、自己可以在脱机环境查看开发的版本历史
    2、多人开发时如果充当中央仓库的Git仓库挂了,任何一个开发者的仓库都可以作为中央仓库进行服务

    svn中央服务器挂了,那我一样可以将本地的项目重新搭建一个服务器呢?

    答:不行,你的本地没有历史版本

    答!! 断网了,看看能不能查看历史版本?看看能不能提交代码?

    关于git commit

    git commit命令确认的是最近的一次git add,如果文件最近的内容修改没有被git add,那么在git commit时,最近的文件修改内容不会被提交。

    • 指定文件可以提交未保存到暂存区(unstaged)
      vi readme.txt
      git commit readme.txt -m "commit with unstaged modify"
      vi readme.txt
      git commit -a -m "commit all file will commit unstaged modify"

    • 不使用其他参数不会提交unstaged modify
      git commit -m "commit without param will not commit unstaged modify"

    是否可以把公钥私钥一起给别人呢

    只需要给公钥。

    原始数据经过私钥加密后只能用公钥解密,换句话说,别人收到经过加密的数据后,如果用你的公钥能够解密,那么他就可以确认这些数据是你发送的
    如果把私钥给别人的话,别人就可以冒充你给别人发东西了

    关于回退操作

    对于没提交到stage的修改;

    删除后,重新恢复,修改的内容是会直接消失的;比如你在文件中添加一个字符:‘1’;不用git add file 添加到stage;直接用rm删除后,再用git checkout -- file恢复;恢复过来后,去看文件是没有这个字符:‘1’的。

    印证了 git checkout -- file恢复的是已经添加到stage的内容;

    而使用git rm删除的就是stage的内。git reset HEAD -- file会从master中将被删的stage的内容拷贝过去。如果你使用了git rm之后接着使用git commit -m “remove file”则会删除master里的内容;

    所以,关于一次回退流程是这样的

    • git reset --hard HEAD^可以将删除的master从回收站恢复过来;
    • 然后利用git reset HEAD -- file 从master中拷贝到stage中;
    • 最后再用git checkout -- file 从stage中拷贝到工作目录中。

    关于未提交的修改

    现象:
    在分支修并提交后,切到主干,主干的工作区是干净的;在分支修改不提交,切回主干,主干工作区是被修改过未提交的状态

    解释:
    这样做的好处可能是你本来想对DEV分支进行想改,但是你忘了切换到dev你还在master就已经改了工作区,如果这时切换到dev修改的工作区内容没了,岂不是很操蛋。只有commit之后才确定修改的内容属于哪个分支。

    未commit的工作区文件和stage文件是可以灵活地在且仅在任一branch存在的。这是前提。

    • 在工作区做了修改,提交到DEV的分支,再切换回master

      这时候,对master来说,工作区没有任何未提交的修正(因为所有修正都已经commit)。则工作区内容应该是与master分支最后一次提交的内容一致。(处于任何其他时间点,都意味着工作区可能存在修正,这就出现了矛盾)

    • 在工作区做了修改,没有提交到DEV分支,即切换回master
      这个时候,对master来说,工作区有了修正,那么就保持工作区的现有状态即可。

    pull&& push

    pull:本地 <-- 远程
    push:本地 --> 远程

    本质上都是同步commit

    如果你本地落后远程,必然要pull
    如果你本地超前远程,必然要push

    课后查看

    • 为什么会产生冲突?什么时候产生冲突
    • 上面的截图可以看出,解决冲突的部分被单独划归出来了
    • 图形界面操作
    • 断网情况下查看一下svn的操作

    附录

    相关文章

      网友评论

          本文标题:git 使用小结

          本文链接:https://www.haomeiwen.com/subject/gcakmttx.html