阅读 678

git大礼包:学完出师

说明

基于 demo 讲解 git 的使用方法,从头到尾敲一遍,保证可以应对日常工作中 git 的使用。

设置

设置姓名和 Email

    git config --global user.name 'Your Name'
    git config --global user.email 'your_email@whatever.com'
复制代码

创建项目

使用 git init 初始化当前 git 仓库

    //1 创建项目文件夹
    mkdir hiGit
    cd hiGit
    
    //2 创建 git 仓库
    git init
    
    //输出
    Initialized empty Git repository in/Users/cuixiaorui/code/cgit/hiGit/.git/
    
    //3 添加程序到仓库
    //新建 index.js 脚本文件
    vim index.js
    git add index.js
    git commit -m 'First Commit'
复制代码

检查状态

使用 git status 命令检查当前仓库的状态

    $ git status
    
    //可以看到
    On branch master
    nothing to commit, working tree clean
复制代码

做更改

    //修改脚本
    echo 'console.log("git")' >> index.js
    
    //检查状态
    git status
    
    //可以看到
    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:   index.js

    no changes added to commit (use "git add" and/or "git commit -a")
复制代码

Git 知道 index.js 文件已被修改,但 Git 还没有通知这些更改。

  • 添加更改到仓库 -- git add
  • 放弃当前更改 -- git checkout

暂存更改

使用 git add [filename] 来暂存更改

  • git add . 暂存当前所有修改的文件
    //添加更改
    git add index.js
    git status
    
    //可以看到
    On branch master
    Changes to be committed:
      (use "git reset HEAD <file>..." to unstage)
    
    	modified:   index.js
复制代码

对 index.js 文件的更改已被暂存,但还没有永久记录到仓库中。

  • 不提交更改 --git reset

提交

使用 git commit -m 'content' 来提交修改

  • -m 为添加注释
    //提交
    //-m 为注释
    git commit -m 'change index.js'
    
    //可以看到
    [master 0a820ee] change index.js
    1 file changed, 1 insertion(+)
复制代码

分开提交

假设编辑了三个文件(a.js b.js 和 c.js)。现在想提交所有的更改,想要 a.js 和 b.js 中的更改作为单个提交,而 c.js 的更改与前两个文件在逻辑上不相关,所以应该分开提交。

    //第一次提交 a 和 b
    git add a.js
    git add b.js
    git commit -m 'change for a and b'
    
    //第二次提交 c
    git add c.js
    git commit -m 'change to c'
复制代码

通过分开暂存和提交,能够更加容易的调优每一个提交。

更改而非文件

Git 聚焦于文件的更改而非文件本身。当 Git 添加文件时,并非告诉 Git 要添加文件到仓库。而是说 Git 应当对文件的当前状态做记录以便稍后提交。

    //第一次更改
    echo "console.log('add-1');" >> index.js 
    
    //并且暂存
    git add index.js
    
    //第二次更改
    echo "console.log('add-2');" >> index.js 
    
    //查看当前的状态
    git status
    
    //可以看到
    //状态被列了两次。第一次更改已被暂存,且准备提交。第二次更改还未暂存。如果你现在提交,那么第二次修改不会到仓库中。
    On branch master
    Changes to be committed:
      (use "git reset HEAD <file>..." to unstage)
    
    	modified:   index.js
    
    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:   index.js
    	
    //提交第一次暂存的更改
    git commit -m 'change index add-1'
    
    //再次查看当前的状态
    git status
    
    //可以看到
    //第一次的修改被提交了,只剩下第二次的修改
    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:   index.js
    
    no changes added to commit (use "git add" and/or "git commit -a")
   
    //暂存当前目录下的所有修改的文件
    git add .
    
    //提交第二次更改
    git commit -m 'change index add-2'
复制代码

历史

使用 git log 查看更改清单,参阅 man git-log 了解更多细节

    //查看提交历史
    git log
    
    //可以看到
    commit eb590d7e75294c467918ec8081532a0ba718f851 (HEAD -> master)
    Author: 崔效瑞 <cuixiaorui@100tal.com>
    Date:   Tue Aug 21 15:07:54 2018 +0800
    
        add-2
    
    commit c067396ee1bff99e6e48586ea3d9b19a288c4cfb
    Author: 崔效瑞 <cuixiaorui@100tal.com>
    Date:   Tue Aug 21 14:56:07 2018 +0800
    
        change index add-1
    
    commit 0a820ee15229817cc11deb7e22e80dae99b28708
    Author: 崔效瑞 <cuixiaorui@100tal.com>
    Date:   Tue Aug 21 14:35:44 2018 +0800
    
        change index.js
    
    commit 50518d33b40030d0a454df7974151bfad9cdab46
    Author: 崔效瑞 <cuixiaorui@100tal.com>
    Date:   Tue Aug 21 14:20:42 2018 +0800
    
        first commit
