# 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 `git.Repo` object

### Initialize a new git Repo

>     # $ git init <path/to/dir>

>     from git import Repo

>     repo = Repo.init(path_to_dir)

### Existing local git Repo

>     repo = Repo(path_to_dir)

### Clone from URL

For the rest of this tutorial we will use a clone from https://github.com/gitpython-developers/QuickStartTutorialFiles.git

>     # $ git clone <url> <local_dir>

>     repo_url = "https://github.com/gitpython-developers/QuickStartTutorialFiles.git"

>     repo = Repo.clone_from(repo_url, local_dir)

## Trees & Blobs

### Latest Commit Tree

>     tree = repo.head.commit.tree

### Any Commit Tree

>     prev_commits = list(repo.iter_commits(all=True, max_count=10))  # Last 10 commits from all branches.
>     tree = prev_commits[0].tree

### Display level 1 Contents

>     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')]

### Recurse through the Tree

>     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)

>     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

## Usage

### Add file to staging area

>     # 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")

> Now lets add the updated file to git

>     # $ git add <file>
>     add_file = [update_file]  # relative path from git root
>     repo.index.add(add_file)  # notice the add function requires a list of paths

> Notice the add method requires a list as a parameter

> Warning: If you experience any trouble with this, try to invoke `git` instead via repo.git.add(path)

### Commit

>     # $ git commit -m <message>
>     repo.index.commit("Update to file2")

### List of commits associated with a file

>     # $ git log <file>

>     # 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: <generator object Commit._iter_from_process_or_stream at 0x7fb66c186cf0>

> Notice this returns a generator object

>     commits_for_file_generator = repo.iter_commits(all=True, max_count=10, paths=update_file)
>     commits_for_file = list(commits_for_file_generator)
>     commits_for_file

>     # Outputs: [<git.Commit "SHA1-HEX_HASH-2">,
>     # <git.Commit "SHA1-HEX-HASH-1">]

> returns list of `Commit` objects

### Printing text files

Lets print the latest version of <local_dir>/dir1/file2.txt

>     print_file = "dir1/file2.txt"
>     tree[print_file]  # The head commit tree.

>     # Output <git.Blob "SHA1-HEX-HASH">

>     blob = tree[print_file]
>     print(blob.data_stream.read().decode())

>     # Output
>     # File 2 version 1
>     # Update version 2

> Previous version of <local_dir>/dir1/file2.txt

>     commits_for_file = list(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

### Status

> - Untracked files

>   > Lets create a new file

>   >     f = open(f"{local_dir}/untracked.txt", "w")  # Creates an empty file.
>   >     f.close()

>   >     repo.untracked_files
>   >     # Output: ['untracked.txt']

> - Modified files

>   >     # 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.

>   >     repo.index.diff(None)  # Compares staging area to working directory.

>   >     # Output: [<git.diff.Diff object at 0x7fb66c076e50>,
>   >     # <git.diff.Diff object at 0x7fb66c076ca0>]

>   > returns a list of `Diff` objects

>   >     diffs = repo.index.diff(None)
>   >     for d in diffs:
>   >         print(d.a_path)

>   >     # Output
>   >     # Downloads/file3.txt

### Diffs

Compare staging area to head commit

>     diffs = repo.index.diff(repo.head.commit)
>     for d in diffs:
>         print(d.a_path)

>     # Output

>     # Let's 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

Compare commit to commit

>     first_commit = list(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

## 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 GitPython tutorial and the API Reference. Happy coding!