git 使用小结

2016-12-05  本文已影响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分支上。有两种情况

合并完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。

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

  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 错误回退

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

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

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

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的指针。(和分支很像,但是分支可以移动,标签不能移动),所以创建和删除标签都是瞬间完成的。

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

提高效率的小指令

精彩理解

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

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

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

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

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

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

关于git commit

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

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

只需要给公钥。

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

关于回退操作

对于没提交到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里的内容;

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

关于未提交的修改

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

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

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

pull&& push

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

本质上都是同步commit

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

课后查看

附录

上一篇 下一篇

猜你喜欢

热点阅读