Git

What Does Git Init Do? Learn More

What Does Git Init Do? Learn More

Run git init in a folder and a hidden .git directory appears inside it. Objects, refs/heads, refs/tags and the template files all live in there, along with an initial branch that has no commits yet.

Nothing gets tracked until you run git add. The files already in the folder aren’t touched.

The initial branch is called master by default (git-scm manual, unchanged from Git 2.54.0 through the current Git 2.56.0, 2026), and the same manual says the default changes to main with Git 3.0.

Running it again inside an existing repository is safe, since nothing gets overwritten.

What Does git init Do?

YouTube player

It creates an empty repository, either in the current directory or in a folder you name. You end up with a hidden .git directory holding subdirectories for objects, refs/heads and refs/tags plus template files, and an initial branch with no commits on it (git-scm documentation, 2026).

Nothing gets staged, tracked, or committed at this point. Files already in the folder stay untracked until you run git add.

That .git folder is the only thing separating a plain directory from a full Git repository. Add, commit, status and log all read from it.

Pass a directory name and the command runs inside that folder, creating it first if it doesn’t exist. Two environment variables change the layout. GIT\_DIR replaces ./.git as the base of the repository, and GIT\_OBJECT\_DIRECTORY moves object storage away from $GIT\_DIR/objects, with the sha1 directories created underneath.

According to the git-init manual (last changed in Git 2.54.0, April 2026), a new repository starts out like this:

  • sha1 for the hash algorithm
  • files for ref storage, meaning loose files plus packed-refs
  • master as the initial branch name
  • sample hooks copied from the template directory, every one of them disabled
  • /usr/share/git-core/templates as the default template directory

What Is Created Inside the .git Directory

YouTube player

Right after initialization, the entries in this table account for most of what’s in there.

Why is GitHub the heart of open source?

Uncover GitHub statistics: developer community growth, repository trends, collaboration patterns, and the platform that powers modern software development.

Explore GitHub Data →
EntryHoldsState after git init
HEADPointer to the current branchPoints at the initial branch, which has no commits
configRepository-level settingsCore settings written by init
hooks/Scripts that run on Git eventsSample files only, none active
objects/The object databaseEmpty apart from info and pack subdirectories
refs/Branch and tag pointersheads and tags folders, both empty

A description file (read by GitWeb) and an info/exclude file show up too. The exclude file carries the suggested patterns from the template.

The sample hooks do nothing until you rename them and drop the .sample suffix. That catches people who expect a pre-commit check to run out of the box.

There’s no index file right after init. Git writes it on the first git add, and from then on it acts as the staging area between your working files and the next commit.

How Is the Default Branch Name Set?

YouTube player

It’s master unless something overrides it. git init -b main handles a single repository, while the init.defaultBranch variable handles every new one.

Git 2.28 (July 2020) made the name configurable, and its release notes describe the goal as weaning Git off the hardcoded master. GitHub switched its own default to main on October 1, 2020 (thoughtbot, 2025).

Looking to sharpen your Git skills? Branching, merging, rebasing, and everything else you need - including git rebase, git stash, and commit workflows - is on one page in the Git Cheat Sheet.

Git itself took longer. The Git 2.52 notes (November 2025) declared that a plain git init uses main starting with Git 3.0, and thoughtbot reported no planned release date for that version at the time, with estimates pointing to the end of 2026.

As of Git 2.56.0, the current release, the git-init manual (unchanged since Git 2.54.0 in April 2026) still documents master as the fallback, and Git 3.0 has not shipped. So a local repository and a brand new GitHub repository can start with different branch names.

You can pick the name per repository with git init -b main, or the long form git init –initial-branch=main. To set it for the whole machine, run git config –global init.defaultBranch main. If you do neither, you get master, at least until Git 3.0 changes it to main.

The flag wins when you pass it. Without it the setting applies, and without the setting the fallback does.

Set the global value once in your Git configuration and every new repository on that machine follows it. Existing repositories keep whatever names they already have.

The initial branch is an ordinary Git branch, so git branch -m renames it later without touching history. Check the name before the first push to a new remote.

Which git init Options Change the Result?

YouTube player

