git init

TL;DR: git init converts an ordinary directory into a fully functioning Git repository. It achieves this by creating a hidden administrative directory named .git/ at the root of the project. This folder acts as the repository’s brain, housing the object database, branch references, configurations, and lifecycle hook templates that track your code’s history.


What Does git init Actually Do?

At its core, a computer’s filesystem is relatively simple: it tracks filenames, file contents, file sizes, and directory hierarchies. It has no native understanding of change history, author identities, or branching paths.

When you run git init inside a folder, you are not altering your existing files. You are executing an initialization script that builds a localized, flat-file database system within your project. This database is self-contained within a single hidden directory: .git/.

By creating .git/, Git draws a boundary around your files. The directory containing .git/ becomes the Working Directory (your workspace). Git immediately begins scanning this directory for untracked changes, waiting for you to explicitly stage them using git add and commit them to the database.


What Happens Inside .git/ (Hands-on exploration)

To understand how Git works, you must look inside the .git/ directory. Let’s initialize a fresh repository and examine the skeleton of Git’s internal database.

Step 1: Initialize the Directory

First, create a new directory and initialize it:

$ mkdir git-init-deepdive && cd git-init-deepdive
$ git init
Initialized empty Git repository in /home/admin/Code/git-init-deepdive/.git/

Now let’s list the contents of the newly created .git/ folder:

$ ls -al .git/
total 32
drwxr-xr-x 7 admin admin 4096 Jul  7 10:25 .
drwxr-xr-x 3 admin admin 4096 Jul  7 10:25 ..
-rw-r--r-x 1 admin admin   23 Jul  7 10:25 HEAD
-rw-r--r-x 1 admin admin   92 Jul  7 10:25 config
-rw-r--r-x 1 admin admin   73 Jul  7 10:25 description
drwxr-xr-x 2 admin admin 4096 Jul  7 10:25 hooks
drwxr-xr-x 2 admin admin 4096 Jul  7 10:25 info
drwxr-xr-x 4 admin admin 4096 Jul  7 10:25 objects
drwxr-xr-x 4 admin admin 4096 Jul  7 10:25 refs

Let’s analyze what each of these entries does in a newly initialized repository:

1. HEAD (The Active Pointer)

The HEAD file is a plain text file that tells Git which branch or commit is currently active. Let’s read the contents of this file:

$ cat .git/HEAD
ref: refs/heads/main

This output shows that HEAD points to a reference file at refs/heads/main.

However, if we look inside our repository, the branch file refs/heads/main does not exist yet!

$ ls -R .git/refs
.git/refs:
heads/  tags/

.git/refs/heads:
# Empty!

This state is known as an Unborn Branch. Git points HEAD to your default branch target (e.g., main), but it does not create the physical branch reference file until you write your first commit. Once you make a commit, Git writes the commit object, creates the file at .git/refs/heads/main, and writes the 40-character commit hash inside it.

2. config (Local Configuration Settings)

This file houses repository-level settings that override your system and global configurations (refer to git config for details). Let’s view the initial settings:

