Back to Git & Dev Tools
2026-03-087 min read

Low-level commands (plumbing) (Git & Dev Tools)

Learn Low-level commands (plumbing) (Git & Dev Tools) step by step with clear examples and exercises.

Title: Mastering Low-level Git Commands (Plumbing) for Practical Depth

Why This Matters

In the realm of software development, Git is an indispensable tool that simplifies code management and collaboration among developers. However, understanding low-level commands, also known as plumbing commands, can elevate your Git expertise to new heights. These commands offer direct access to the Git repository's data structures and object database, enabling a deeper understanding of Git internals, troubleshooting complex issues, or even extending Git's functionality. This knowledge is invaluable for acing interviews, debugging real-world problems, or contributing to open-source projects.

Prerequisites

Before delving into low-level Git commands, it is crucial to have a strong foundation in high-level Git commands such as git init, git add, git commit, and git push. Familiarity with the Git workflow, branches, merging, and rebase will also be beneficial.

Git Basics Recap

  1. Initializing a new repository: git init
  2. Adding files to the staging area: git add
  3. Committing changes: git commit -m ""
  4. Updating the working directory with remote changes: git pull
  5. Pushing local commits to a remote repository: git push origin
  6. Creating a new branch: git branch
  7. Switching between branches: git checkout
  8. Merging branches: git merge

Core Concept

Low-level Git commands (plumbing) are a set of internal commands that provide direct access to the Git repository's data structures and object database. These commands are not intended for everyday use but can help in understanding how Git works internally, troubleshooting issues, or even extending Git's functionality.

Git Objects

Git stores everything as objects. There are four main types of objects:

  1. Blob: Stores file content (e.g., source code)
  2. Tree: Represents a directory structure and points to the blobs and other trees that make up the directory
  3. Commit: A snapshot containing a tree, author information, commit message, and parent commits' hashes
  4. Tag: A lightweight annotated tag pointing to a specific commit

Plumbing Commands

Git provides several plumbing commands to interact with these objects directly. Some of the essential plumbing commands are:

  • cat-file: Display the contents of a Git object (blob, tree, commit, or tag)
  • checkout-index: Update the index (staging area) with changes from the working directory
  • commit-tree: Create a new commit using a tree and author information
  • count-objects: Count the number of objects in the Git repository
  • diff-index: Show differences between the index and the working directory or two trees
  • hash-object: Create a new object (blob, tree, or commit) from given data
  • ls-tree: List the contents of a tree as a series of Git objects
  • ls-remote: Fetch information about remote repository's refs and objects
  • merge-base: Find the most recent common ancestor (MRCSA) between two commits or trees
  • read-tree: Read a tree from a Git repository
  • rev-list: List commits in reverse chronological order, optionally filtered by conditions
  • rev-parse: Resolve a revision to its object name
  • show-ref: Show information about local refs (branches and tags)
  • symbolic-ref: Update a reference to point to another commit
  • update-index: Update the index with changes from the working directory or a patch file
  • verify-pack: Verify the integrity of a Git packfile
  • write-tree: Create a new tree object from given files and directories

Worked Example

To demonstrate the use of plumbing commands, let's create a simple Git repository, modify some files, and then directly manipulate objects using low-level commands.

  1. Initialize a new Git repository:
git init my_repo
cd my_repo
  1. Create a file file1.txt with some content:
echo "Hello, World!" > file1.txt
  1. Add the new file to the index and create an initial commit:
git add file1.txt
git commit -m "Initial commit"
  1. Now, let's create a new blob object with some different content for file1.txt using the hash-object command:
echo "New Content" > file1.txt
git hash-object file1.txt --stdin

This command will output a unique SHA-1 hash representing the new blob object. Save this hash for later use.

  1. Create a new tree object that includes both the old and new file1.txt objects:
git write-tree

This command will output a new tree object's hash. Save this hash for later use.

  1. Create a new commit using the new tree object and an author name:
git commit-tree <new_tree_hash> -m "New commit with modified file1.txt" --author="Author Name <author@example.com>"