复制代码

单行历史

控制处理 log 命令要精确显示的内容。

例如 单行格式:

    //通过 pretty=oneline 设置显示格式为单行
    git log --pretty=oneline
    
    //可以看到
    eb590d7e75294c467918ec8081532a0ba718f851 (HEAD -> master) add-2
    c067396ee1bff99e6e48586ea3d9b19a288c4cfb change index add-1
    0a820ee15229817cc11deb7e22e80dae99b28708 change index.js
    50518d33b40030d0a454df7974151bfad9cdab46 first commit
复制代码

终极日志格式

    git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
    
    //可以看到
    * eb590d7 2018-08-21 | add-2 (HEAD -> master) [崔效瑞]
    * c067396 2018-08-21 | change index add-1 [崔效瑞]
    * 0a820ee 2018-08-21 | change index.js [崔效瑞]
    * 50518d3 2018-08-21 | first commit [崔效瑞]
复制代码
  • --pretty="..." 定义输出的格式
  • %h 是提交 hash 的缩写
  • %d 是提交的装饰(如分支头或标签)
  • %ad 是创作日期
  • %s 是注释
  • %an 是作者姓名
  • --graph 使用 ASCII 图形布局显示提交树
  • --date=short 保留日期格式更好且更短

别名

git status、git add、git commit、git checkout 是非常常用的命令,因此对它们进行缩写十分有用。

添加下列内容到 $HOME 目录的 .gitconfig 文件中:

    //  ~/.gitconfig
    [alias]
      co = checkout
      ci = commit
      st = status
      br = branch
      hist = log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
      type = cat-file -t
      dump = cat-file -p
复制代码

使用新命令:

    //git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short 的简写
    git hist
    
    //可以看到
    * eb590d7 2018-08-21 | add-2 (HEAD -> master) [崔效瑞]
    * c067396 2018-08-21 | change index add-1 [崔效瑞]
    * 0a820ee 2018-08-21 | change index.js [崔效瑞]
    * 50518d3 2018-08-21 | first commit [崔效瑞]
复制代码

获得旧版本

使用 git checkout 命令,将从仓库复制任意快照到工作目录。

    //获取先前版本的哈希
    git hist
    
    //可以看到
    * eb590d7 2018-08-21 | add-2 (HEAD -> master) [崔效瑞]
    * c067396 2018-08-21 | change index add-1 [崔效瑞]
    * 0a820ee 2018-08-21 | change index.js [崔效瑞]
    * 50518d3 2018-08-21 | first commit [崔效瑞]
    
    //使用 git co 这是 git checkout 的简写
    //回到哈希为 50518d3 版本的快照
    git co 50518d3
    
    //可以看到
    ote: checking out '50518d3'.

    You are in 'detached HEAD' state. You can look around, make experimental
    changes and commit them, and you can discard any commits you make in this
    state without impacting any branches by performing another checkout.
    
    If you want to create a new branch to retain commits you create, you may
    do so (now or later) by using -b with the checkout command again. Example:
    
      git checkout -b <new-branch-name>
    
    HEAD is now at 50518d3 first commit

    //回到最新版本
    // master 是默认分支的名称。通过名称检出分支,能够回到该分支的最新版本
    git co master
    
    //可以看到
    Previous HEAD position was 50518d3 first commit
    Switched to branch 'master'
复制代码

给版本打标签

使用 git tag [tagName] 来给当前版本打 tab

