I want to fly higher
programming Explorer
posts - 114,comments - 263,trackbacks - 0

Git


分布版本控制系统

Git诞生


为什么Linus不把Linux代码放到版本控制系统里呢?不是有CVS、SVN这些免费的版本控制系统吗?  
因为Linus坚定地反对CVS和SVN,这些集中式的版本控制系统不但速度慢,而且必须联网才能使用。
有一些商用的版本控制系统,虽然比CVS、SVN好用,但那是付费的,和Linux的开源精神不符。

集中式vs分布式


集中式版本控制系统,版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。  

    集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网内还好,带宽够大,速度够快,可如果在互联网上,遇到网速慢的话,可能提交一个10M的文件就需要5分钟,这还不得把人给憋死啊。  
    
    分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说你在自己电脑上改
了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。----远程仓库

    和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。

    在实际使用分布式版本控制系统的时候,其实很少在两人之间的电脑上推送版本库的修改,因为可能你们俩不在一个局域网内,两台电脑互相访问不了,也可能今天你的同事病了,他的电脑压根没有开机。因此,分布式版本控制系统通常也有一台充当
“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。

    当然,Git的优势不单是不必联网这么简单,后面我们还会看到Git极其强大的分支管理,把SVN等远远抛在了后面。

    集中式的服务器挂了所有人都挂了,因为完整仓库只存在服务器上,分布式如果github挂了你可以重新建一个服务器,然后把任何一个人的仓库clone过去。
    一句话总结:分布式版本控制的每个节点都是完整仓库。
    反之SVN则不可以,如果svn中央服务器挂了,是无法根据本地的项目搭建一个服务器的,因为本地没有历史版本。

    svn上你要创建分支,必须联网。git就不用,直接就可以创建分支。感觉上就像是你在自己的机器上同时安装了svn服务端和客户端。git你可以在自己的机器上一个人随便开发,它拥有完整的版本控制服务。

    git没有权限控制。
    “权限”问题是很多商业软件选择svn的最重要的原因。企业开发商业软件,通常一个开发者只有一部分模块的权限,用来避免完整的代码被太多人获取从而影响利益。商业软件release一个版本通常会有专人负责,或者用软件自动构建。开源就不同了,
完全不用考虑这些。

安装Git(windows)

我是从 https://git-scm.com/ 下载的Git-2.8.4-64-bit.exe
安装完毕后,命令后可以直接使用git:
    usage: git [--version] [--help] [-C <path>] [-c name=value]
           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
           <command> [<args>]
    
另外因为使用TortoiseSVN很长时间,所以对于git-gui,选择了TortoiseGit-2.1.0.0-64bit.msi
These are common Git commands used in various situations:

创建版本库


1.git init
    Initialized empty Git repository
    目录下生成一个.git隐藏目录
2.git add <file>,添加多个文件
3.git commit -m "comment",提交
    注:后面两个命令和svn命令相关命令相似,只是命令相似而已,具体底层实现并一定相同。


git add readme.md   // 文件颜色变化显示+号(tortoise-git)
git commit -m "wrote a readme.md"   // 文件显示绿色对勾(tortoise-git),提示显示2(+)
    [master (root-commit) 90857d7] wrote a readme.md
    1 file changed, 2 insertions(+)
    create mode 100644 readme.md

获取工作区状态、查看修改内容


git status  // 提示没有提交的内容
    On branch master
    nothing to commit, working directory clean


// 修改readme.md,此时readme.md文件显示红色叹号(tortoise-git)
git status  // 显示readme.md被修改了
    On branch master
    Changes not staged for commit:
      (use "git add <file>" to update what will be committed)
      (use "git checkout -- <file>" to discard changes in working directory)
        modified:   readme.md
    no changes added to commit (use "git add" and/or "git commit -a")


git diff // 查看具体修改的内容,最后面显示加了两行
    diff --git a/readme.md b/readme.md
    index 25339a4..9995557 100644
    --- a/readme.md
    +++ b/readme.md
    @@ -1,2 +1,4 @@
     ### This a git tutorial
    +
    +* git status


git add readme.md // git add
git status // add之后查看状态,显示变化将被提交
    On branch master
    Changes to be committed:
        (use "git reset HEAD <file>" to unstage)
    modified:   readme.md


git commit -m "add 2 lines"
git status // commit之后,查看状态,显示没有东西提交
    On branch master
    nothing to commit, working directory clean

版本回退


git log // 查看历史记录,可以看到三次提交记录
    commit d1eb111fe227cbd39168de8237726d5df575415f
    Author: mavsplus <mavsplus@aliyun.com>
    Date:   Tue Jun 14 16:23:31 2016 +0800
        delete chinese
    commit 0b602fb113c8bbad53e6fbfe7edea62ebcea781e
    Author: mavsplus <mavsplus@aliyun.com>
    Date:   Tue Jun 14 16:12:32 2016 +0800
        add 2 lines
    commit 90857d7381c9c860e23e01c1bca20724e250c94a
    Author: mavsplus <mavsplus@aliyun.com>
    Date:   Tue Jun 14 15:52:16 2016 +0800
        wrote a readme.md
git log --pretty=oneline // 调整展示形式
    d1eb111fe227cbd39168de8237726d5df575415f delete chinese
    0b602fb113c8bbad53e6fbfe7edea62ebcea781e add 2 lines
    90857d7381c9c860e23e01c1bca20724e250c94a wrote a readme.md
// commit id,即版本号,和SVN不同,不是从1,2,3递增的数字,而是一个SHA1计算出来。因为Git是一个分布式的版本控制系统,版本号不能冲突。


