From 89e2e2267152919e27b201896f0403690aa405d1 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Fri, 26 Nov 2010 16:32:32 +0100 Subject: [PATCH] Reorganize the tutorial. This kills some of the duplication and fixes the testsuite to test all chapters. It also adds a note in the conclusion indicating that the tutorial only covers a part of Dulwich' functionality. --- .bzrignore | 1 + AUTHORS | 2 + NEWS | 5 + docs/tutorial/0-introduction.txt | 19 +-- docs/tutorial/1-repo.txt | 28 +++++ docs/tutorial/2-change-file.txt | 88 -------------- ...-initial-commit.txt => 2-object-store.txt} | 109 +++++++++++++----- docs/tutorial/3-add-file.txt | 41 ------- docs/tutorial/3-conclusion.txt | 11 ++ docs/tutorial/4-remove-file.txt | 30 ----- docs/tutorial/5-rename-file.txt | 33 ------ docs/tutorial/6-conclusion.txt | 11 -- docs/tutorial/index.txt | 9 +- dulwich/tests/__init__.py | 5 +- 14 files changed, 143 insertions(+), 249 deletions(-) create mode 100644 docs/tutorial/1-repo.txt delete mode 100644 docs/tutorial/2-change-file.txt rename docs/tutorial/{1-initial-commit.txt => 2-object-store.txt} (53%) delete mode 100644 docs/tutorial/3-add-file.txt create mode 100644 docs/tutorial/3-conclusion.txt delete mode 100644 docs/tutorial/4-remove-file.txt delete mode 100644 docs/tutorial/5-rename-file.txt delete mode 100644 docs/tutorial/6-conclusion.txt diff --git a/.bzrignore b/.bzrignore index 8aca99f45..b6b09f6c6 100644 --- a/.bzrignore +++ b/.bzrignore @@ -5,3 +5,4 @@ dist apidocs *,cover .testrepository +docs/tutorial/index.html diff --git a/AUTHORS b/AUTHORS index 3c76848c0..affdeb616 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,4 +3,6 @@ James Westby John Carr Dave Borowitz +Hervé Cauwelier wrote the original tutorial. + See the revision history for a full list of contributors. diff --git a/NEWS b/NEWS index 9d2c15502..88622b3c1 100644 --- a/NEWS +++ b/NEWS @@ -16,8 +16,13 @@ * Support parsing git mbox patches without a version tail, as generated by Mercurial. (Jelmer Vernooij) + DOCUMENTATION + * Run the tutorial inside the test suite. (Jelmer Vernooij) + * Reorganized the tutorial. (Jelmer Vernooij) + + 0.6.2 2010-10-16 BUG FIXES diff --git a/docs/tutorial/0-introduction.txt b/docs/tutorial/0-introduction.txt index b60903a1e..84ff50bfd 100644 --- a/docs/tutorial/0-introduction.txt +++ b/docs/tutorial/0-introduction.txt @@ -45,16 +45,16 @@ tree. The Tree -------- -A tree is a collection of file information, the state of your working copy at +A tree is a collection of file information, the state of a single directory at a given point in time. A tree file looks like this:: - tree ... + tree ... And repeats for every file in the tree. -Note that for a unknown reason, the SHA-1 digest is in binary form here. +Note that the SHA-1 digest is in binary form here. The file mode is like the octal argument you could give to the ``chmod`` command. Except it is in extended form to tell regular files from @@ -88,14 +88,15 @@ accelerate operations and reduce space. More About Git formats ---------------------- -These three objects make 90 % of a Git repository. The rest is branch -information and optimizations. +These three objects make up most of the contents of a Git repository and are +used for the history. They can either appear as simple files on disk (one file +per object) or in a ``pack`` file, which is a container for a number of these +objects. -For instance there is an index of the current state of the working copy. -There are also pack files to group several small objects in a single indexed -file. +The is also an index of the current state of the working copy in the +repository as well as files to track the existing branches and tags. -For a more detailled explanation of object formats and SHA-1 digests, see: +For a more detailed explanation of object formats and SHA-1 digests, see: http://www-cs-students.stanford.edu/~blynn/gitmagic/ch08.html Just note that recent versions of Git compress object files using zlib. diff --git a/docs/tutorial/1-repo.txt b/docs/tutorial/1-repo.txt new file mode 100644 index 000000000..084ff3c09 --- /dev/null +++ b/docs/tutorial/1-repo.txt @@ -0,0 +1,28 @@ +The Repository +============== + +After this introduction, let's start directly with code:: + + >>> from dulwich.repo import Repo + +The access to a repository is through the Repo object. You can open an +existing repository or you can create a new one. There are two types of Git +repositories: + + Regular Repositories -- They are the ones you create using ``git init`` and + you daily use. They contain a ``.git`` folder. + + Bare Repositories -- There is not ".git" folder. The top-level folder + contains itself the "branches", "hooks"... folders. These are used for + published repositories (mirrors). They do not have a working tree. + +Let's create a folder and turn it into a repository, like ``git init`` would:: + + >>> from os import mkdir + >>> mkdir("myrepo") + >>> repo = Repo.init("myrepo") + >>> repo + + +You can already look a the structure of the "myrepo/.git" folder, though it +is mostly empty for now. diff --git a/docs/tutorial/2-change-file.txt b/docs/tutorial/2-change-file.txt deleted file mode 100644 index b18a0d177..000000000 --- a/docs/tutorial/2-change-file.txt +++ /dev/null @@ -1,88 +0,0 @@ -Changing a File and Commit it -============================= - -Now we have a first commit, the next one will show a difference. - -As seen in the introduction, it's about making a path in a tree point to a -new blob. The old blob will remain to compute the diff. The tree is altered -and the new commit'task is to point to this new version. - -In the following examples, we will need a ``repo`` and an initial commit -(``c1``) similar to the one made in the previous chapter:: - - >>> from dulwich.repo import Repo - >>> from dulwich.objects import Blob, Tree - >>> repo = Repo.init("myrepo", mkdir=True) - >>> blob = Blob.from_string("My file content\n") - >>> tree = Tree() - >>> tree.add(0100644, "spam", blob.id) - >>> repo.object_store.add_object(blob) - >>> repo.object_store.add_object(tree) - >>> c1_id = repo.do_commit("Initial commit.", - ... committer="JaneExample ", tree=tree.id) - -Let's first build the blob:: - - >>> from dulwich.objects import Blob - >>> spam = Blob.from_string("My new file content\n") - >>> spam.id - '16ee2682887a962f854ebd25a61db16ef4efe49f' - -An alternative is to alter the previously constructed blob object:: - - >>> blob.data = "My new file content\n" - >>> blob.id - '16ee2682887a962f854ebd25a61db16ef4efe49f' - -In any case, update the blob id known as "spam". You also have the -opportunity of changing its mode:: - - >>> tree["spam"] = (0100644, spam.id) - -Now let's record the change:: - - >>> from dulwich.objects import Commit - >>> from time import time - >>> c2 = Commit() - >>> c2.tree = tree.id - >>> c2.parents = [c1_id] - >>> c2.author = c2.committer = "John Doe " - >>> c2.commit_time = c2.author_time = int(time()) - >>> c2.commit_timezone = c2.author_timezone = 0 - >>> c2.encoding = "UTF-8" - >>> c2.message = 'Changing "spam"' - -In this new commit we record the changed tree id, and most important, the -previous commit as the parent. Parents are actually a list because a commit -may happen to have several parents after merging branches. - -Remain to record this whole new family:: - - >>> repo.object_store.add_object(spam) - >>> repo.object_store.add_object(tree) - >>> repo.object_store.add_object(c2) - -You can already ask git to introspect this commit using ``git show`` and the -value of ``c2.id`` as an argument. You'll see the difference will the -previous blob recorded as "spam". - -The diff between the previous head and the new one can be printed using -write_tree_diff:: - - >>> from dulwich.patch import write_tree_diff - >>> import sys - >>> write_tree_diff(sys.stdout, repo.object_store, repo[c1_id].tree, tree.id) - diff --git a/spam b/spam - index c55063a..16ee268 100644 - --- a/spam - +++ b/spam - @@ -1,1 +1,1 @@ - -My file content - +My new file content - -You won't see it using git log because the head is still the previous -commit. It's easy to remedy:: - - >>> repo.refs['refs/heads/master'] = c2.id - -Now all git tools will work as expected. diff --git a/docs/tutorial/1-initial-commit.txt b/docs/tutorial/2-object-store.txt similarity index 53% rename from docs/tutorial/1-initial-commit.txt rename to docs/tutorial/2-object-store.txt index 0ed9ef5f4..dfa608250 100644 --- a/docs/tutorial/1-initial-commit.txt +++ b/docs/tutorial/2-object-store.txt @@ -1,34 +1,13 @@ -The Repository -============== +The object store +================ -After this introduction, let's start directly with code:: +The objects are stored in the ``object store`` of the repository. >>> from dulwich.repo import Repo - -The access to every object is through the Repo object. You can open an -existing repository or you can create a new one. There are two types of Git -repositories: - - Regular Repositories -- They are the ones you create using ``git init`` and - you daily use. They contain a ``.git`` folder. - - Bare Repositories -- There is not ".git" folder. The top-level folder - contains itself the "branches", "hooks"... folders. These are used for - published repositories (mirrors). - -Let's create a folder and turn it into a repository, like ``git init`` would:: - - >>> from os import mkdir - >>> mkdir("myrepo") - >>> repo = Repo.init("myrepo") - >>> repo - - -You can already look a the structure of the "myrepo/.git" folder, though it -is mostly empty for now. + >>> repo = Repo.init("myrepo", mkdir=True) Initial commit -============== +-------------- When you use Git, you generally add or modify content. As our repository is empty for now, we'll start by adding a new file:: @@ -94,7 +73,7 @@ Now our repository is officialy tracking a branch named "master" refering to a single commit. Playing again with Git -====================== +---------------------- At this point you can come back to the shell, go into the "myrepo" folder and type ``git status`` to let Git confirm that this is a regular repository on @@ -115,5 +94,77 @@ blob:: $ cat spam My file content -.. attention:: Remember to recreate the repo object when you modify the - repository outside of Dulwich! +Changing a File and Committing it +--------------------------------- + +Now we have a first commit, the next one will show a difference. + +As seen in the introduction, it's about making a path in a tree point to a +new blob. The old blob will remain to compute the diff. The tree is altered +and the new commit'task is to point to this new version. + +Let's first build the blob:: + + >>> from dulwich.objects import Blob + >>> spam = Blob.from_string("My new file content\n") + >>> spam.id + '16ee2682887a962f854ebd25a61db16ef4efe49f' + +An alternative is to alter the previously constructed blob object:: + + >>> blob.data = "My new file content\n" + >>> blob.id + '16ee2682887a962f854ebd25a61db16ef4efe49f' + +In any case, update the blob id known as "spam". You also have the +opportunity of changing its mode:: + + >>> tree["spam"] = (0100644, spam.id) + +Now let's record the change:: + + >>> from dulwich.objects import Commit + >>> from time import time + >>> c2 = Commit() + >>> c2.tree = tree.id + >>> c2.parents = [commit.id] + >>> c2.author = c2.committer = "John Doe " + >>> c2.commit_time = c2.author_time = int(time()) + >>> c2.commit_timezone = c2.author_timezone = 0 + >>> c2.encoding = "UTF-8" + >>> c2.message = 'Changing "spam"' + +In this new commit we record the changed tree id, and most important, the +previous commit as the parent. Parents are actually a list because a commit +may happen to have several parents after merging branches. + +Let's put the objects in the object store:: + + >>> repo.object_store.add_object(spam) + >>> repo.object_store.add_object(tree) + >>> repo.object_store.add_object(c2) + +You can already ask git to introspect this commit using ``git show`` and the +value of ``c2.id`` as an argument. You'll see the difference will the +previous blob recorded as "spam". + +The diff between the previous head and the new one can be printed using +write_tree_diff:: + + >>> from dulwich.patch import write_tree_diff + >>> import sys + >>> write_tree_diff(sys.stdout, repo.object_store, commit.tree, tree.id) + diff --git a/spam b/spam + index c55063a..16ee268 100644 + --- a/spam + +++ b/spam + @@ -1,1 +1,1 @@ + -My file content + +My new file content + +You won't see it using git log because the head is still the previous +commit. It's easy to remedy:: + + >>> repo.refs['refs/heads/master'] = c2.id + +Now all git tools will work as expected. diff --git a/docs/tutorial/3-add-file.txt b/docs/tutorial/3-add-file.txt deleted file mode 100644 index 75d642925..000000000 --- a/docs/tutorial/3-add-file.txt +++ /dev/null @@ -1,41 +0,0 @@ -Adding a file -============= - -If you followed well, the next lesson will be straightforward. - -We need a new blob:: - - >>> ham = Blob.from_string("Another\nmultiline\nfile\n") - >>> ham.id - 'a3b5eda0b83eb8fb6e5dce91ecafda9e97269c70' - -But the same tree:: - - >>> tree["ham"] = (0100644, spam.id) - -And a new commit:: - - >>> c3 = Commit() - >>> c3.tree = tree.id - >>> c3.parents = [commit.id] - >>> c3.author = c3.committer = author - >>> c3.commit_time = c3.author_time = int(time()) - >>> c3.commit_timezone = c3.author_timezone = tz - >>> c3.encoding = "UTF-8" - >>> c3.message = 'Adding "ham"' - -Save it all:: - - >>> object_store.add_object(spam) - >>> object_store.add_object(tree) - >>> object_store.add_object(c3) - -Update the head:: - - >>> repo.refs['refs/heads/master'] = commit.id - -A call to ``git show`` will confirm the addition of "spam". - -Remember you can also call ``git checkout -f`` to make it appear. - -Well... Adding "spam" was not such a good idea... We'll remove it. diff --git a/docs/tutorial/3-conclusion.txt b/docs/tutorial/3-conclusion.txt new file mode 100644 index 000000000..5ec596ddb --- /dev/null +++ b/docs/tutorial/3-conclusion.txt @@ -0,0 +1,11 @@ +Conclusion +========== + +This tutorial currently only covers a small (but important) part of Dulwich. +It still needs to be extended to cover packs, tags, refs, reflogs and network +communication. + +Dulwich is abstracting much of the Git plumbing, so there would be more to +see. + +For now, that's all folks! diff --git a/docs/tutorial/4-remove-file.txt b/docs/tutorial/4-remove-file.txt deleted file mode 100644 index 125639ce3..000000000 --- a/docs/tutorial/4-remove-file.txt +++ /dev/null @@ -1,30 +0,0 @@ -Removing a file -=============== - -Removing a file just means removing its entry in the tree. The blob won't be -deleted because Git tries to preserve the history of your repository. - -It's all pythonic:: - - >>> del tree["ham"] - - >>> c4 = Commit() - >>> c4.tree = tree.id - >>> c4.parents = [commit.id] - >>> c4.author = c4.committer = author - >>> c4.commit_time = c4.author_time = int(time()) - >>> c4.commit_timezone = c4.author_timezone = tz - >>> c4.encoding = "UTF-8" - >>> c4.message = 'Removing "ham"' - -Here we only have the new tree and the commit to save:: - - >>> object_store.add_object(spam) - >>> object_store.add_object(tree) - >>> object_store.add_object(c4) - -And of course update the head:: - - >>> repo.refs['refs/heads/master'] = commit.id - -If you don't trust me, ask ``git show``. ;-) diff --git a/docs/tutorial/5-rename-file.txt b/docs/tutorial/5-rename-file.txt deleted file mode 100644 index 15c6b264a..000000000 --- a/docs/tutorial/5-rename-file.txt +++ /dev/null @@ -1,33 +0,0 @@ -Renaming a file -=============== - -Remember you learned that the file name and content are distinct. So renaming -a file is just about associating a blob id to a new name. We won't store more -content, and the operation will be painless. - -Let's transfer the blob id from the old name to the new one:: - - >>> tree["eggs"] = tree["spam"] - >>> del tree["spam"] - -As usual, we need a commit to store the new tree id:: - - >>> c5 = Commit() - >>> c5.tree = tree.id - >>> c5.parents = [commit.id] - >>> c5.author = c5.committer = author - >>> c5.commit_time = c5.author_time = int(time()) - >>> c5.commit_timezone = c5.author_timezone = tz - >>> c5.encoding = "UTF-8" - >>> c5.message = 'Rename "spam" to "eggs"' - -As for a deletion, we only have a tree and a commit to save:: - - >>> object_store.add_object(tree) - >>> object_store.add_object(c5) - -Remains to make the head bleeding-edge:: - - >>> repo.refs['refs/heads/master'] = commit.id - -As a last exercise, see how ``git show`` illustrates it. diff --git a/docs/tutorial/6-conclusion.txt b/docs/tutorial/6-conclusion.txt deleted file mode 100644 index 8b4dd41e7..000000000 --- a/docs/tutorial/6-conclusion.txt +++ /dev/null @@ -1,11 +0,0 @@ -Conclusion -========== - -You can also make Tag objects, but this is left as a exercise to the reader. - -Dulwich is abstracting much of the Git plumbing, so there would be more to -see. - -Dulwich is also able to clone and push repositories. - -That's all folks! diff --git a/docs/tutorial/index.txt b/docs/tutorial/index.txt index f2a06bded..a7c1cedb7 100644 --- a/docs/tutorial/index.txt +++ b/docs/tutorial/index.txt @@ -5,9 +5,6 @@ Dulwich Tutorial .. contents:: .. include:: 0-introduction.txt -.. include:: 1-initial-commit.txt -.. include:: 2-change-file.txt -.. include:: 3-add-file.txt -.. include:: 4-remove-file.txt -.. include:: 5-rename-file.txt -.. include:: 6-conclusion.txt +.. include:: 1-repo.txt +.. include:: 2-object-store.txt +.. include:: 3-conclusion.txt diff --git a/dulwich/tests/__init__.py b/dulwich/tests/__init__.py index c80cd9f82..8594c98da 100644 --- a/dulwich/tests/__init__.py +++ b/dulwich/tests/__init__.py @@ -80,8 +80,9 @@ def test_suite(): result.addTests(suite) tutorial = [ '0-introduction', - '1-initial-commit', - '2-change-file', + '1-repo', + '2-object-store', + '3-conclusion', ] tutorial_files = ["../../docs/tutorial/%s.txt" % name for name in tutorial] def setup(test):