标签就是一个别名,可以通过这个标记检出,而不需要使用 hash (不方便记忆)

    //当前版本为 v1
    git tag v1
    
    //查看提交历史
    git hist
    
    //可以看到
    //最新版本的提交 eb590d7 后面有个 tag 标志:v1 证明标签已经打成功了。
    * eb590d7 2018-08-21 | add-2 (HEAD, tag: v1, master) [崔效瑞]
    * c067396 2018-08-21 | change index add-1 [崔效瑞]
    * 0a820ee 2018-08-21 | change index.js [崔效瑞]
    * 50518d3 2018-08-21 | first commit [崔效瑞]
    
    //给 c067396 tag为 v1-beta
    //c067396 为 eb590d7 的上一个提交
    //可以使用 v1^ 代表 (v1 为 eb590d7。 ^ 为当前版本的上一个提交 所以就是 c067396)
    //co 为 git checkout 的简写
    //先检出到 c067396 版本
    git co v1^
    git tag v1-beta
    
    //查看提交历史
    git hist
    
    //可以看到
    // c067396 多了 tag 标签:v1-beta 证明标签已经打成功。
    * c067396 2018-08-21 | change index add-1 (HEAD, tag: v1-beta) [崔效瑞]
    * 0a820ee 2018-08-21 | change index.js [崔效瑞]
    * 50518d3 2018-08-21 | first commit [崔效瑞]
    
    //查看 master 分支的所有提交历史
    git hist master
    
    //可以看到
    // eb590d7 为最新版本 tag 为 v1
    // c067396 为最新版本的上一个提交 tag 为 v1-beta
    // HEAD 代表当前检出的提交
    * eb590d7 2018-08-21 | add-2 (tag: v1, master) [崔效瑞]
    * c067396 2018-08-21 | change index add-1 (HEAD, tag: v1-beta) [崔效瑞]
    * 0a820ee 2018-08-21 | change index.js [崔效瑞]
    * 50518d3 2018-08-21 | first commit [崔效瑞]
复制代码

撤销提交的更改

使用 git revert [版本号] 撤销已经提交的操作。

    //提交了一个错误编写
    echo ';' >> index.js
    git add .
    git commit -m '误操作'
    
    //意识到提交有问题 需要撤销
    //HEAD 为最新的提交 也就是刚刚的提交。HEAD 可以替换为 hash 或者 tag
    //--no-edit 不打开编辑器
    git revert HEAD --no-edit
    
    //查看提交历史
    git hist
    
    //可以看到
    //提交历史中记录了我们提交和 revert 的操作
    * 931ba54 2018-08-21 | Revert "误操作" (HEAD -> master) [崔效瑞]
    * 9ecb67c 2018-08-21 | 误操作 [崔效瑞]
    * eb590d7 2018-08-21 | add-2 (tag: v1) [崔效瑞]
    * c067396 2018-08-21 | change index add-1 (tag: v1-beta) [崔效瑞]
    * 0a820ee 2018-08-21 | change index.js [崔效瑞]
    * 50518d3 2018-08-21 | first commit [崔效瑞]
复制代码

从分支移除提交

使用 git reset [版本号(如哈希、分支或标签吗)] 将:

  • 重写当前分支到指向的特定提交
  • 重置暂存区到匹配特定的提交(可选)
  • 重置工作目录到匹配特定的提交(可选)

重置的危险:

在本地分支上重置一般是安全的。任何“事故”通常都能通过重置到想要的提 交来恢复。

然而,如果分支在共享的远程仓库上,那么重置可能使其他用户共享的分支 混乱。

    //查看提交历史
    git hist
    
    //可以看到
    * 931ba54 2018-08-21 | Revert "误操作" [崔效瑞]
    * 9ecb67c 2018-08-21 | 误操作 [崔效瑞]
    * eb590d7 2018-08-21 | add-2 (HEAD -> master, tag: v1) [崔效瑞]
    * c067396 2018-08-21 | change index add-1 (tag: v1-beta) [崔效瑞]
    * 0a820ee 2018-08-21 | change index.js [崔效瑞]
    * 50518d3 2018-08-21 | first commit [崔效瑞]
    
    //给当前版本打个 tag 以后好找
    git tag shiwu
    
    //使用 reset 回到 tag:v1 的快照
    git reset v1
    
    //再次查看提交历史
    git hist
    
    //可以看到
    //之前的 revert 记录已经消失了。
    * eb590d7 2018-08-21 | add-2 (HEAD -> master, tag: v1) [崔效瑞]
    * c067396 2018-08-21 | change index add-1 (tag: v1-beta) [崔效瑞]
    * 0a820ee 2018-08-21 | change index.js [崔效瑞]
    * 50518d3 2018-08-21 | first commit [崔效瑞]
    
    //查看所有的提交记录
    git hist --all
    
    //可以看到
    //但是没有真的消失
    * 931ba54 2018-08-21 | Revert "误操作" (tag: shiwu) [崔效瑞]
    * 9ecb67c 2018-08-21 | 误操作 [崔效瑞]
    * eb590d7 2018-08-21 | add-2 (HEAD -> master, tag: v1) [崔效瑞]
    * c067396 2018-08-21 | change index add-1 (tag: v1-beta) [崔效瑞]
    * 0a820ee 2018-08-21 | change index.js [崔效瑞]
    * 50518d3 2018-08-21 | first commit [崔效瑞]
    
    //还是存在的
    //因为打了 tag
    //提交依然在仓库中,它们只是不再列到 master 分支中。
    //未引用的提交保留在仓库中,一直到系统运行垃圾回收软件时。