版本回退的话,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本。上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100。
git reset --hard HEAD^  // 版本回退到上一个版本
// 注意用这个命令出现 More?
git reset --hard HEAD~  // 用~替换^,版本顺利回退到上一个版本
    HEAD is now at 0b602fb add 2 lines // 0b602fb即上一个版本的commit id的前几位 
git reset --hard d1eb111 // 根据版本号回退,目前这个版本号是最新的一个版本号
    HEAD is now at d1eb111 delete chinese
// 1.版本号没有必要全写,前几位就可以,这里是7位
// 2.版本回退既可以向前,也可以向后,只需要版本号即可,是不是有点双向链表的意思。
// 3.版本回退快的原因是因为Git在内部有个指向当前版本的HEAD【指针],当你回退版本的时候,Git仅仅是不断的将HEAD指针指向不同的commit id


如果找不到commit id怎么办?
git reflog  // 用来记录你的每一次命令
    d1eb111 HEAD@{0}: reset: moving to d1eb111
    0b602fb HEAD@{1}: reset: moving to HEAD~
    d1eb111 HEAD@{2}: commit: delete chinese
    0b602fb HEAD@{3}: commit: add 2 lines
    90857d7 HEAD@{4}: commit (initial): wrote a readme.md
// 可以看到每次的commit id,有了commit id,有回退到任何版本都可以,前进后退都可以

工作区和暂存区


Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。
电脑里能看到的目录,如本人的git_demo文件夹就是一个工作区,该目录下有上面举例的readme.md,以及初始化创建的版本库,.git隐藏目录。
Git版本库中重要的东西:
    1.称为stage(或者叫index)的暂存区
    2.Git为我们自动创建的第一个分支master
    3.指向master的一个指针叫HEAD

示意图:

image


git add 命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支。

经过git add后,示意图:

image

经过git commit后,示意图:

image


注意:这只是示意图
1.从理解上看,暂存区就像购物车。
2.当多个文件修改完成放入暂存区的时候,发现其中一个文件的代码有问题,这时候你可以用checkout单独将这个文件还原重改;如果你将这些文件一次性全部放进库里,等你发现有问题时就不能单独拿出一个文件了,只能版本回档。
3.其实commit后,暂存区并不是"没有任何内容了"。这句话不妥,暂存区不是没有任何内容了,而是暂存区的内容都已经同步到了最近的一次提交。  
    git diff --cached 命令可以对比暂存区和最近一次提交的不同。当使用该命令后,会发现没有任何输出,则说明暂存区和已提交仓库是完全一样的。
4.git diff 查看当前没有add的内容修改,即是比较的是a(暂存区)和 b(工作区,本地文件)。
5.git diff --cached 比较的是a(已提交仓库)和 b(暂存区)。
6.其实commit操作只是让暂存区和分支(master)版本保持一致,是同步操作。

管理修改


Git管理的是修改,当你用git add命令后,在工作区的第一次修改被放入暂存区,准备提交,但是,在工作区的第二次修改并没有放入暂存区,所以,git commit只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。
git diff HEAD -- readme.md  命令可以查看工作区和版本库里面最新版本的区别
第一次修改 -> git add -> 第二次修改 -> git add -> git commit,好,现在,把第二次修改提交了。
每次修改,如果不add到暂存区,那就不会加入到commit中。


git commit -m "not add,commit directly" // 没有add,直接commit,会提示"Changes not staged"
    On branch master
    Changes not staged for commit:
            modified:   readme.md
    no changes added to commit


git commit -m "add,then commit" // 下面是正确commit的输出
    [master b60ed5f] add,then commit
     1 file changed, 1 insertion(+)

撤销修改


git checkout -- readme.md 命令 意思就是,把readme.md文件在工作区的修改全部撤销,这里有两种情况:
    一种是readme.md自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
    一种是readme.md已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit或git add时的状态。


git checkout -- readme.md   // 撤销修改,没有任何输出
git reset HEAD file //  可以把暂存区的修改撤销掉(unstage),重新放回工作区
git reset HEAD readme.md
    Unstaged changes after reset:
    M       readme.md
git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。


场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file。
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,则参考"版本回退"一节。不过前提是没有推送到远程库。

删除文件


git add delete.txt  // 增加一个新文件
git commit -m "add delete.txt"  // 提交至master
    [master 6e56cbf] add delete.txt
     1 file changed, 0 insertions(+), 0 deletions(-)
     create mode 100644 delete.txt
del delete.txt  // 删除,dos命令
git status  // 查看状态,显示删除了文件
    On branch master
    Changes not staged for commit:
      (use "git add/rm <file>" to update what will be committed)
      (use "git checkout -- <file>" to discard changes in working directory)
            deleted:    delete.txt
    no changes added to commit (use "git add" and/or "git commit -a")
git rm delete.txt   // 从版本库中删除并且提交
    rm 'delete.txt'
git commit -m "delete delete.txt"
    [master 211d1ae] delete delete.txt
     1 file changed, 0 insertions(+), 0 deletions(-)
     delete mode 100644 delete.txt
git status  // 此时工作区"干净"了
    On branch master
    nothing to commit, working directory clean


git add delete2.txt // 添加另一个文件
git commit -m "another delete.txt"  // 提交
    [master 7655a07] another delete.txt
     1 file changed, 0 insertions(+), 0 deletions(-)
     create mode 100644 delete2.txt
del delete2.txt // dos删除
git checkout -- delete2.txt // 还原,还原可能误删的文件
git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

