0%

Git Basic Principle

Git Pinciple


Git使用命令有很多,参数也很多,如何把它迅速掌握?就应该从原理开始。从建立git跟踪文件到最后多次提交,内部文件数据结构是怎样的呢?结合Git Simple Tutorial的基本概念,本节将一步一步揭开它的不为人知的秘密。一般初学者掌握这个以后,使用命令就不会那么迷茫了。


Stage1 - init

1
$ git init canai

stage1_git_init
stage1_git_init_objects

  • 发生了什么呢?

    • 创建了很多空目录,比如.git/objects/和.git/refs/
    • 还没有索引(Index)文件
    • 创建了符号索引文件HEAD

      1
      2
      $ cat HEAD
      ref: refs/heads/master

Stage2 - add

1
$ git add README

stage2_git_add
stage2_git_add2
stage2_git_add_objects

  • 发生了什么呢?

    • 创建了索引(Index)文件,产生一个SHA1哈希值指向一个blob实体

      1
      2
      3
      4
      5
      6
      # 索引文件以二进制格式保存
      $ cat .git/index
      DIRCZM??$???ZM??$???q?????_???????e?8??V?README2?r??;V7?E???uA?ۣ
      # 只能这种方式打开看到里面的内容
      $ git ls-files --stage
      100644 5f89c6f016cad2d419e865df380595e39b1256db 0 README
    • 创建了一个blob实体。README文件的内容存储在该blob中

      1
      2
      3
      4
      5
      6
      # blob文件以二进制格式保存
      $ cat .git/objects/5f89c6f016cad2d419e865df380595e39b1256db
      xK??OR02bpT(?/?THN?K?T((??JM.???? ?
      # 只能这种方式打开看到里面的内容
      $ git cat-file blob 5f89c6
      A roti canai project.

Stage3 - commit

1
2
3
4
$ git commit -m 'first commit'
[master (root-commit) 06fae2f] first commit
1 file changed, 1 insertion(+)
create mode 100644 README

stage3_git_commit
stage3_git_commit_objects

  • 发生了什么呢?

    • 创建了分支‘master’引用,指向‘master’分支中最新的commit实体

      1
      2
      $ cat .git/refs/heads/master
      06fae2f981d13ea39253dce05ca2bb440009e74e
    • 创建了第一个commit实体(含有一个引用指向代码仓库根目录tree实体)

      1
      2
      3
      4
      5
      6
      $ git cat-file commit 06fae2f
      tree 0ff699bbafc5d17d0637bf058c924ab405b5dcfe
      author Li Li <allen.lili@hotmail.com> 1515037476 +1100
      committer Li Li <allen.lili@hotmail.com> 1515037476 +1100

      first commit
    • 创建了tree实体(含有一个引用指向该tree代表目录“canai”)

      1
      2
      $ git ls-tree 0ff699b
      100644 blob 5f89c6f016cad2d419e865df380595e39b1256db README

Stage4 add modified files

1
2
$ echo "Welcome everyone." >> README
$ git add README

stage4_git_add_motified
stage4_git_add_motified_objects

  • 发生了什么呢?

    • 更新了索引(Index)文件。注意到了吗?它记录了一个新blob

      1
      2
      $ git ls-files --stage
      100644 1192db4c15e019da7fc053225d09dea14bc3ac07 0 README
    • 创建了一个新的blob实体(即是README)

      1
      2
      3
      $ git cat-file blob 1192db4
      A roti canai project.
      Welcome everyone.

Stage5 add modified files into a new directory

1
2
3
$ mkdir doc
$ echo "[[TBD]] manual toc" > doc/manual.txt
$ git add doc

stage5_git_add_motified
stage5_git_add_motified_objects

  • 发生了什么呢?

    • 更新了索引(Index)文件。注意到了吗?它记录了一个新blob,–stage暂存区有两个文件,包括前一步还没有commit的blob

      1
      2
      3
      $ git ls-files --stage
      100644 1192db4c15e019da7fc053225d09dea14bc3ac07 0 README
      100644 ea283e4fb22719fad512405d41dffa050cd16f9a 0 doc/manual.txt
    • 创建了一个新的blob实体(即是manual.txt)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      		$ git cat-file blob ea283
      [[TBD]] manual toc

      ```
      -------------------------

      # Stage6 - **commit** modified files
      ```bash
      $ git commit -m'second commit'
      [master 5fd2aa8] second commit
      2 files changed, 2 insertions(+)
      create mode 100644 doc/manual.txt

stage6_git_commit_modified
stage6_git_commit_objects

  • 发生了什么呢?

    • 更新了分支“master”引用,指向该分支中最新的commit实体。

      1
      2
      $ cat .git/refs/heads/master
      5fd2aa86e614e93d3beffb2b53ea43f2ebbf2c8f
    • 创建了第二个commit实体。注意它的“parent”是指向首个commit实体。这样形成了一个提交图谱。

      1
      2
      3
      4
      5
      6
      7
      $ git cat-file commit 5fd2
      tree 7729a8b15b747bce541a9752a8f10d57daf221b6
      parent 06fae2f981d13ea39253dce05ca2bb440009e74e
      author Li Li <allen.lili@hotmail.com> 1515043504 +1100
      committer Li Li <allen.lili@hotmail.com> 1515043504 +1100

      second commit
    • 创建了一个新的代码仓库根目录tree实体。

      1
      2
      3
      $ git ls-tree 7729
      100644 blob 1192db4c15e019da7fc053225d09dea14bc3ac07 README
      040000 tree 6ff17d485bf857514f299f0bde0e2a5c932bd055 doc
    • 创建了一个新的子目录tree实体。

      1
      2
      3
      4
      5
      $ git ls-tree 6ff1
      100644 blob ea283e4fb22719fad512405d41dffa050cd16f9a manual.txt
      $ git cat-file blob 1192
      A roti canai project.
      Welcome everyone.

Stage7 添加一个注释标签(annotated tag)

1
$ git tag -a -m'this is annotated tag' v0.1 06fae

stage7_git_tag1
stage7_git_tag2
stage7_git_tag_objects

  • 发生了什么呢?

    • 创建了一个标签引用,指向一个tag实体。

      1
      2
      $ cat .git/refs/tags/v0.1
      aad34fe53bcbf0904ce9a1b1e4b1512d2556963e
    • 创建了一个tag实体。

      1
      2
      3
      4
      5
      6
      7
      $ git cat-file tag aad34
      object 06fae2f981d13ea39253dce05ca2bb440009e74e
      type commit
      tag v0.1
      tagger Li Li <allen.lili@hotmail.com> 1515047016 +1100

      this is annotated tag

Stage8 添加一个新的(轻量的)标签

1
$ git tag root-commit 06fae2f

stage8_git_root-commit
stage8_git_root-commit_objects

  • 发生了什么呢?
    • 创建了一个标签引用,指向一个commit实体
      1
      2
      $ cat .git/refs/tags/root-commit 
      06fae2f981d13ea39253dce05ca2bb440009e74e

结论

init, add, commit简单情况下的记忆线索:
HEAD -> master -> commit -> tree or blob
根目录 -> 引用目录 -> 实体目录
根目录包括HEAD等
引用目录包括master、tag等
实体目录包括commit、blob、tree、tag


Reference

Learning Git Internals by Example
炼数成金深入浅出Git