复制代码

删除标签

使用 git tag -d [tagName] 删除标签

    //查看当前所有的tag
    git tag
    
    //可以看到
    v1
    v1-beta
    shiwu
    
    //删除 tag shiwu
    git tag -d shiwu
    
    //可以看到
    Deleted tag 'shiwu' (was 931ba54)
    
    //再次查看 tag
    git tag
    
    //可以看到
    v1
    v1-beta
复制代码

修正提交

使用 git commit --amend -m 'content' 修正上次的提交

    //修改 index.js
    vim index.js
    
    //增加以下代码
    function add(a,b){
        return a+b;
    }
    
    var result = add(10,20);
    
    //提交
    git add .
    git ci -m '增加 add 函数'
    git hist
    
    //可以看到
    * 8aec665 2018-08-21 | 增加 add 函数 (HEAD -> master) [崔效瑞]
    * eb590d7 2018-08-21 | add-2 (tag: v1) [崔效瑞]
    
    //当提交后,发现代码并没有打印 result 的值
    //但是并不想在多增加一个 commit 记录
   
    //修改之前的提交
    echo "console.log(result);" >> index.js
    
    //添加修改
    git add .
    
    //修正上次的提交
    git ci --amend -m '增加 add 函数,并打印 10 和 20 的结果'
    
    //查看提交历史
    git hist
    
    //可以看到
    //806fb92 的注释变更了。并且没有增加新的快照。
    * 806fb92 2018-08-21 | 增加 add 函数,并打印 10 和 20 的结果 (HEAD -> master) [崔效瑞]
    * eb590d7 2018-08-21 | add-2 (tag: v1) [崔效瑞]
复制代码

移动文件

使用 git mv [source] [destination] 移动或者更改名称

    //1. 使用 git mv 
    
        //创建 lib 文件夹
        //把 index.js 移动到 lib 内
        mkdir lib
        git mv index.js lib
        
        //查看状态
        git st
        
        //可以看到
        On branch master
        Changes to be committed:
          (use "git reset HEAD <file>..." to unstage)
        
        	renamed:    index.js -> lib/index.js
    	
    	
    //2. 使用 mv
    
        mv index.js lib/index.js
        
        //查看状态
        git st
        
        //可以看到
        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:    index.js
        
        Untracked files:
          (use "git add <file>..." to include in what will be committed)
        
        	lib/
        
        no changes added to commit (use "git add" and/or "git commit -a")
    
    //可以看到用系统的 mv 来移动文件的话,git 并不知道
    //需要 git add . & git commit -m 'change location of index.js ' 
    //虽然操作有点多,但是结果相同
复制代码

创建分支

使用 git checkout -b [branchName] 创建分支,并切换。

git checkout -b 为 git branch branchName 和 git checkout branchName 的简写。

    //创建分支
    git co -b 'greet'
    
    //可以看到
    //git 提示你在 greet 分支
    Switched to a new branch 'greet'
    
    //新建 lib/greeter.js
    cd lib
    vim greeter.js
    
    //新增内容
    module.exports = function(){
        console.log('my name is greet!');
    }
    
    //提交
    git add .
    git ci -m 'add greeter.js'
    
    //修改 lib/index.js
    vim index.js
    const greeter = require('./greeter.js');
    console.log(greeter());
    
    //提交
    git add .
    git ci -m 'call greeter in index.js'
    
    //查看提交历史
    git hist
    
    //可以看到
    //HEAD 变更为 greet 
    //并且在 greet 分支下有两个提交记录
    * 57fa783 2018-08-21 | call greeter in index.js (HEAD -> greet) [崔效瑞]
    * 7b0e4c1 2018-08-21 | add greeter.js [崔效瑞]
    * ca9f8fd 2018-08-21 | move index.js to lib (master) [崔效瑞]
    * 806fb92 2018-08-21 | 增加 add 函数,并打印 10 和 20 的结果 [崔效瑞]