远程仓库


Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。怎么分布呢?最早,肯定只有一台机器有一个原始版本库,此后,别的机器可以“克隆”这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。
GitHub,提供Git仓库托管服务。只要注册一个GitHub账号,就可以免费获得Git远程仓库。
注:
    1.本人未按照教程的方式创建SSH Key,我是之前clone了github的项目,然后push远程的时候输入
    了github的账号和密码。
    2.因为需要将本地库push,所以创建ssh key,因为本地Git仓库和GitHub仓库之间的传输是通过SSH加密
    3.因为安装了Git-2.8.4-64-bit.exe,安装完毕后,会有一个GitBash,打开,输入:
        $ ssh-keygen -t rsa -C "mavsplus@aliyun.com"
      则在用户主.ssh目录下生成id_rsa、id_rsa.pub
    4.在github的settings#SSH Keys#New SSH key,将id_ras.pub中的添加进去即可。
    5.为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。

添加远程库


1. github上New repository,取名gitTutorial,访问地址:https://github.com/mavsplus/gitTutorial
2. git remote add origin git@github.com:mavsplus/gitTutorial.git // 将本地仓库与github上的库关联,远程库的名字是origin
3. git push -u origin master // 把本地库的所有内容推送到远程库中
    报错:
        Permission denied (publickey).
        fatal: Could not read from remote repository.
    解决:
        还是需要添加publickey,即需要创建ssh key并和github账号绑定.
    继续报错:
        ! [rejected]        master -> master (fetch first)
        ! [rejected]        master -> master (non-fast-forward)
    原因:因为github仓库中自动创建了一个README.md,不允许直接覆盖上去.
    解决: git push --force origin master // 强制push,远程仓库的readme.md被覆盖或者fetch first.
            Counting objects: 18, done.
            Delta compression using up to 8 threads.
            Compressing objects: 100% (12/12), done.
            Writing objects: 100% (18/18), 1.56 KiB | 0 bytes/s, done.
            Total 18 (delta 2), reused 0 (delta 0)
            To git@github.com:mavsplus/gitTutorial.git
             + fb1fd6a7655a07 master -> master (forced update)
4. 此时 https://github.com/mavsplus/gitTutorial 展示和本地完全同步


要关联一个远程库,使用命令git remote add origin git@server-name:path/repo-name.git;
关联后,使用命令git push -u origin master第一次推送master分支的所有内容;
此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改;
加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来。

从远程库克隆


git clone git@github.com:mavsplus/gitTutorial.git // 从github上远程克隆
    Cloning into 'gitTutorial'
    remote: Counting objects: 18, done.
    remote: Compressing objects: 100% (10/10), done.
    remote: Total 18 (delta 2), reused 18 (delta 2), pack-reused 0
    Receiving objects: 100% (18/18), done.
    Resolving deltas: 100% (2/2), done.
    Checking connectivity done.
要克隆一个仓库,首先必须知道仓库的地址,然后使用git clone命令克隆。
Git支持多种协议,包括https,但通过ssh支持的原生git协议速度最快。
使用https除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh协议而只能用https。

分支管理


分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
其他版本控制系统如SVN等都有分支管理,但是用过之后你会发现,这些版本控制系统创建和切换分支比蜗牛还慢,简直让人无法忍受,结果分支功能成了摆设,大家都不去用。
但Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。

创建与合并分支


Git仓库初始化有一个默认主分支,即Master分支.
一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:

image


每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长:
当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:

image


你看,Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化!不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:

image


假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:

image


所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:

image



git checkout -b dev // 创建dev分支,然后切换到dev分支,-b参数表示创建并切换
    Switched to a new branch 'dev'
// 上面的命令等同于下面两个命令
// git branch dev
// git checkout dev
git branch  // 列出所有分支,当前分支前面会标一个*号
    * dev
      master
git add readme.md   // 在dev分支add并commit
git commit -m "branch dev"  // 从commit的输出看,会显示在当前哪个分支进行操作,即[dev 10c4e87]
    [dev 10c4e87] branch dev
    1 file changed, 1 insertion(+)
git checkout master // dev分支的工作完成,我们就可以切换回master分支
    Switched to branch 'master'
// 此时发现readme.md被reload了,之前在dev添加的内容不见了.因为那个提交是在dev分支上,而master分支此刻的提交点并没有变:

image



git merge dev   // 把dev分支的工作成果合并到master分支上
    Updating 7655a07..10c4e87
    Fast-forward
    readme.md | 1 +
    1 file changed, 1 insertion(+)
git merge命令用于合并指定分支到当前分支。合并后,再查看readme.txt的内容,就可以看到,和dev分支的最新提交是完全一样的。
注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。
当然,也不是每次合并都能Fast-forward.
合并完成后,就可以放心地删除dev分支了.
git branch -d dev // 合并完成后,就可以放心地删除dev分支了.
    Deleted branch dev (was 10c4e87).
git branch  // 查看分支,此时只剩下master分支了
    * master
因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。


Git鼓励大量使用分支:
查看分支:git branch
创建分支:git branch <name>
切换分支:git checkout <name>
创建+切换分支:git checkout -b <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>

解决冲突


git checkout -b dev2 // 准备一个新分支
    Switched to a new branch 'dev2'
git add readme.md   // 修改dev2分支readme.md,添加一行并提交
git commit -m "dev2 branch"
    [dev2 fd26fe3] dev2 branch
    1 file changed, 1 insertion(+)
git checkout master // 切回master
    Switched to branch 'master'
git add readme.md // 同样修改readme.md,和dev2分支修改同一行,造成冲突
git commit -m "switch 2 master"
    git commit -m "switch 2 master"
git merge dev2 // 合并,提示冲突
    Auto-merging readme.md
    CONFLICT (content): Merge conflict in readme.md
    Automatic merge failed; fix conflicts and then commit the result.
git status // 通过查看状态也可以看到冲突
    On branch master
    You have unmerged paths.
      (fix conflicts and run "git commit")
    Unmerged paths:
      (use "git add <file>" to mark resolution)
            both modified:   readme.md
    no changes added to commit (use "git add" and/or "git commit -a")