The manual lists 8 options in total, among them –quiet, –bare, –template, –separate-git-dir, –object-format, –ref-format, –initial-branch and –shared (git-scm documentation, 2026). Most projects never need more than -b.

OptionEffectTypical use
–bareCreates a repository with no working treeCentral server location
–templateCopies files from a chosen template directoryShared hooks and exclude patterns
–separate-git-dirStores the repository elsewhere, leaves a pointer fileKeeping .git off a synced folder
–sharedSets group or world permissions on the repositorySeveral users pushing to one server path
–object-format, –ref-formatPicks hash algorithm and ref storageExperiments with sha256 or reftable

–bare sets GIT\_DIR to the current directory when the variable is empty. It suits a bare repository that people push to rather than edit in.

–shared falls back to group when you give it no value. Shared repositories also turn on receive.denyNonFastForwards, so a force push gets refused.

When Git looks for a template, it checks these places in order and stops at the first match:

  1. The directory given with –template
  2. The GIT\_TEMPLATE\_DIR environment variable
  3. The init.templateDir configuration variable
  4. The default template directory

Only files and folders whose names don’t start with a dot get copied into the new .git.

SHA-1 or SHA-256 for a New Repository

Stay on SHA-1 unless you control every machine and server that touches the repository. The manual lists sha1 as the default and offers sha256 only when the Git build includes it.

Git 2.29 (October 2020) added experimental SHA-256 repositories, and Git 2.45 (April 2024) added preliminary, limited interoperability with SHA-1, according to the GitHub Blog.

The git-init manual still states that SHA-256 and SHA-1 repositories do not interoperate. Both statements hold up, because they describe different stages. The interoperability work has started, but the manual’s advice for daily use is to treat the formats as separate.

The failure is easy to picture. Older Git versions can’t read a SHA-256 repository (Git hash function transition document), and a sha1-only host has nothing it can accept a push into.

Reftable and the Files Format

–ref-format takes files (the default) or reftable. Both store the same branch and tag data, and reftable simply replaces loose files and packed-refs.

  • The object format default can be set with init.defaultObjectFormat or the GIT\_DEFAULT\_HASH variable.
  • For the ref format, use init.defaultRefFormat or the GIT\_DEFAULT\_REF\_FORMAT variable.

In both cases the command-line option and the environment variable beat the config setting.

Is It Safe to Run git init Again?

YouTube player

Yes. The manual states that running git init in an existing repository will not overwrite what’s already there.

Git prints “Reinitialized existing Git repository” followed by the path. Commits, branches, and the config values you set all stay as they were.

People rerun it mostly to pick up templates added since the repository was created, or to move the repository to another path with –separate-git-dir.

The move leaves a text file in the old .git location that holds the path to the actual repository. Git treats that file like a filesystem-agnostic symbolic link.

A rerun makes a poor repair tool. It adds missing template files and doesn’t rebuild history, so a repository that lost its objects stays broken.

What to Run After git init

YouTube player

Getting from a freshly initialized folder to the first push is a short trip. Check status, write a .gitignore, stage, commit, add a remote, push.

  1. Run git status. It reports that there are no commits yet and lists every file as untracked.
  2. Create a .gitignore for build output, dependency folders, and local config files.
  3. Stage the project with git add .
  4. Record the first snapshot with git commit -m "Initial commit"
  5. Link a server with git remote add origin <url>
  6. Upload with git push -u origin <branch>, using the branch name your repository actually has.

Step 2 sits before step 3 on purpose. A .gitignore file only keeps untracked files out, and anything you stage once stays tracked until you remove it.

A remote is just a name plus a URL. Convention calls it origin, and the git remote command lists or changes it later.

The -u flag records the upstream branch, so later pushes and pulls need no arguments.

Create the remote repository empty. A README or license added on the host counts as a commit your local repository doesn’t have, and the first push gets rejected.

Same steps for GitHub, GitLab, and Bitbucket. Only the URL changes.

git init, git clone or gh repo create?

YouTube player

Use git init for a local folder with no remote, git clone when a remote repository already exists, and gh repo create when you want the GitHub repository built for you.