复制代码

切换分支

使用 git checkout [branchName] 切换分支

    //切换分支到 master 
    git checkout master
    
    //可以看到
    Switched to branch 'master'
    
    //查看提交记录
    git hist
    
    //可以看到
    * ca9f8fd 2018-08-21 | move index.js to lib (HEAD -> master) [崔效瑞]
    * 806fb92 2018-08-21 | 增加 add 函数,并打印 10 和 20 的结果 [崔效瑞]
    //没有在 greet 分支上的提交记录,只有 master 分支上的提交记录
    
    //回到 greet 分支
    git checkout greet
复制代码

查看分叉的分支

使用 git hist --all 查看分叉的分支

  • --all 选项可以查看所有分支(默认只显示当前分支)
    //回到 master 分支
    git co master

    //创建 REDEME.md
    vim REDEME.md
    ## 这是一个学习 git 的教程

    //提交
    git add .
    git ci -m 'add REDEME'
    
    //查看提交历史
    git hist
    
    //可以看到
    //刚刚的提交记录 add REDEME
    * 1e69b46 2018-08-22 | add REDEME (HEAD -> master) [崔效瑞]
    * ca9f8fd 2018-08-21 | move index.js to lib [崔效瑞]
    
    //使用 git hist --all 查看提交历史
    //可以看到两个分支(greet 和 master),并且 master 分支是当前的 HEAD
    //两个分支的共同祖先是 ca9f8fd 'move index.js to lib'
    //现在看到的是 git log --graph 选项的效果。
    //graph 能够使用简单的 ASCII 字符来绘制提交树
    * 1e69b46 2018-08-22 | add REDEME (HEAD -> master) [崔效瑞]
    | * 57fa783 2018-08-21 | call greeter in index.js (greet) [崔效瑞]
    | * 7b0e4c1 2018-08-21 | add greeter.js [崔效瑞]
    |/  
    * ca9f8fd 2018-08-21 | move index.js to lib [崔效瑞]
    * 806fb92 2018-08-21 | 增加 add 函数,并打印 10 和 20 的结果 [崔效瑞]
复制代码

合并

合并将两个分支中的更改结合在一起。

使用 git merge [branchName] 合并分支

    //回到 greet 分支
    git co greet
    
    //合并 master 分支
    git merge master
    
    //可以看到
    //git 提示没有提交这个合并,需要使用 git commit
    Not committing merge; use 'git commit' to complete the merge.
    
    //提交合并
    git ci -m 'merge master'
    
    //可以看到
    *   9ff3d7f 2018-08-22 | merge master (HEAD -> greet) [崔效瑞]
    |\  
    | * 1e69b46 2018-08-22 | add REDEME (master) [崔效瑞]
    * | 57fa783 2018-08-21 | call greeter in index.js [崔效瑞]
    * | 7b0e4c1 2018-08-21 | add greeter.js [崔效瑞]
    |/  
    * ca9f8fd 2018-08-21 | move index.js to lib [崔效瑞]
    * 806fb92 2018-08-21 | 增加 add 函数,并打印 10 和 20 的结果 [崔效瑞]
复制代码

通过周期性的合并 master 到 greet 分支,可以选择 master 的任意更改,并保持 greet 中的更改与主干中的更改兼容。

冲突

创建冲突

    //回到 greet 分支
    git co greet 
    
    //修改 REDEME.md
    echo '#### 第一步' >> REDEME.md
    
    //提交 REDEME.md
    git add .
    git ci -m 'change REDEME'
    
    //切换分支到 master
    git co master
    
    //修改 REDEME.md
    echo '## 这是一个学习 git 教程。' > REDEME.md
    
    //提交 REDEME.md
    git add .
    git ci -m 'change REDEME'
    
    //切换分支到 greet
    git co greet
    
    //合并 master 分支
    git master master
    
    //可以看到
    //git 提示合并失败
    Auto-merging REDEME.md
    CONFLICT (content): Merge conflict in REDEME.md
    Automatic merge failed; fix conflicts and then commit the result.
    
    //查看 REDEME.md
    cat REDEME.md
    
    //可以看到
    //<<<<< HEAD 到 ===== 中间区域是:当前分支的内容
    //===== 到 master 中间区域是:master 分支的内容 
    <<<<<<< HEAD
    ## 这是一个学习 git 的教程
    #### 第一步
    =======
    ## 这是一个学习 git 教程。
    >>>>>>> master
    
    //至此一个冲突就已经出现了