Replace `` with the hash output from step 5. This command will create a new commit with the specified message and author information using the new tree object.

  1. To verify that the new commit has been created, use the rev-list command:
git rev-list HEAD

This command should display both commits in reverse chronological order, with the new commit appearing first.

  1. Now let's examine the objects using cat-file and ls-tree commands:

Show the contents of the new blob object

git cat-file -p

List the contents of the new tree object as Git objects

git ls-tree -r


Replace `` and `` with the respective hashes from steps 4 and 6.

Common Mistakes

  1. Forgetting to specify object type: When using hash-object, always specify the object type (blob, tree, or commit) as the first argument. Failing to do so may result in an error or unexpected behavior.

Correct usage:

git hash-object file1.txt --stdin
  1. Not quoting arguments: When using plumbing commands with filenames containing spaces, always quote the arguments to prevent errors.

Correct usage:

git hash-object "file with space.txt" --stdin
  1. Using plumbing commands inappropriately: Low-level Git commands can be powerful tools but should be used judiciously, as they can potentially corrupt your repository or introduce inconsistencies if misused.
  1. Not understanding the implications of low-level commands: Before using a low-level command, ensure you understand its purpose and potential consequences on your repository.

Practice Questions

  1. Write a command to list all blobs in your Git repository.
git ls-tree -r HEAD --name-only | grep blob
  1. Create a new branch named new_branch based on the latest commit, and switch to it.
git checkout -b new_branch HEAD
  1. Find the SHA-1 hash of the first commit in your Git repository.
git rev-list --max-count=1 HEAD
  1. Merge new_branch back into the main branch (master).
git checkout master
git merge new_branch
  1. Remove a file file2.txt from the index without deleting it from the working directory.
git rm --cached file2.txt
  1. Create a new tag named v1.0.0 for the latest commit.
git tag v1.0.0 HEAD
  1. Find the parent commit of a specific commit using its SHA-1 hash.
git merge-base <commit_hash> HEAD
  1. Show the differences between the working directory and the index (staging area).
git diff --name-only --diff-filter=d
  1. Verify the integrity of a Git packfile located at path/to/packfile.pack.
git verify-pack path/to/packfile.pack

FAQ

  1. What is the difference between high-level and low-level Git commands?

High-level commands are user-friendly, easy-to-use commands like git init, git add, and git commit. Low-level commands (plumbing) provide direct access to the Git repository's data structures and object database, allowing for more control over the repository.

  1. Why would I need to use low-level Git commands?

Low-level Git commands can help in understanding how Git works internally, troubleshooting complex issues, or even extending Git's functionality. They are not meant for everyday use but can be useful in specific situations.

  1. Are there any risks associated with using low-level Git commands?

Yes, since low-level commands give you direct access to the repository's data structures, misuse could potentially corrupt your repository or introduce inconsistencies. It is essential to understand what each command does before using it.

  1. Can I use low-level Git commands in a collaborative project?

While low-level commands can be used in a collaborative project, it's generally recommended to stick with high-level commands to avoid unintended consequences for your teammates. If you need to use low-level commands, discuss the changes with your team before applying them to the shared repository.

  1. How do I revert a specific commit using plumbing commands?

To revert a specific commit using plumbing commands, first find the parent commit hash and create a new tree object that excludes the unwanted file or changes. Then, use commit-tree to create a new commit with the parent commit hash and the new tree object as its tree. Finally, use reflog to reset the branch to the new commit.

Find the parent commit hash

git rev-list --parents -n 1

Create a new tree object without the unwanted file or changes

... (remove the unwanted file or changes from the working directory)

git write-tree

Create a new commit with the parent commit hash and the new tree object as its tree

git commit-tree -p -m "Revert commit" --author="Author Name "

Reset the branch to the new commit using reflog

git reset --hard HEAD@{}


Replace ``, ``, and `` with the respective hashes and number from the steps above.
Low-level commands (plumbing) (Git & Dev Tools) | Git & Dev Tools | XQA Learn