原文英文地址:http://maryrosecook.com/blog/post/git-in-six-hundred-words
Git源码地址:https://github.com/maryrosecook/gitlet
Git使用参考文档:http://git-scm.com/docs
设想你现在位于 alpha/ 目录下,这里有一个文本文件 number.txt,里面的内容只有一个词:“first”。
现在执行 git init 将这个 alpha 文件夹初始化为 Git 仓库。
执行 git add number.txt 会将 number.txt 添加到 Git 的索引(index)中。这个索引记录了所有 Git 保持追踪的文件,现在它有了一个映射记录 number.txt -> first,同时 add 命令还会把一个包含了 first 字符串的二进制对象加入 Git 的对象数据库里。
现在执行 git commit -m first。这条命令会做三件事情。首先在对象数据库内创建一个树对象,用以记录 alpha 目录下的文件列表,这个对象有一个指针指向前面 git add 命令创建的 first 二进制对象;第二,这条命令还会创建一个 commit 对象用以代表刚刚提交的版本,它包含一个指针指向刚刚的树对象;第三,master 分支也会指向这个新创建的 commit 对象。
现在执行 git clone . ../beta。它会创建一个新目录 beta 并将其初始化为 Git 仓库,然后把 alpha 仓库的对象数据库中所有对象拷贝给 beta 的对象数据库,将 beta 的 master 分支像 alpha 的 master 一样指向相应的对象。它还根据 first提交的内容配置索引,并根据索引更新目录下的文件——也就是 number.txt。
现在切换到 beta 目录,修改 number.txt 的内容为“second”,执行 git add number.txt 和 git commit -m second,新创建的提交对象 second(译注:姑且称之为 second)会有一个指向父提交(first)的指针,表示 second 继承自 first,而 master 分支则指向 second 提交。
回到 alpha 目录,执行 git remote add beta ../beta,将 beta 仓库设为远程仓库。然后执行 git pull beta master。
在这条命令背后,它其实会执行 git fetch beta master,从 beta 仓库中找到 second 提交的相关对象拷贝到 alpha 仓库;把 alpha 中关于 beta 的 master 分支记录指向这个 second 提交;更新 FETCH_HEAD 指向刚刚从 beta 仓库拉取的 master 分支,还是这个 second 提交。
此外,pull 命令还会执行 git merge FETCH_HEAD。从 FETCH_HEAD 得知最近拉取的分支是 beta 仓库的 master 分支,据此拿到相应的对象,也就是 second 提交对象。此时 alpha 的 master 分支指着 first 提交,正好是 second 的祖先提交,于是对于 merge 命令来说只需要将 master 分支指向 second 提交即可。接下来 merge 命令还会更新索引以匹配 second 提交的内容,并且相应更新工作目录中的文件。
现在执行 git branch red,创建一个名为“red”、指向 second 提交的新分支。
然后执行 git checkout red。在 checkout 之前,HEAD 指向 master 分支,执行命令之后它就指向了 red 分支,使得 red 成为当前分支。
接下来把 number.txt 的内容修改为 “third”,执行 git add numbers.txt 和 run git commit -m third。
之后再执行 git push beta red,这条命令会把 alpha 仓库内跟 third 提交相关的对象拷贝至 beta 仓库,并且将(alpha 仓库内记录的)beta 仓库 red 分支指向 third 提交。
英文原文:
(This essay is a companion piece to Gitlet, my implementation of Git in JavaScript.)
Imagine you have a directory called alpha. It contains a file called number.txt that contains the text first.
You run git init to set up alpha as a Git repository.
You run git add number.txt to add number.txt to the index. The index is a list of all the files that Git is keeping track of. It maps filenames to the content of the files. It now has the mapping number.txt -> first. Running the add command has also added a blob object containing first to the Git object database.
You run git commit -m first. This does three things. First, it creates a tree object in the objects database. This object represents the list of items in the top level of the alpha directory. This object has a pointer to the first blob object that was created when you ran git add. Second, it creates a commit object that represents the version of the repository that you have just committed. This includes a pointer to the tree object. Third, it points the master branch at the new commit object.
You run git clone . ../beta. This creates a new directory called beta. It initializes it as a Git repository. It copies the objects in the alpha objects database to the beta objects database. It points the master branch on beta at the commit object that the master branch points at on the alpha repository. It sets the index on beta to mirror the content of the first commit. It updates your files - number.txt - to mirror the index.
You move to the beta repository. You change the content of number.txt to second. You run git add number.txt and git commit -m second. The commit object that is created has a pointer to its parent, the first commit. The commit command points the master branch at the second commit.
You move back to the alpha repository. You run git remote add beta ../beta. This sets the beta repository as a remote repository.
You run git pull beta master.
Under the covers, this runs git fetch beta master. This finds the objects for the second commit and copies them from the beta repository to the alpha repository. It points alpha’s record of beta’s master at the second commit object. It updates FETCH_HEAD to show that the master branch was fetched from the beta repository.
Under the covers, the pull command runs git merge FETCH_HEAD. This reads FETCH_HEAD, which shows that the master branch on the beta repository was the most recently fetched branch. It gets the commit object that alpha’s record of beta’s master is pointing at. This is the second commit. The master branch on alpha is pointing at the first commit, which is the ancestor of the second commit. This means that, to complete the merge, the merge command can just point the master branch at the second commit. The merge command updates the index to mirror the contents of the second commit. It updates the working copy to mirror the index.
You run git branch red. This creates a branch called red that points at the second commit object.
You run git checkout red. Before the checkout, HEAD pointed at the master branch. It now points at the red branch. This makes the red branch the current branch.
You set the content of number.txt to third, run git add numbers.txt and run git commit -m third.
You run git push beta red. This finds the objects for the third commit and copies them from the alpha repository to the beta repository. It points the red branch on the beta repository at the third commit object, and that’s it.
(If you would like to learn about the internals of Git in detail, you can read my essay, Git from the inside out.)