复制代码

解决冲突

    //出现冲突后我们先查看 git 当前的状态
    git st
    
    //可以看到
    //REDEME 文件有改动,但是没有暂存
    On branch greet
    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:   REDEME.md
    
    no changes added to commit (use "git add" and/or "git commit -a")
    
    //首先需要修改 REDEME.md
    //实际工作中这里需要分析是保留当前分支的内容还是主分支的内容
    //这里我们保留 master 分支的内容
    vim REDEME.md
    
    //修改为
    ## 这是一个学习 git 的教程
    
    //修改完成后提交 REDEME.md 
    git add .
    git ci -m 'Resolve the conflict'
    
    //接着查看提交记录
    git hist --all 
    
    //可以看到
    //当前版本为 greet 
    //是在 fa38887 合并而来
    *   efc98d8 2018-08-22 | Resolve the conflict (HEAD -> greet) [崔效瑞]
    |\  
    | * fa38887 2018-08-22 | change REDEME (master) [崔效瑞]
    * | 4f1f24f 2018-08-22 | change REDEME [崔效瑞]
    * |   9ff3d7f 2018-08-22 | merge master [崔效瑞]
    |\ \  
    | |/  
    | * 1e69b46 2018-08-22 | add REDEME [崔效瑞]
    * | 57fa783 2018-08-21 | call greeter in index.js [崔效瑞]
    * | 7b0e4c1 2018-08-21 | add greeter.js [崔效瑞]
    |/  
    * ca9f8fd 2018-08-21 | move index.js to lib [崔效瑞]
    * 806fb92 2018-08-21 | 增加 add 函数,并打印 10 和 20 的结果 [崔效瑞]
复制代码

变基

在 Git 中整合来自不同分支的修改主要有两种方法:merge 以及 rebase。 在本节中我们将学习什么是“变基”,怎样使用“变基”,并将展示该操作的惊艳之处,以及指出在何种情况下你应避免使用它。

    //首先需要先回到没有合并的状态下
    //切换到 greet 分支
    git co greet
    
    //查看提交树
    git hist 
    
    //可以看到
     *   efc98d8 2018-08-22 | Resolve the conflict (HEAD -> greet) [崔效瑞]
    |\  
    | * fa38887 2018-08-22 | change REDEME (master) [崔效瑞]
    * | 4f1f24f 2018-08-22 | change REDEME [崔效瑞]
    * |   9ff3d7f 2018-08-22 | merge master [崔效瑞]
    |\ \  
    | |/  
    | * 1e69b46 2018-08-22 | add REDEME [崔效瑞]
    * | 57fa783 2018-08-21 | call greeter in index.js [崔效瑞]
    * | 7b0e4c1 2018-08-21 | add greeter.js [崔效瑞]
    |/  
    * ca9f8fd 2018-08-21 | move index.js to lib [崔效瑞]
    * 806fb92 2018-08-21 | 增加 add 函数,并打印 10 和 20 的结果 [崔效瑞]
    
    //我们需要回到 merge 之前的版本
    git co 57fa783
    
    //然后使用 rebase 来合并
    git rebase master
    
    //查看提交树
    git hist
    
    //可以看到
    * 471ced2 2018-08-21 | call greeter in index.js (HEAD -> greet) [崔效瑞]
    * 26d356d 2018-08-21 | add greeter.js [崔效瑞]
    * 1e69b46 2018-08-22 | add REDEME (master) [崔效瑞]
    * ca9f8fd 2018-08-21 | move index.js to lib [崔效瑞]
    * 806fb92 2018-08-21 | 增加 add 函数,并打印 10 和 20 的结果 [崔效瑞]
    
    //最后把 greet 中的更改合并到 master
    git co master
    git merge greet
    
    //可以看到
    //因为 master 的头是 greet 分支头的直接祖先
    //所以 Git 可以做快进合并
    //当快进时,分支指针简单地前进到与 greet 分支相同的提交处
    //在快进合并中从来不会冲突
    Updating 1e69b46..471ced2
    Fast-forward
     lib/greeter.js |  6 ++++++
     lib/index.js   | 11 ++---------
     2 files changed, 8 insertions(+), 9 deletions(-)
     create mode 100644 lib/greeter.js