CommandStarting pointCreates a remoteHistory you get
git initA folder on your machineNoNone, empty initial branch
git cloneAn existing remote repositoryNo, it connects to oneFull history of the source
gh repo createNothing, or a local repository via –sourceYes, on GitHubWhatever you push with –push

git clone is built on git init. Atlassian’s Git tutorial states that clone calls init internally before it fetches anything.

What clone adds on top is the connection. The git-clone manual describes remote-tracking branches under refs/remotes/origin, plus the remote.origin.url and remote.origin.fetch settings, and the full behavior of git clone goes further than this article needs.

The two commands collide in one case. Cloning into an existing directory is only allowed when the directory is empty (git-clone manual, unchanged since Git 2.54.0), so a folder you already ran git init in rejects the clone.

For a local project with no remote yet, run git init and add a remote once one exists. If the remote is already there, clone it and skip git init entirely. And when you need a GitHub repository too, run git init first and then gh repo create.

With –source, gh expects an existing local repository, so git init still comes first. The GitHub CLI then folds steps 5 and 6 from the list above into one command, such as gh repo create my-project --private --source=. --push.

–remote names the new remote, and –push uploads the commits you already made (GitHub CLI manual, 2026).

When git init Is the Wrong Move

Skip it inside another repository, in a directory that’s far too broad, or for a project that already lives on a remote.

Running It Inside Another Repository

A folder with its own .git directory sitting inside a tracked project becomes an embedded repository. The next git add of the parent prints a warning that it’s adding an embedded git repository.

Git’s own hint explains the damage. Clones of the outer repository don’t receive the contents of the embedded one, because the parent records only a gitlink with mode 160000 (Beej’s Guide to Git).

One fastpages user hit this after git add .. The GitHub Action build then failed with a “No url found for submodule path” error until git rm --cached removed the entry (fast.ai forum thread).

To get out of it, you can unstage the path with git rm and the –cached flag and then delete the inner .git directory. Or you can declare it a real submodule with git submodule add and the repository URL.

Running It in the Wrong Directory

Your home folder, a drive root, Downloads, Desktop. Any of these is a bad place for it.

In each one, git status lists everything below as untracked and git add . stages the lot. Run pwd (or cd on Windows) before git init.

How to Undo git init

YouTube player

Delete the .git directory. The project files stay exactly as they are, and every commit stored in the repository goes with it.

On macOS and Linux that’s rm -rf .git. PowerShell uses Remove-Item -Recurse -Force .git, and Command Prompt uses rmdir /s /q .git.

Deleting the local directory doesn’t touch a remote you already pushed to. Check git log first if you’re not sure the folder holds nothing worth keeping.

git init FAQ

Does git init work the same on Windows?

Yes. The command behaves identically in Git Bash, PowerShell, and Command Prompt once Git for Windows is installed.

File Explorer hides the .git folder by default. Turn on hidden items to see it.

Is git init required before git add?

Yes, unless the folder is already a repository. Outside one, git add stops with a “not a git repository” error.

Cloning also creates a repository, so a cloned project needs no separate git init.

Does git init need an internet connection?

No. It runs entirely on your machine and contacts nothing.

You only need a network later, when you push to or fetch from a remote repository.

Can I run git init from VS Code?

Yes. The Source Control view shows an Initialize Repository button for a folder that isn’t a repository yet, and it runs git init on that folder.

Git must already be installed on the machine for the button to work.

What to Set Before Sharing a New Repository

Before anyone else touches a repository created with git init, decide on the branch name, object format, and template directory, in that order.

  • Every collaborator and CI job reads the branch name.
  • The host has to support whichever object format you pick.
  • Only your own machines ever see the template directory.

The order follows how many people and systems each choice reaches. Pinning main globally has a cost, which is drift. Repositories from your machine start with a different name than those from an unconfigured one.

Once Git 3.0 ships, the branch-name step drops out, because the fallback and the hosting platforms agree on main. That position holds against the current git-init manual (unchanged since Git 2.54.0 in April 2026, and still current in Git 2.56.0).

The next command in the sequence is the first commit, and what git commit records in each snapshot deserves a read before running it.

Bogdan Sandu

Stay sharp. Ship better code.

Every week: one curated article, one tool worth knowing, one tip you can use tomorrow. No noise, no padding.