此时查看readme.md文件,发现冲突(冲突形式也类似svn)
    <<<<<<< HEAD
    * swith to master branch
    =======
    * create a new dev2 branch
    >>>>>>> dev2

image


master分支和dev2分支各自都分别有新的提交.这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。
只有解决冲突,再次提交,我是这样解决的:
    * create a new dev2 branch
    * swith to master branch
git add readme.md   // 在master解决冲突后add,commit
git commit -m "conflict fixed"

image

此时master分支和dev2分支变成了是上图所示


git log --graph --pretty=oneline --abbrev-commit // 可以看到分支合并的情况
    *   aea7a48 conflict fixed
    |\
    | * fd26fe3 dev2 branch
    * | 45458d8 switch 2 master
    |/
    * 10c4e87 branch dev
    * 7655a07 another delete.txt
    * 211d1ae delete delete.txt
    * 6e56cbf add delete.txt
    * b60ed5f add,then commit
    * d1eb111 delete chinese
    * 0b602fb add 2 lines
    * 90857d7 wrote a readme.md
git branch -d dev2 // 最后删除dev2分支
    Deleted branch dev2 (was fd26fe3).


首先git分支没有主次之分,master叫主分支其实和别的分支完全一样,其次冲突是由于两个分支merge引起的。冲突是因为两个分支A、B各自都有自己的commit,而且修改内容可能是同一文件的同一行,导致git合并时无法自动merge,就有了冲突。

分支管理策略


通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。
    即一旦删除分支后,我们既无法知道分支存在过,也无法区分哪些修改是在分支上提交的。
如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
首先看一下使用ff模式:
    git checkout -b dev2 // 分支dev2
    git add readme.md
    git commit -m "ff"
    git checkout master // 切回master
    git merge dev2 // ff模式合并
        Updating aea7a48..f2c207f
        Fast-forward
         readme.md | 1 +
         1 file changed, 1 insertion(+)
    git branch -d dev2 // 删除dev2分支,注:无论是否删除分支,都看不出来曾经做过合并
    git log --graph --pretty=oneline --abbrev-commit // 查看分支历史,看不出来曾经做过合并
        * f2c207f ff


下面看一下禁用Fast forward模式
    git checkout -b dev3 // 分支dev3
    git add readme.md
    git commit -m "no ff"
    git checkout master
    git merge --no-ff -m "merge with no-ff" dev3 // 禁用ff模式,因为会生成一个新的commit,所以-m指定了提交描述,另外可以看到合并使用的是'recursive'策略
        Merge made by the 'recursive' strategy.
        readme.md | 1 +
        1 file changed, 1 insertion(+)
    git log --graph --pretty=oneline --abbrev-commit // 查看分支历史,可以清楚的看到合并的记录
        *   5faa867 merge with no-ff
        |\
        | * 77d4303 no ff
        |/
    可以看到,不使用Fast forward模式,merge后就像这样,即master又commit了一次

image

分支策略

首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
所以,团队合作的分支看起来就像这样:

image



ff和no ff的结果都是一样的,都会合并,其最大区别在于,ff会"默默"地合并,其仅仅是将指针移动到分支的最新处(当然删除分支并不会对master产生任何影响),而no ff则是重新建立了一个commit,然后将合并当做一个comiit来处理,自然就产生了合并的信息.
所以,所谓的分支信息丢失,在这里是指:合并的时候,master的log里面没有任何的合并记录!
所以,可想而知,如果你的master很重要,某一次合并后出现问题,而你又是使用ff,这里没有任何记录可以让你回退版本的!(回退只有靠commit id).


合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。

Bug分支


在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,等等,当前正在dev上进行的工作还没有提交:
并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?
幸好,Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:


git checkout -b dev // 有一个dev分支,修改readme.md,此时发现了master有一个bug:
git stash // 存储当前工作现场,此时发现readme.md中之前在dev分支修改的内容不见了
    Saved working directory and index state WIP on dev: 0085d48 try ff,not delete branch
    HEAD is now at 0085d48 try ff,not delete branch
git status  // 隐藏当前工作区后,发现工作区"clean"
    On branch dev
    nothing to commit, working directory clean
git checkout master // 切换master并从master建立一个临时分支bug-001
git checkout -b bug-001
git add readme.md   // 在bug-001分支修复bug并提交
git commit -m "fix bug 001"
git checkout master
git merge --no-ff -m "merged bug fix 001" bug-001 // 合并回master
git branch -d bug-001 // 删除临时分支
git checkout dev // 切换会dev分支继续干活
git stash list // 查看之前保存的工作现场,发现现场还在
    stash@{0}: WIP on dev: 0085d48 try ff,not delete branch
git stash pop // 恢复现场并删除stash内容,此时发现之前dev的修改的内容已经恢复
    On branch dev
    Changes not staged for commit:
      (use "git add <file>" to update what will be committed)
      (use "git checkout -- <file>" to discard changes in working directory)
            modified:   readme.md
    no changes added to commit (use "git add" and/or "git commit -a")
    Dropped refs/stash@{0} (24ac8b196b4e9ff1191e7b1a9e5812e792e6b92e)
git stash list // 此时没有任何之前保存的现场
还有一种是用git stash apply恢复,但是恢复后,stash内容并不删除:
你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令"
    git stash apply stash@{0}

Feature分支


添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。
git checkout -b dev // 我们本身在dev分支
git checkout -b feature // 创建一个feature分支用来开发新功能,新建了一个feature.txt
git branch  // 查看分支,三个分支,当前在feature分支
      dev
    * feature
      master
