Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
227 lines (162 sloc) 9.18 KB
layout title author categories
post
Git Intro
JamesW
Tool

转载须注明出处:www.wufei.org

  • content {:toc}

Version Control System (VCS) 已经有很长一段历史了, 之前一直都是群雄逐鹿. Git的出现改变了这种状况, 虽然还有些公司仍然在用其他VCS工具, 比如Perforce, 不过完全是不得已而为之. 老的项目可能因为这种那种原因不得不继续使用以前的工具, 对于新的项目, 绝大多数人都会认真考虑Git, 甚至写博客都开始使用Github Pages了.

Basics

如果只是使用Github Pages来写博客的话, 确实只要知道几个简单命令就可以开始了, 但是想要发挥Git更好的作用, 了解add/commit几个命令是远远不够的.

Objects

  • Blob - 文件的内容. 比如一个.c文件, 里面的代码就是一个Blob. 就像文件系统里, 文件需要inode来索引, Blob同样需要解决引用的问题, Git会把每个Blob都计算 出一个SHA-1值, 这个SHA-1就可以认为是该Blob的inode. 由于SHA-1的冲突概率 非常之小, 以至于可以忽略不计, 所以可以认为SHA-1唯一地对应于一个Blob. 这个概念同样在Dedupe系统中使用, 只是用处有所不同.

      $ git cat-file -p aac8b4d0018c75cb461d0abaee66c48242f231c6
      content in foo
    
  • Tree - 文件的目录结构. 跟文件系统一样, Git里面的文件是通过Tree组织在一起的. Tree里面可以包含文件和子目录. Tree也有对应的SHA-1值. (正常的文件系统是否 能够借鉴这种方法?)

      $ git cat-file -p master^{tree} 
      100644 blob aac8b4d0018c75cb461d0abaee66c48242f231c6    foo
      040000 tree e1565d7eb2da807b3b27ef38e76549765dd24f15    subdir
    
  • Commit - 对应每一次修改. Commit可以类比于文件系统的Snapshot, 每一个Commit 都会生成一个Snapshot. Snapshot可以由一颗指向Root的Tree来表示. 当然Commit 除了指向Tree外, 还包含一些额外的信息比如Author/Committer/Timestamp/Message 等等, 当然如果不是第一个Commit的话, 还有Parent指针, 通过这个指针可以遍历 Git的历史记录, 如果是Merge的话, 一个Commit会有2个甚至更多的Parents. 顺便说一下, Snapshot能极大地提升访问速度, 这意味着任意版本 的文件都是静态的完整的, 而不需要通过Patch去合成. 虽然这看起来会额外地占用 一些磁盘空间, 但是Git也提供了Dedupe和压缩的功能. 这同样使用了跟存储领域相似 的技术, 也就是在Dedupe/Snapshot的时候, 让以前的Snapshot指向最新的Snapshot, 这样有利于新Commit的访问速度.

      $ git cat-file -p HEAD
      tree 1984939ca6a5cfd4114ecbb32a904de46c5bbbed
      parent 65f0a280addfc4e9b1e29a8df458dce7872083e2
      author Wu Fei <at.wufei@qq.com> 1456323698 +0800
      committer Wu Fei <at.wufei@qq.com> 1456323698 +0800
    
      add 2nd
    

有了这3种Objects, Git就可以轻易地表示各个版本.

States

要想正确使用Git, 理解这些概念是必须的.

  • Working Directory - 工作目录, 也就是我们正常看到的, Checkout出来用于编辑的目录. 只在这里修改过的文件的状态就是Modified.
  • Staging Area - 也叫Index, 中间状态的一个目录, 表示下一次Commit的改变. 通过add等命令stage了的文件会放在这里.
  • Git Directory - Git在本地的仓库, 所有已经Commit的内容都放在这里.

Drawing

Operations

Basic

掌握了Git的基本概念之后, 就很容易掌握Git的基本操作.

首先需要有一个Git本地的仓库, 这可以通过初始化一个本地仓库

$ git init

或者Clone一个远程的仓库, 比如Clone我的Github Pages:

$ git clone ssh://git@github.com/atwufei/atwufei.github.io

Git支持很多协议, 这里使用ssh是为了免去每次Commit时输入用户名密码, 当然这需要 先把公钥放到Github服务器上.

有了本地仓库后, 就可以Edit/Commit你的修改了.

$ vim something
$ git add something
$ git commit -m "Add something"

这样就创建了本地的一个Commit, 但是这个Commit还没有同步到Remote Repository上. 如果在Remote上有写权限, 可以通过Push把本地的修改Checkin到Remote上.

$ git push