复制代码

合并VS变基

变基的最终结果与合并很相似。greet 分支现在包含它的全部 更改以及来自 master 分支中的所有更改。然而,提交树却十分不同。greet 分支的提交树已被重写,以致 master 分支成 为了其提交历史的一部分。这使提交链更加线性,且更易阅读。

何时变基,何时合并?

Git 是一个非常强大的工具,它允许你对提交历史做许多事情,但每个团队、每个项目对此的需求并不相同。 既然你已经分别学习了两者的用法,相信你能够根据实际情况作出明智的选择。

总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。

简单的说

不要使用变基……

如果是公开且与其他人共享的分支,那么重写公开的共享分支 将会搞砸团队中的其他会员。

要是提交分支的精确历史重要(因为变基将重写提交历史)。

根据上述准则,我会针对短期生命的本地分支使用变基,而对 公开仓库的分支使用合并。

远程仓库

到此为止我们处理的都是单一 Git 仓库。其实 Git 也擅长处理多个仓库。这些扩展的仓库也许存储在本地,也许通过网络连接访问。

接下来我们创建名为 'clone_hiGit' 的新仓库,展示如何将更改从一个仓库迁移到另一个仓库。(无论仓库是存储在本地还是通过网络远程访问都受用)

克隆远程仓库

使用 git clone 来克隆一个仓库

    //我们需要基于 hiGit 来克隆一个仓库
    
    //首先确认当前的位置
    pwd
    
    //可以看到当前所在的位置
    /Users/cuixiaorui/code/cgit/hiGit
    
    //回到 /Users/cuixiaorui/code/cgit/
    //克隆 hiGit 
    //取名为 clone_hiGit
    cd ..
    git clone hiGit clone_hiGit
    
    //进入 clone_hiGit 目录
    cd clone_hiGit
    
    //查看当前的文件
    ls
    
    //可以看到
    REDEME.md lib
    
    //查看提交树
    git hist
    
    //可以看到新仓库的全部提交列表
    //它和原始仓库的提交历史,仅在分支名称上有差异
    * 471ced2 2018-08-21 | call greeter in index.js (HEAD -> master, origin/master, origin/greet, origin/HEAD) [崔效瑞]
    * 26d356d 2018-08-21 | add greeter.js [崔效瑞]
    * 1e69b46 2018-08-22 | add REDEME [崔效瑞]
    * ca9f8fd 2018-08-21 | move index.js to lib [崔效瑞]
    * 806fb92 2018-08-21 | 增加 add 函数,并打印 10 和 20 的结果 [崔效瑞]
    * eb590d7 2018-08-21 | add-2 (tag: v1) [崔效瑞]
    * c067396 2018-08-21 | change index add-1 (tag: v1-beta) [崔效瑞]
    * 0a820ee 2018-08-21 | change index.js [崔效瑞]
    * 50518d3 2018-08-21 | first commit [崔效瑞]
    
    //至此 clone_hiGit 和 hiGit 的目录结构一样,克隆完成
复制代码

查看克隆仓库信息

使用 git remote 可以查看远程

使用 git remote show origin 查看 origin 仓库的详细信息

    //查看当前的远程仓库
    git remote
    
    //可以看到
    origin 
    
    //查看 origin 仓库的详细信息
    git remote show origin 
    
    //可以看到
    //origin 就是原始仓库 hiGit
    //origin 没什么特别的,只是对主中央仓库命名的习惯约定而已
    * remote origin
      Fetch URL: /Users/cuixiaorui/code/cgit/hiGit
      Push  URL: /Users/cuixiaorui/code/cgit/hiGit
      HEAD branch: master
      Remote branches:
        greet  tracked
        master tracked
      Local branch configured for 'git pull':
        master merges with remote master
      Local ref configured for 'git push':
        master pushes to master (up to date)
复制代码

远程分支