git add feature.txt // 在feature分支提交
git commit -m "add feature.txt"
git checkout dev // 切回dev分支准备合并新功能
一切顺利的话,feature分支和bug分支是类似的,合并,然后删除。
但是,就在此时,接到上级命令,因经费不足,新功能必须取消!
虽然白干了,但是这个分支还是必须就地销毁:
git branch -d feature // 删除分支,提示feature分支还没有被合并
    error: The branch 'feature' is not fully merged.
    If you are sure you want to delete it, run 'git branch -D feature'.
git branch -D feature // 强制删除
    Deleted branch feature (was 48030ee).
git branch // 此时查看分支,只剩下dev和master了
    * dev
    master


开发一个新feature,最好新建一个分支;
如果要丢弃一个没有被合并过的分支,可以通过git branch -D <name>强行删除。


新分支是基于当前分支而创建的,如果是在dev下新建分支issue,那么issue就是dev下的分支,issue分支的所有文件都继承了dev分支的所有文件

多人协作


当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin。
git remote // 查看远程库信息
    origin
git remote -v // 查看更详细的远程库信息
    origin  git@github.com:mavsplus/gitTutorial.git (fetch)
    origin  git@github.com:mavsplus/gitTutorial.git (push)
上面显示了可以抓取和推送的origin的地址
现在本地的dev分支和master分支各自有不同的内容,比如dev分支下有一个dev.txt,而master分支下有一个master.txt
而:推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把[该分支推送到远程库对应的远程分支上]:
git push origin master // 推送本地master分支到远程库对应的master分支
    Warning: Permanently added the RSA host key for IP address '192.30.252.121' to the list of known hosts.
    Counting objects: 34, done.
    Delta compression using up to 8 threads.
    Compressing objects: 100% (34/34), done.
    Writing objects: 100% (34/34), 3.01 KiB | 0 bytes/s, done.
    Total 34 (delta 13), reused 0 (delta 0)
    To git@github.com:mavsplus/gitTutorial.git
       7655a07..1da3118  master -> master
git push origin dev // 推送本地dev分支到远程库对应的dev分支
    Warning: Permanently added the RSA host key for IP address '192.30.252.131' to the list of known hosts.
    Counting objects: 2, done.
    Delta compression using up to 8 threads.
    Compressing objects: 100% (2/2), done.
    Writing objects: 100% (2/2), 267 bytes | 0 bytes/s, done.
    Total 2 (delta 0), reused 0 (delta 0)
    To git@github.com:mavsplus/gitTutorial.git
     * [new branch]      dev -> dev

此时查看github,https://github.com/mavsplus/gitTutorial/branches/all,可以github的远程库有两个分支,master和dev,完美的和本地同步但是,并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?
    master分支是主分支,因此要时刻与远程同步;
    dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
    bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
    feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。
    总之,就是在Git中,分支完全可以在本地自己藏着玩,是否推送,视你的心情而定!


多人协作的工作模式通常是这样:
    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 branch-name origin/branch-name。
    这就是多人协作的工作模式,一旦熟悉了,就非常简单。

模拟一下多人协作的情形:
git clone git@github.com:mavsplus/gitTutorial.git // 另一个小伙伴从远程克隆到本地,注意也可以使用https,只不过较慢(https://github.com/mavsplus/gitTutorial.git)
    Cloning into 'gitTutorial'
    remote: Counting objects: 54, done.
    remote: Compressing objects: 100% (33/33), done.
    remote: Total 54 (delta 18), reused 51 (delta 15), pack-reused 0
    Receiving objects: 100% (54/54), 4.60 KiB | 0 bytes/s, done.
    Resolving deltas: 100% (18/18), done.
    Checking connectivity done.
gitTutorial>git branch // 从远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支
    * master
gitTutorial>git checkout -b dev origin/dev // 小伙伴要在dev分支上开发,就必须创建远程origin的dev分支到本地,此时本地可以看到dev分支的dev.txt了
    Branch dev set up to track remote branch dev from origin.
    Switched to a new branch 'dev'
gitTutorial>git add dev.txt // 本地修改dev.txt,默认文件为空,增加一行
gitTutorial>git commit -m "first modify dev.txt" // 本地提交
gitTutorial>git push origin dev // 推送至远程的dev分支,此时github的dev分支已同步
    Counting objects: 3, done.
    Delta compression using up to 8 threads.
    Compressing objects: 100% (2/2), done.
    Writing objects: 100% (3/3), 336 bytes | 0 bytes/s, done.
    Total 3 (delta 0), reused 0 (delta 0)
    To git@github.com:mavsplus/gitTutorial.git
       9c93773..34fe4de  dev -> dev
git_demo>git checkout dev // 而此时你也在dev下面进行了修改,注意是git_demo目录,和上面区分,表示是两个伙伴进行开发
git_demo>git add dev.txt // 修改并提交
git_demo>git commit -m "landon modify dev.txt"
git_demo>git push origin dev // 推送至远程,此时提示拒绝push,原因很简单:冲突了,因为你的小伙伴的最新提交和你试图推送的提交有冲突
    To git@github.com:mavsplus/gitTutorial.git
    ! [rejected]        dev -> dev (fetch first)
    error: failed to push some refs to 'git@github.com:mavsplus/gitTutorial.git'
    hint: Updates were rejected because the remote contains work that you do
    hint: not have locally. This is usually caused by another repository pushing
    hint: to the same ref. You may want to first integrate the remote changes
    hint: (e.g., 'git pull ') before pushing again.
    hint: See the 'Note about fast-forwards' in 'git push --help' for details.
