diff --git a/doc/source/index.rst b/doc/source/index.rst index 69fb573a4..72db8ee5a 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -9,6 +9,7 @@ GitPython Documentation :maxdepth: 2 intro + quickstart tutorial reference roadmap diff --git a/doc/source/quickstart.rst b/doc/source/quickstart.rst new file mode 100644 index 000000000..c5930eb8a --- /dev/null +++ b/doc/source/quickstart.rst @@ -0,0 +1,244 @@ +.. _quickdoc_toplevel: + +.. highlight:: python + +.. _quickdoc-label: + +============================== +GitPython Quick Start Tutorial +============================== +Welcome to the GitPython Quickstart Guide! Designed for developers seeking a practical and interactive learning experience, this concise resource offers step-by-step code snippets to swiftly initialize/clone repositories, perform essential Git operations, and explore GitPython's capabilities. Get ready to dive in, experiment, and unleash the power of GitPython in your projects! + + +git.Repo +******** + +There are a few ways to create a :class:`git.Repo ` object + +Initialize a new git Repo +######################### + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [1-test_init_repo_object] + :end-before: # ![1-test_init_repo_object] + +Existing local git Repo +####################### + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [2-test_init_repo_object] + :end-before: # ![2-test_init_repo_object] + +Clone from URL +############## + +For the rest of this tutorial we will use a clone from https://github.com/gitpython-developers/QuickStartTutorialFiles.git + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [1-test_cloned_repo_object] + :end-before: # ![1-test_cloned_repo_object] + + +Trees & Blobs +************** + +Latest Commit Tree +################## + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [12-test_cloned_repo_object] + :end-before: # ![12-test_cloned_repo_object] + +Any Commit Tree +############### + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [13-test_cloned_repo_object] + :end-before: # ![13-test_cloned_repo_object] + +Display level 1 Contents +######################## + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [14-test_cloned_repo_object] + :end-before: # ![14-test_cloned_repo_object] + +Recurse through the Tree +######################## + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [15-test_cloned_repo_object] + :end-before: # ![15-test_cloned_repo_object] + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [16-test_cloned_repo_object] + :end-before: # ![16-test_cloned_repo_object] + + + + +Usage +**************** + +Add file to staging area +######################## + + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [2-test_cloned_repo_object] + :end-before: # ![2-test_cloned_repo_object] + + Now lets add the updated file to git + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [3-test_cloned_repo_object] + :end-before: # ![3-test_cloned_repo_object] + + Notice the add method requires a list as a parameter + + Warning: If you experience any trouble with this, try to invoke :class:`git ` instead via repo.git.add(path) + +Commit +###### + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [4-test_cloned_repo_object] + :end-before: # ![4-test_cloned_repo_object] + +List of commits associated with a file +####################################### + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [5-test_cloned_repo_object] + :end-before: # ![5-test_cloned_repo_object] + + Notice this returns a generator object + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [6-test_cloned_repo_object] + :end-before: # ![6-test_cloned_repo_object] + + returns list of :class:`Commit ` objects + +Printing text files +#################### +Lets print the latest version of `/dir1/file2.txt` + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [17-test_cloned_repo_object] + :end-before: # ![17-test_cloned_repo_object] + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [18-test_cloned_repo_object] + :end-before: # ![18-test_cloned_repo_object] + + Previous version of `/dir1/file2.txt` + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [18.1-test_cloned_repo_object] + :end-before: # ![18.1-test_cloned_repo_object] + +Status +###### + * Untracked files + + Lets create a new file + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [7-test_cloned_repo_object] + :end-before: # ![7-test_cloned_repo_object] + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [8-test_cloned_repo_object] + :end-before: # ![8-test_cloned_repo_object] + + * Modified files + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [9-test_cloned_repo_object] + :end-before: # ![9-test_cloned_repo_object] + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [10-test_cloned_repo_object] + :end-before: # ![10-test_cloned_repo_object] + + returns a list of :class:`Diff ` objects + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [11-test_cloned_repo_object] + :end-before: # ![11-test_cloned_repo_object] + +Diffs +###### + +Compare staging area to head commit + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [11.1-test_cloned_repo_object] + :end-before: # ![11.1-test_cloned_repo_object] + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [11.2-test_cloned_repo_object] + :end-before: # ![11.2-test_cloned_repo_object] + +Compare commit to commit + + .. literalinclude:: ../../test/test_quick_doc.py + :language: python + :dedent: 8 + :start-after: # [11.3-test_cloned_repo_object] + :end-before: # ![11.3-test_cloned_repo_object] + + +More Resources +**************** + +Remember, this is just the beginning! There's a lot more you can achieve with GitPython in your development workflow. +To explore further possibilities and discover advanced features, check out the full :ref:`GitPython tutorial ` +and the :ref:`API Reference `. Happy coding! diff --git a/test/test_quick_doc.py b/test/test_quick_doc.py new file mode 100644 index 000000000..eaee4e581 --- /dev/null +++ b/test/test_quick_doc.py @@ -0,0 +1,224 @@ +import pytest + + +from test.lib import TestBase +from test.lib.helper import with_rw_directory + + +class QuickDoc(TestBase): + def tearDown(self): + import gc + + gc.collect() + + @with_rw_directory + def test_init_repo_object(self, path_to_dir): + + # [1-test_init_repo_object] + # $ git init + + from git import Repo + + repo = Repo.init(path_to_dir) + # ![1-test_init_repo_object] + + # [2-test_init_repo_object] + repo = Repo(path_to_dir) + # ![2-test_init_repo_object] + + @with_rw_directory + def test_cloned_repo_object(self, local_dir): + + from git import Repo + import git + # code to clone from url + # [1-test_cloned_repo_object] + # $ git clone + + repo_url = "https://github.com/gitpython-developers/QuickStartTutorialFiles.git" + + repo = Repo.clone_from(repo_url, local_dir) + # ![1-test_cloned_repo_object] + + # code to add files + # [2-test_cloned_repo_object] + # We must make a change to a file so that we can add the update to git + + update_file = 'dir1/file2.txt' # we'll use local_dir/dir1/file2.txt + with open(f"{local_dir}/{update_file}", 'a') as f: + f.write('\nUpdate version 2') + # ![2-test_cloned_repo_object] + + # [3-test_cloned_repo_object] + # $ git add + add_file = [update_file] # relative path from git root + repo.index.add(add_file) # notice the add function requires a list of paths + # ![3-test_cloned_repo_object] + + # code to commit - not sure how to test this + # [4-test_cloned_repo_object] + # $ git commit -m + repo.index.commit("Update to file2") + # ![4-test_cloned_repo_object] + + # [5-test_cloned_repo_object] + # $ git log + + # relative path from git root + repo.iter_commits(all=True, max_count=10, paths=update_file) # gets the last 10 commits from all branches + + # Outputs: + + # ![5-test_cloned_repo_object] + + # [6-test_cloned_repo_object] + commits_for_file_generator = repo.iter_commits(all=True, max_count=10, paths=update_file) + commits_for_file = [c for c in commits_for_file_generator] + commits_for_file + + # Outputs: [, + # ] + # ![6-test_cloned_repo_object] + + # Untracked files - create new file + # [7-test_cloned_repo_object] + f = open(f'{local_dir}/untracked.txt', 'w') # creates an empty file + f.close() + # ![7-test_cloned_repo_object] + + # [8-test_cloned_repo_object] + repo.untracked_files + # Output: ['untracked.txt'] + # ![8-test_cloned_repo_object] + + # Modified files + # [9-test_cloned_repo_object] + # Let's modify one of our tracked files + + with open(f'{local_dir}/Downloads/file3.txt', 'w') as f: + f.write('file3 version 2') # overwrite file 3 + # ![9-test_cloned_repo_object] + + # [10-test_cloned_repo_object] + repo.index.diff(None) # compares staging area to working directory + + # Output: [, + # ] + # ![10-test_cloned_repo_object] + + # [11-test_cloned_repo_object] + diffs = repo.index.diff(None) + for d in diffs: + print(d.a_path) + + # Output + # Downloads/file3.txt + # ![11-test_cloned_repo_object] + + # compares staging area to head commit + # [11.1-test_cloned_repo_object] + diffs = repo.index.diff(repo.head.commit) + for d in diffs: + print(d.a_path) + + # Output + + # ![11.1-test_cloned_repo_object] + # [11.2-test_cloned_repo_object] + # lets add untracked.txt + repo.index.add(['untracked.txt']) + diffs = repo.index.diff(repo.head.commit) + for d in diffs: + print(d.a_path) + + # Output + # untracked.txt + # ![11.2-test_cloned_repo_object] + + # Compare commit to commit + # [11.3-test_cloned_repo_object] + first_commit = [c for c in repo.iter_commits(all=True)][-1] + diffs = repo.head.commit.diff(first_commit) + for d in diffs: + print(d.a_path) + + # Output + # dir1/file2.txt + # ![11.3-test_cloned_repo_object] + + + + '''Trees and Blobs''' + + # Latest commit tree + # [12-test_cloned_repo_object] + tree = repo.head.commit.tree + # ![12-test_cloned_repo_object] + + # Previous commit tree + # [13-test_cloned_repo_object] + prev_commits = [c for c in repo.iter_commits(all=True, max_count=10)] # last 10 commits from all branches + tree = prev_commits[0].tree + # ![13-test_cloned_repo_object] + + # Iterating through tree + # [14-test_cloned_repo_object] + files_and_dirs = [(entry, entry.name, entry.type) for entry in tree] + files_and_dirs + + # Output + # [(< git.Tree "SHA1-HEX_HASH" >, 'Downloads', 'tree'), + # (< git.Tree "SHA1-HEX_HASH" >, 'dir1', 'tree'), + # (< git.Blob "SHA1-HEX_HASH" >, 'file4.txt', 'blob')] + # ![14-test_cloned_repo_object] + + # [15-test_cloned_repo_object] + def print_files_from_git(root, level=0): + for entry in root: + print(f'{"-" * 4 * level}| {entry.path}, {entry.type}') + if entry.type == "tree": + print_files_from_git(entry, level + 1) + + # ![15-test_cloned_repo_object] + + # [16-test_cloned_repo_object] + print_files_from_git(tree) + + # Output + # | Downloads, tree + # ----| Downloads / file3.txt, blob + # | dir1, tree + # ----| dir1 / file1.txt, blob + # ----| dir1 / file2.txt, blob + # | file4.txt, blob + # # ![16-test_cloned_repo_object] + + # Printing text files + # [17-test_cloned_repo_object] + print_file = 'dir1/file2.txt' + tree[print_file] # the head commit tree + + # Output + # ![17-test_cloned_repo_object] + + # print latest file + # [18-test_cloned_repo_object] + blob = tree[print_file] + print(blob.data_stream.read().decode()) + + # Output + # file 2 version 1 + # Update version 2 + # ![18-test_cloned_repo_object] + + # print previous tree + # [18.1-test_cloned_repo_object] + commits_for_file = [c for c in repo.iter_commits(all=True, paths=print_file)] + tree = commits_for_file[-1].tree # gets the first commit tree + blob = tree[print_file] + + print(blob.data_stream.read().decode()) + + # Output + # file 2 version 1 + # ![18.1-test_cloned_repo_object] \ No newline at end of file