使用 git branch 查看可用的分支

  • -a 显示全部分支
    //查看分支
    git branch
    
    //可以看到
    * master
    
    //只有一个 master 分支,greet 分支去哪里了
    //其实 git branch 默认只会列出本地分支
    
    //列出远程分支
    git branch -a
    
    //可以看到
    * master
      remotes/origin/HEAD -> origin/master
      remotes/origin/greet
      remotes/origin/master
复制代码

Git 具有原始仓库的全部提交,但在远程仓库中的分支不会用为本地分支。如果我们想要 greet 分支,需要自行创建它

获取远程仓库的更改

使用 git fetch 或者 git pull 获取远程仓库的更改

  • git fetch 只会拉取,不会合并
  • git pull 是拉取后自动合并。等于 git fetch & git merge origin/master
    //回到 hiGit 目录
    cd ../hiGit
    
    //修改 REDEME
    echo '#### 第一步' >> REDEME.md
    
    //提交
    git add .
    git ci -m 'add content in REDEME'
    
    //回到 clone_hiGit 目录
    cd ../clone_hiGit
    
    //拉取远程仓库的
    git fetch
    
    //可以看到
    emote: Enumerating objects: 5, done.
    remote: Counting objects: 100% (5/5), done.
    remote: Compressing objects: 100% (3/3), done.
    remote: Total 3 (delta 0), reused 0 (delta 0)
    Unpacking objects: 100% (3/3), done.
    From /Users/cuixiaorui/code/cgit/hiGit
       471ced2..d327276  master     -> origin/master
       
    //查看提交树
    git hist --all
    
    //可以看到
    //最新的提交已经是 hiGit 的提交记录了
    //注意 HEAD 指向的是 471ced2 不是当前最新的
    * d327276 2018-08-22 | add content in REDEME (origin/master, origin/HEAD) [崔效瑞]
    * 471ced2 2018-08-21 | call greeter in index.js (HEAD -> master, origin/greet) [崔效瑞]
    * 26d356d 2018-08-21 | add greeter.js [崔效瑞]
    * 1e69b46 2018-08-22 | add REDEME [崔效瑞]
    * ca9f8fd 2018-08-21 | move index.js to lib [崔效瑞]
    * 806fb92 2018-08-21 | 增加 add 函数,并打印 10 和 20 的结果 [崔效瑞]
    * eb590d7 2018-08-21 | add-2 (tag: v1) [崔效瑞]
    * c067396 2018-08-21 | change index add-1 (tag: v1-beta) [崔效瑞]
    * 0a820ee 2018-08-21 | change index.js [崔效瑞]
    * 50518d3 2018-08-21 | first commit [崔效瑞]

    //说明 fetch 没有把 origin 的代码合并到本地
    //验证一下
    cat REDEME.md
    
    //可以看到
    //REDEME 还是本地的 不是 origin 更新的内容
    ## 这是一个学习 git 的教程
    
    //接下来需要合并 origin 的内容
    git merge origin/master
    
    //可以看到
    Updating 471ced2..d327276
    Fast-forward
     REDEME.md | 1 +
     1 file changed, 1 insertion(+)
     
    //验证一下
    cat REDEME.md
    
    //可以看到
    ## 这是一个学习 git 的教程
    #### 第一步
    
    //查看下提交树
    git hist
    
    //可以看到
    //HEAD 已经指向了最新的一次提交
    * d327276 2018-08-22 | add content in REDEME (HEAD -> master, origin/master, origin/HEAD) [崔效瑞]
    * 471ced2 2018-08-21 | call greeter in index.js (origin/greet) [崔效瑞]
复制代码

添加跟踪的分支

使用 git branch --track [本地分支] [远程分支] 来添加远程分支到本地

    //添加远程分支
    git branch --track greet origin/greet
    
    //可以看到
    Branch 'greet' set up to track remote branch 'greet' from 'origin'.
    
    //查看当前的分支
    git branch 
    
    //可以看到
    //greet 已经在本地分支列表内了
      greet
    * master
    
    //查看提交树
    git hist
    
    //可以看到
    //现在已经可以在提交树种看到 greet 分支了
    * d327276 2018-08-22 | add content in REDEME (HEAD -> master, origin/master, origin/HEAD) [崔效瑞]
    * 471ced2 2018-08-21 | call greeter in index.js (origin/greet, greet) [崔效瑞]
    * 26d356d 2018-08-21 | add greeter.js [崔效瑞]
复制代码