解决办法也很简单,先用git pull(提示fetch first)把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送:
git_demo>git pull // 从远程拉取,提示no tracking information并给出了命令提示
    remote: Counting objects: 3, done.
    remote: Compressing objects: 100% (2/2), done.
    remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
    Unpacking objects: 100% (3/3), done.
    From github.com:mavsplus/gitTutorial
       9c93773..34fe4de  dev        -> origin/dev
    There is no tracking information for the current branch.
    Please specify which branch you want to merge with.
    See git-pull(1) for details.
    git pull <remote> <branch>
    If you wish to set tracking information for this branch you can do so with:
    git branch --set-upstream-to=origin/<branch> dev
git_demo>git branch --set-upstream-to=origin/dev dev // 指定本地dev分支与远程origin/dev分支的链接,注意教程中的git branch --set-upstream dev origin/dev已经被deprecated了.
    Branch dev set up to track remote branch dev from origin.
git_demo>git pull // 再次拉取,提示自动合并冲突(dev.txt显示黄色三角叹号_TortoiseGit)
    Auto-merging dev.txt
    CONFLICT (content): Merge conflict in dev.txt
    Automatic merge failed; fix conflicts and then commit the result.
下面是冲突的文件
    <<<<<<< HEAD
    i'm the landon's modify
    =======
    i'm first modify the dev.txt
    >>>>>>> 34fe4deebc74c4a8a780c3dd0f61c45976a25786
需要手动解决冲突,然后提交,再push:
git_demo>git add dev.txt
git_demo>git commit -m "fix merge conflict"
git_demo>git push origin dev
    Counting objects: 6, done.
    Delta compression using up to 8 threads.
    Compressing objects: 100% (5/5), done.
    Writing objects: 100% (6/6), 676 bytes | 0 bytes/s, done.
    Total 6 (delta 0), reused 0 (delta 0)
    To git@github.com:mavsplus/gitTutorial.git
       34fe4de..ef938cd  dev -> dev
顺利push到远程,此时查看远程github的dev分支的dev.txt,已经是完美同步


总结:
1.查看远程库信息,使用git remote -v;
2.本地新建的分支如果不推送到远程,对其他人就是不可见的;
3.从本地推送分支,使用git push origin branch-name,如果推送失败,先用git pull抓取远程的新提交;
4.在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致;
5.建立本地分支和远程分支的关联,使用git branch --set-upstream-to=origin/<branch> branch-name;// 教程中的该命令已经被废弃了
6.从远程抓取分支,使用git pull,如果有冲突,要先处理冲突。

标签管理


发布一个版本时,我们通常先在版本库中打一个标签,这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。
Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像对不对?但是分支可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的。

创建标签


git branch // 查看分支并切换至master
git checkout master
git tag v1.0.0 // 打一个新标签
git tag // 查看所有标签,默认标签是打在最新提交的commit上的
    v1.0.0
git log --pretty=oneline --abbrev-commit // 查看提交记录
    1da3118 add master.txt
    4447507 conflict fixed
    b434e98 dev develop
    caf4705 merged bug fix 001
    884d29c fix bug 001
    0085d48 try ff,not delete branch
    5faa867 merge with no-ff
    77d4303 no ff
    f2c207f ff
    aea7a48 conflict fixed
    45458d8 switch 2 master
    fd26fe3 dev2 branch
    10c4e87 branch dev
    7655a07 another delete.txt
    211d1ae delete delete.txt
    6e56cbf add delete.txt
    b60ed5f add,then commit
    d1eb111 delete chinese
    0b602fb add 2 lines
    90857d7 wrote a readme.md
git tag v0.9.1 10c4e87 // 可以给某个commit id打tag
git show v0.9.1 // 查看某个指定的tag
    commit 10c4e875cffda57cd4eff41782c23dfb59897f60
    Author: mavsplus <mavsplus@aliyun.com>
    Date:   Thu Jun 16 10:04:29 2016 +0800
        branch dev
    diff --git a/readme.md b/readme.md
    index 68f2f90..b65fc2f 100644
    --- a/readme.md
    +++ b/readme.md
    @@ -2,3 +2,4 @@
     * git status
     * not add,commit directly
    +* create a new dev branch
git tag -a v0.9.2 -m "version 0.9.2 tag" 884d29c // 创建带有说明的tag
git show v0.9.2 // 查看v0.9.2的tag
    tag v0.9.2
    Tagger: mavsplus <mavsplus@aliyun.com>
    Date:   Sat Jun 18 14:46:39 2016 +0800
    version 0.9.2 tag
    commit 884d29c5fbbf720f07fe842f3bf2d2173d3787e9
    Author: mavsplus <mavsplus@aliyun.com>
    Date:   Thu Jun 16 15:20:33 2016 +0800
        fix bug 001
    diff --git a/readme.md b/readme.md
    index 048f963..ece4787 100644
    --- a/readme.md
    +++ b/readme.md
    @@ -7,4 +7,4 @@
     * swith to master branch
     * Fast Forward
     * no Fast Forward
    -* Try Fast Forward,no delete branch
    \ No newline at end of file