当然Remote可能会拒绝这个Push操作, 因为已经有别人先Push过了, 这时候master(假设 在这个Branch上)和remote/master不一致. Git Server并不会主动帮你去Merge, Server 只会接受Fast-forward的Merge, 这个时候就需要

$ git pull

这个操作会更新remote/master, 同时会对把它Merge进本地master. 如果Merge没有冲突, 那么这个时候就可以再次Push. 只要这个时间窗口没有人再Push了, 这个时候就能成功了, 因为Local的Branch HEAD是Remote Branch的子孙, 这样就可以形成一个Fast-forward Merge了.

Branch

不到长城非好汉, 没用过Branch也就谈不上熟悉Git. Branch在Git是非常轻量级, 它就是 一个可移动的指针, 指向某一个Commit. 当有新Commit的时候, Branch就移动到这个Commit上.

在本地创建一个Branch, 或者同时创建和Checkout:

$ git branch
$ git checkout -b

也可以查看Local或者Remote的Branches:

$ git branch -a
* editing
  master
  remotes/origin/HEAD -> origin/master
  remotes/origin/editing
  remotes/origin/master

这是我Github Pages的Branches, Pages会把master Branch的内容处理然后当成博客呈现出来. 因为我不想show一篇没完的博客, 我会先把未完成的文章都Commit到editing Branch, 然后push editing到Pages上去. 这样别人就不会看到临时未完成的博客, 而同时又能够把它存到Pages上去.

$ vim _posts
$ git add _posts
$ git commit -m "Add new one"
$ git push origin editing

当博客写完了, 我可以先把editing Merge到master, 然后在把master Push到Remote Server上, 这样就把博客发布了.

Merge/Rebase

一般来说, 把一个Branch的Commits搬到另一个Branch上主要有2种方法: Merge和Rebase. 比如我们想把topic的内容合并到当前的Branch master上

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

如果使用Merge的方法, 我们会得到这样的结果

$ git merge topic
                 A---B---C topic
                /         \
           D---E---F---G---H master

如果使用的是Rebase的方法

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

可以看到Rebase后的History更加干净, 变成线性了, 也没有额外的Merge Commit. 这两种方法没有好坏之分, 全看个人喜好. 需要注意的是, Rebase是修改了历史的, 如果之前的历史已经被其他人/Commit引用了, 那会带来不必要的麻烦. 对于Merge, 因为不存在修改Commit, 也就没有这个问题.

由于Rebase能够修改记录, 所以还能实现很多其他的功能.

* --onto 选项
* -i 选项

当然并不是只有Rebase才能修改历史, 比如git commit --amend就能修改最近的一个Commit.

Reset

刚接触Git Reset可能觉得不是很容易理解, 其实很简单:

1. MOVE HEAD, --soft
2. UPDATING THE INDEX, --mixed
3. UPDATING THE WORKING DIRECTORY, --hard

当然如果后面指定了具体的路径, 显然Step 1已经不可能执行了, 这时只会有后面2步.

Revision

Answer in stackoverflow

Manpage

----------------------------------------------------------------------
|    Commit-ish/Tree-ish    |                Examples
----------------------------------------------------------------------
|  1. <sha1>                | dae86e1950b1277e545cee180551750029cfe735
|  2. <describeOutput>      | v1.7.4.2-679-g3bee7fb
|  3. <refname>             | master, heads/master, refs/heads/master
|  4. <refname>@{<date>}    | master@{yesterday}, HEAD@{5 minutes ago}
|  5. <refname>@{<n>}       | master@{1}
|  6. @{<n>}                | @{1}
|  7. @{-<n>}               | @{-1}
|  8. <refname>@{upstream}  | master@{upstream}, @{u}
|  9. <rev>^                | HEAD^, v1.5.1^0
| 10. <rev>~<n>             | master~3
| 11. <rev>^{<type>}        | v0.99.8^{commit}
| 12. <rev>^{}              | v0.99.8^{}
| 13. <rev>^{/<text>}       | HEAD^{/fix nasty bug}
| 14. :/<text>              | :/fix nasty bug
----------------------------------------------------------------------
|       Tree-ish only       |                Examples
----------------------------------------------------------------------
| 15. <rev>:<path>          | HEAD:README.txt, master:sub-directory/
----------------------------------------------------------------------
|         Tree-ish?         |                Examples
----------------------------------------------------------------------
| 16. :<n>:<path>           | :0:README, :README
----------------------------------------------------------------------

End

Git还有很多有用的功能, 需要的时候可以很容易找到相应的文档, 这里主要介绍一下 基本概念, 有了这些知识, 就可以很容易理解其他的文档.