$ cat .git/config
[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true

These core variables define how Git interacts with your filesystem:

  • repositoryformatversion = 0: Defines the repository’s database format version. If Git encounters a repository version higher than it supports, it will refuse to read it.
  • filemode = true: Tells Git to track POSIX file executable bit changes (e.g., if you make a script executable via chmod +x).
  • bare = false: Declares that this is a standard working repository containing checked-out files on disk. A “bare” repository contains only the Git administrative directory (usually for remote hosting servers) and has this set to true.
  • logallrefupdates = true: Instructs Git to maintain the reflog (a detailed history tracking where branch tips and HEAD have pointed over time, stored in .git/logs/).

3. description (Legacy Web Helper)

This is a plain text file containing a placeholder: Unnamed repository; edit this file 'description' to name the repository.. It is used by GitWeb (a legacy web-based viewer for Git repos). In modern development, this file is ignored and can be safely deleted or left as-is.

4. hooks/ (Lifecycle Automation)

This folder contains shell script templates that automate tasks when Git events occur (e.g., pre-commit, commit-msg, pre-push). These templates are copied from your system installation’s templates directory during initialization. By default, all scripts in this directory end in .sample, which prevents them from executing. To activate a hook, remove the .sample extension and make the script executable (chmod +x).

5. info/exclude (Local-Only Ignore List)

This file is used to define ignore patterns, similar to a .gitignore file. However, while .gitignore is committed and shared with your team, info/exclude is local-only. Use it to ignore files specific to your local machine (such as local IDE configurations) without affecting other developers’ environments.

6. objects/ (The Object Database)

This is the core storage database for your repository. It uses content-addressable storage to store objects, which are categorized into:

  • Blobs: Stored file contents.
  • Trees: Directory hierarchies.
  • Commits: Metadata snapshots pointing to trees and parents. Initially, the database contains only two empty subdirectories: info/ and pack/ (used for packfile indexes and storage optimization). The database is empty until you run your first commit.

7. refs/ (References)

This directory acts as a index for your repository’s commit pointers. It contains:

  • refs/heads/: Houses local branch pointers.
  • refs/tags/: Houses tags (frozen pointers to historical commits).
  • refs/remotes/: Houses pointers tracking the state of branches on remote repositories (created after adding a remote).

Detailed Command Options & Advanced Usage

1. Re-initializing an Existing Repository

What happens if you run git init in a directory that is already a Git repository?

$ git init
Reinitialized existing Git repository in /home/admin/Code/git-init-deepdive/.git/

Running git init on an existing repository is safe. It does not overwrite your commits, branches, index, or configuration files. Instead, it checks the directory, repairs missing metadata folders if they were accidentally deleted, and pulls in updated hook templates from the system’s template directory.

2. Bare Repositories (git init --bare)

Standard Git repositories are designed for local development. They contain a .git/ administrative folder alongside your editable project files (Working Copy).

If you are setting up a central server to share code (like GitHub or a self-hosted server), you do not need editable files. You only need the version database where developers can push and pull changes. This is called a Bare Repository.

# Initialize a bare repository
$ git init --bare my-project.git
Initialized empty Git repository in /home/admin/Code/my-project.git/

Let’s look at the directory structure of a bare repository:

$ ls -F my-project.git
HEAD  config  description  hooks/  info/  objects/  refs/

Notice that there is no .git/ subdirectory. Instead, the administrative files are placed directly in the root directory. A bare repository has no working directory, meaning you cannot run commands like git add or git commit within it. It serves solely as a shared storage hub.

3. Setting the Initial Branch Name

Historically, Git default-initialized the primary branch as master. To adopt modern naming conventions, you can specify the default branch name during initialization:

# Initialize a repository with a default branch named "main"
$ git init --initial-branch=main

You can also set this default branch name globally so that all future git init commands apply it automatically (refer to git config):

$ git config --global init.defaultBranch main

Real-World Scenario: Setting Up a Clean Repository

Let’s walk through initializing a new project and configuring it with standard settings before recording the first commit.

Step 1: Create the Project Directory and Initialize Git

$ mkdir team-portal && cd team-portal
$ git init --initial-branch=main
Initialized empty Git repository in /home/admin/Code/team-portal/.git/

Step 2: Establish Git Configurations

Verify your local user configurations to ensure commits match your team identity:

$ git config --local user.name "Alex Developer"
$ git config --local user.email "alex@portalproject.com"

Step 3: Define Ignore Rules

Create a .gitignore file to prevent tracking temporary files or system caches:

$ echo "node_modules/" > .gitignore
$ echo ".DS_Store" >> .gitignore
$ echo "*.log" >> .gitignore

Step 4: Write Initial Code and Record the First Commit

Create a file, stage it, and record the commit:

$ echo "<h1>Team Portal</h1>" > index.html
$ git add .
$ git commit -m "chore: initial repository layout"
[main (root-commit) f23c92e] chore: initial repository layout
 2 files changed, 4 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 index.html

Let’s look inside .git/refs/heads/main now that we have recorded a commit:

$ cat .git/refs/heads/main
f23c92e10db526ab1a9db3f28249a02ce5c144e1

The unborn branch state has resolved. The refs/heads/main file exists and points directly to our first commit hash (f23c92e1...).


Common Pitfalls and Mistakes

1. Accidental Nested Repositories

The Mistake: Running git init inside a subdirectory of a folder that is already tracked by a Git repository.

/my-project/ (.git/ exists)
  └── /src/
        └── /components/ (runs git init here)

Why it’s a problem: Git will treat the inner directory as a Submodule or ignore its tracking contents entirely, leading to detached commits and unpushed files. The Fix: Avoid initializing Git inside subdirectories. Always run git status before running git init to verify whether the parent directory is already tracked:

# If this returns status info, you are already inside a repository!
$ git status

If you initialized a nested repository by mistake, delete the inner .git/ folder to restore normal tracking:

$ rm -rf src/components/.git

2. Initializing in Your System’s Home Directory

The Mistake: Running git init in your terminal’s default path (e.g., C:\Users\Username\ or /home/username/) by accident. Why it’s a problem: Git will attempt to track every file on your computer, including desktop files, downloads, applications, and system caches. This slows down your terminal and leads to commit conflicts. The Fix: Locate and delete the accidental .git folder in your home directory:

# Navigate to home and delete the hidden .git directory
$ cd ~
$ rm -rf .git

(Caution: Double-check that you are in your home directory before running rm -rf .git to avoid deleting a valid project history.)

3. Manually Editing Database Objects

The Mistake: Attempting to manually modify, rename, or write files inside .git/objects/ or .git/refs/ to fix a repository issue. Why it’s a problem: Git’s object database relies on exact cryptographic hashing. Modifying even a single character in an object file will corrupt the hash, making the repository unreadable. The Fix: Use Git’s command-line API to interact with repository contents rather than editing the files in .git/ directly.


Command Quick Reference

CommandActionCommon Context
git initInitializes standard Git repositoryRun once at project root
git init --bareInitializes repository without working directoryRemote servers / shared hubs
git init -b <name>Specifies custom default branch nameReplaces legacy master branch naming
git init --template=<dir>Initializes repo with custom hook templatesStandardizes team hooks
rm -rf .gitUn-tracks project, deleting all local historyWarning: Destructive and irreversible

FAQ (People Also Ask)

Does git init upload my code to GitHub?

No. git init is a local command that operates entirely on your computer’s storage. It initializes a local version database in your directory but does not connect to the internet or link to external hosts. To upload your code to GitHub, you must create a remote repository on GitHub, link it locally using git remote add, and push your commits.

How do I undo git init?

To remove Git tracking from a project, delete the hidden .git/ folder. This removes all commit history, branches, configuration settings, and stashes. Your working files will remain intact on your disk but will no longer be tracked:

$ rm -rf .git

What is the difference between a standard and bare Git repository?

A standard Git repository contains a .git/ folder alongside a working copy of your files, allowing you to edit and commit changes locally. A bare repository contains only the Git database files directly in its root (no .git/ folder) and has no working directory. Bare repositories are used on remote servers to store pushed commits safely.

Why is the .git folder hidden by default?

The .git/ directory is hidden by default to prevent accidental modification, moving, or deletion of its contents. Since all version history is stored in this folder, deleting it deletes the repository’s entire history.


Test Your Knowledge

Loading quiz…