git tag -s v0.9.3 -m "signed version 0.9.3 released" b434e98 // 过-s用私钥签名一个tag,这里提示错误
    gpg: directory `/c/Users/pc/.gnupg' created
    gpg: new configuration file `/c/Users/pc/.gnupg/gpg.conf' created
    gpg: WARNING: options in `/c/Users/pc/.gnupg/gpg.conf' are not yet active during this run
    gpg: keyring `/c/Users/pc/.gnupg/secring.gpg' created
    gpg: keyring `/c/Users/pc/.gnupg/pubring.gpg' created
    gpg: skipped "mavsplus <mavsplus@aliyun.com>": secret key not available
    gpg: signing failed: secret key not available
    error: gpg failed to sign the data
    error: unable to sign the tag
// 注:需要安装gnupg,https://www.gnupg.org/ (The GNU Privacy Guard),并gpg --gen-key
// tag待gpg签名原因:保证项目作者(私钥持有者)所制定的里程碑别人将无法修改。那么,就可以说,作者的代码是安全传播的。

操作标签


git tag // 本地master有三个tag
    v0.9.1
    v0.9.2
    v1.0.0
git tag -d v0.9.2 // 删除某个tag
git push origin v0.9.1 // 将tag推送到远程
    Warning: Permanently added the RSA host key for IP address '192.30.252.130' to the lis
    Total 0 (delta 0), reused 0 (delta 0)
    To git@github.com:mavsplus/gitTutorial.git
     * [new tag]         v0.9.1 -> v0.9.1
此时我们在github项目的release中可以看到版本v0.9.1,https://github.com/mavsplus/gitTutorial/releases,并且可以查看该版本相关信息.
git push origin --tags // 一次性推送全部尚未推送到远程的本地标签
    Total 0 (delta 0), reused 0 (delta 0)
    To git@github.com:mavsplus/gitTutorial.git
     * [new tag]         v1.0.0 -> v1.0.0
此时可以看到github可以看到1.0.0和0.9.1两个版本,https://github.com/mavsplus/gitTutorial/releases
如何删除远程标签?
git tag -d v0.9.1 // 先本地删除
    Deleted tag 'v0.9.1' (was 10c4e87)
git push origin :refs/tags/v0.9.1 // 从远程删除,注意origin后面的空格
    To git@github.com:mavsplus/gitTutorial.git
    - [deleted]         v0.9.1
此时去github项目的release页面,发现只剩下v1.0.0版本了


小结
1.命令git push origin <tagname>可以推送一个本地标签;
2.命令git push origin --tags可以推送全部未推送过的本地标签;
3.命令git tag -d <tagname>可以删除一个本地标签;
4.命令git push origin :refs/tags/<tagname>可以删除一个远程标签。

使用GitHub


如何参与一个开源项目呢?
如,https://github.com/netty/netty,点击左上角的Fork即可在自己的账号下克隆了一个netty仓库,注:[fork-分叉,用餐的叉子,两个方向,一模一样,可以理解为副本],如 https://github.com/mavsplus/netty
从自己的账号下clone:
git clone git@github.com:mavsplus/netty.git
    Cloning into 'netty'
    remote: Counting objects: 203726, done.
    remote: Total 203726 (delta 0), reused 0 (delta 0), pack-reused 203726
    Receiving objects: 100% (203726/203726), 40.11 MiB | 785.00 KiB/s, done.
    Resolving deltas: 100% (90325/90325), done.
    Checking connectivity done.
一定要从自己的账号下clone仓库,这样你才能推送修改。从原作者的仓库克隆,没有权限,不能推送修改.
你想修复netty的一个bug,或者新增一个功能,立刻就可以开始干活,干完后,往自己的仓库推送。
如果你希望netty的官方库能接受你的修改,你就可以在GitHub上发起一个[pull request]。当然,对方是否接受你的pull request就不一定了。


1.在GitHub上,可以任意Fork开源仓库;
3.自己拥有Fork后的仓库的读写权限;
3.可以推送pull request给官方仓库来贡献代码。

自定义git


因为我们安装的windows版本的Git-2.8.4-64-bit,所以目前cmd下使用命令行,目前会显示配色,还是比较方便。
git config --global color.ui true // 显示颜色,这个命令先记下来

忽略特殊文件

这个和svn的ignore类似
在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。
不需要从头写.gitignore文件,GitHub已经为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore
如:https://github.com/github/gitignore/blob/master/Java.gitignore,直接参考该文件内容,编写一个.gitignore文件(cmd>type nul > .gitignore生成一个.文件,直接无法新建),放到工作区根目录,该文件格式如下
    *.class
    # Mobile Tools for Java (J2ME)
    .mtj.tmp/
    # Package Files #
    *.jar
    *.war
    *.ear
    # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
    hs_err_pid*
检验.gitignore的标准是git status命令是不是说working directory clean
    编辑一个Hello.java,命令行用javac编译,生成Hello.class
    此时发现Hello.class加了一个白色横线(TortoiseGit),说明.gitigore起了作用
git add Hello.class // 如果强迫add,则提示该文件被忽略,当然可以使用-f参数强制添加
    The following paths are ignored by one of your .gitignore files:
    Hello.class
    Use -f if you really want to add them.
git check-ignore -v Hello.class //  你发现,可能是.gitignore写得有问题,需要找出来到底哪个规则写错了,可以用check-ginore命令检查
    .gitignore:1:*.class    Hello.class
        可以看到提示.gitignore的第一行规则忽略了该文件,那么我们就可以知道应该修订哪个规则
1.忽略某些文件时,需要编写.gitignore;
2..gitignore文件本身要放到版本库里,并且可以对.gitignore做版本管理

配置别名


svn经常中有一些别名,如checkout (co),commit (ci)等(可以通过svn --help查看)
git config --global alias.co checkout // 定义checkout的别名为co
git co dev  // 使用别名的checkout
    Switched to branch 'dev'
    Your branch is up-to-date with 'origin/dev'.
--global参数是全局参数,也就是这些命令在这台电脑的所有Git仓库下都有用
配置Git的时候,加上--global是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。
每个仓库的Git配置文件都放在.git/config文件中,配置别名也可以修改这个文件:[alias] aliasName = originName
    [core]
        repositoryformatversion = 0
        filemode = false
        bare = false
        logallrefupdates = true
        symlinks = false
        ignorecase = true
    [remote "origin"]
         url = git@github.com:mavsplus/gitTutorial.git
         fetch = +refs/heads/*:refs/remotes/origin/*
    [branch "dev"]
          remote = origin
          merge = refs/heads/dev
当前用户的Git配置文件放在用户主目录下的一个隐藏文件.gitconfig中:
    [user]
            name = mavsplus
            email = mavsplus@aliyun.com
    [core]
            autocrlf = true
    [alias]
            co = checkout
可以看到别名一行,配置别名也可以修改这个文件


git config --get-regexp alias // 查看别名列表
git config --global --unset alias.co // 取消别名

搭建Git服务器


主要是windows搭建(因为linux太简单了)
    主要是git和ssh即可,git已经安装,这里git要用ssh协议。ssh软件用Copssh即可,主要搭建过程略.(说实话,之所以略过,1.因为实际环境操作中,用windows作为git服务器的太少,而且即使如svn,windows上面有快速搭建git服务器的一系列工具如visualsvn,后续肯定会有类似工具,一键搭建工具 2.实际环境中,我肯定是用linux搭建git服务器的 3.而对于客户端开发而言,因为相对来说大部分都是windows进行开发,所以之前的例子全部是在dos下进行git命令的讲解)
    ps:如What is Gitblit? Gitblit is an open-source, pure Java stack for managing, viewing, and serving Git repositories。


要方便管理公钥,用Gitosis;
要像SVN那样变态地控制权限,用Gitolite。

使用linux搭建Git服务器

本次主要使用vmware-centos 6.5-Final作为目标linux。
安装git,使用yum:
1.[root@localhost ~]# yum -y install git // yum安装git
    Loaded plugins: fastestmirror, refresh-packagekit, security
    Loading mirror speeds from cached hostfile
     * base: mirror.bit.edu.cn
     * extras: mirror.bit.edu.cn
     * updates: mirrors.sina.cn
    Setting up Install Process
    Resolving Dependencies
    --> Running transaction check
    ---> Package git.x86_64 0:1.7.1-4.el6_7.1 will be installed
    --> Processing Dependency: perl-Git = 1.7.1-4.el6_7.1 for package: git-1.7.1-4.el6_7.1.x86_64
    --> Processing Dependency: perl(Git) for package: git-1.7.1-4.el6_7.1.x86_64
    --> Processing Dependency: perl(Error) for package: git-1.7.1-4.el6_7.1.x86_64
    --> Running transaction check
    ---> Package perl-Error.noarch 1:0.17015-4.el6 will be installed
    ---> Package perl-Git.noarch 0:1.7.1-4.el6_7.1 will be installed
    
2.[root@localhost ~]# git // 可以看到git已成功安装
    usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
               [-p|--paginate|--no-pager] [--no-replace-objects]
               [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
               [--help] COMMAND [ARGS]
    The most commonly used git commands are:
       add        Add file contents to the index
       bisect     Find by binary search the change that introduced a bug
       branch     List, create, or delete branches
       checkout   Checkout a branch or paths to the working tree
       clone      Clone a repository into a new directory
       commit     Record changes to the repository
       diff       Show changes between commits, commit and working tree, etc
       fetch      Download objects and refs from another repository
       grep       Print lines matching a pattern
       init       Create an empty git repository or reinitialize an existing one
       log        Show commit logs
       merge      Join two or more development histories together
       mv         Move or rename a file, a directory, or a symlink
       pull       Fetch from and merge with another repository or a local branch
       push       Update remote refs along with associated objects
       rebase     Forward-port local commits to the updated upstream head
       reset      Reset current HEAD to the specified state
       rm         Remove files from the working tree and from the index
       show       Show various types of objects
       status     Show the working tree status
       tag        Create, list, delete or verify a tag object signed with GPG
    See 'git help COMMAND' for more information on a specific command.
3.[root@localhost ~]# cat /etc/ssh/sshd_config  // 查看ssh#RSA配置信息,并去掉注释
    #RSAAuthentication yes
    #PubkeyAuthentication yes
    #AuthorizedKeysFile     .ssh/authorized_keys
  可以看到公钥存储在.ssh/authorized_keys中
4.[root@localhost ~]# adduser git // 这里注意和useradd的区别
  [root@localhost ~]# cd /home/git/
  [root@localhost ~]# mkdir .ssh
  [root@localhost ~]# chown -R git:git .ssh/
  [root@localhost ~]# cat id_rsa.pub >> authorized_keys
    添加一个git用户,在/home/git下建一个.ssh目录,在ssh目录下创建authorized_keys,然后将之前的ssh-keygen生成的SSH Key导入该文件中
5.[root@localhost git]# git init --bare cavs.git // 初始化一个空库(为什么用cavs,因为北京时间2016年6月20日,克里夫兰骑士队夺得NBA总冠军)
    Initialized empty Git repository in /git/cavs.git/
6.[root@localhost git]# chown -R git:git cavs.git // change owner
7.[root@localhost git]# vim /etc/passwd // 禁用git用户的shell登陆
    修改为类似的某行为:
    git:x:501:501::/home/git:/usr/bin/git-shell
    git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出。
8.>git clone git@192.168.168.129:/git/cavs.git // 客户端顺利克隆(windows)
        Cloning into 'cavs'
        warning: You appear to have cloned an empty repository.
        Checking connectivity done.
注意:
    a) 需要用adduser命令,如果用useradd需要手动设置一些权限
    b) .ssh目录owner为git


landon总结


  • 实际项目过程中用TortoiseGit和Git的IDE插件较多(和SVN一样)
  • Git命令行多用于脚本工具等
  • 虽然开发过程用GUI的GIT工具较多,但是Git的底层实现、原理以及鼓励的开发模式还是非常值得学习的
References

posted on 2016-06-20 18:10 landon 阅读(1465) 评论(0)  编辑  收藏 所属分类: Utils

只有注册用户登录后才能发表评论。


网站导航: