Skip to content
academia.sh

Lesson 03 / 15

Creating and Configuring a Repository

Turning an empty directory into a repository, the contents of the `.git` directory, and the precedence of the system, user, and repository configuration layers.

Published Updated

In this lesson

  • Turns a directory into a repository and names the parts of the `.git` directory
  • Explains the three configuration layers and their precedence order
  • Determines which file a setting comes from
Contents

The previous two lessons built the model: history is a directed acyclic graph, and in a distributed system the whole of this graph stands locally. This lesson makes the model concrete — the course’s example repository, the sozluk project, is created, and the repository’s counterpart on disk is examined.

It is necessary to start with configuration, because a commit’s author is read from configuration, and the author information enters the commit’s identity.

Three Configuration Layers

Settings are kept in three separate files, and the narrower scope overrides the wider one:

Layer Scope Option
System All users on the machine --system
User All of a user’s repositories --global
Repository Only the repository currently in --local

If the same key is defined in more than one layer, the repository-level value takes effect. This makes possible the distinction “I generally work under this identity, but in this repository I use a different one.”

Three settings are made at the user level:

git config --global user.name "Deniz Kaya"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main

The first two fill in commits’ author field; if either is undefined, an attempt to write a commit stops with an error. The third sets the name of newly created repositories’ first branch. The branch concept is the subject of the Branching and Collaboration course; here it will appear only as a name visible in output.

Where settings are written can be read:

git config --list --global
user.name=Deniz Kaya
[email protected]
init.defaultbranch=main

Notice that a key name’s section and name parts are case-insensitive: init.defaultBranch was written, init.defaultbranch was listed. Values, however, are preserved letter for letter.

Fixing the Date

A commit’s identity is computed not only from its content but also from its author and date; if the same file is committed at a different moment, a different hash comes out. So that your output matches the lessons in this course, two environment variables that fix the author and committer dates are provided:

export GIT_AUTHOR_DATE="2024-03-04 09:12:00 +0300"
export GIT_COMMITTER_DATE="2024-03-04 09:12:00 +0300"

The variables are valid only in the terminal session that is open. Because commits in later lessons are spread across different days, the date is written again before each commit; if you skip fixing it, everything works, only the hashes and dates differ from those shown in the lessons.

Creating the Repository

A repository is set up inside an existing directory:

mkdir sozluk
cd sozluk
git init

The command writes a single-line confirmation: the phrase Initialized empty Git repository in is followed by the full path of the created .git directory. Because the path depends on the directory the command was run in, it differs on every system.

From this moment on, the directory is a repository. The directory’s visible content has not changed; the only thing added is a hidden subdirectory named .git. The repository is that subdirectory itself: if .git is deleted, the files remain, history is gone.

The Contents of the .git Directory

ls .git
branches
config
description
HEAD
hooks
info
objects
refs

Four of the eight entries will be in constant use throughout this course:

  • objects — the object database. The content-addressed storage covered in the previous lesson takes place here: file contents, directory images, and commits sit in this directory as files named by their hash values. It is empty in a new repository.
  • refs — refs that bind human-readable names to object identities. refs/heads holds branch tips, refs/tags holds tags. Both are empty at the start.
  • HEAD — a single-line file pointing to the ref currently being worked on.
  • config — the repository’s own configuration; the narrowest of the three layers.

The remaining four are used more rarely: hooks holds scripts to be run on certain events, info holds repository-specific helper files, description holds a description text that only some server interfaces read; branches is a remnant of an old layout and is not used in this course. This list was taken on Linux with GNU ls and a UTF-8 locale: the order depends on the locale (with LC_COLLATE=C, HEAD comes first) and the number of entries can vary with the git version. Your output may differ slightly from this list; the four core entries are present everywhere.

The HEAD file can be read directly:

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

The content is not an object identity but a reference to a reference. Because no commit has been written yet, the refs/heads/main file does not exist either; HEAD points to a target that does not exist. This temporary inconsistency will be resolved once the first commit is written.

The Repository’s Own Configuration

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

These values are read from the system and written when the repository is created. repositoryformatversion states the repository format’s version, bare whether the repository has a working directory, logallrefupdates whether ref changes are logged. The output above was taken from a Linux system; on file systems that do not distinguish case in file names (the usual configurations of macOS and Windows), an ignorecase = true line is also written here, and on macOS precomposeunicode = true is added as well.

To write a setting at the repository level, the command is run from inside the repository, with the --local option:

git config --local user.email "[email protected]"

This line affects only commits in the sozluk repository; the user-level value stays in effect in other repositories. That is how the layers override one another; since all commits in this course will be written under a single identity, remove the repository-level setting right away:

git config --local --unset user.email

How the Repository’s Root Is Found

Commands are not required to be run from the repository’s root directory. When a command runs, .git is searched for upward, starting from the directory currently in; the first .git found determines the repository’s root.

To try it, two empty directories are created inside the repository and the innermost one is entered:

mkdir -p temp/sub
cd temp/sub
git rev-parse --show-toplevel

{/* mask: (?<=/tmp/ders-)[a-z0-9_]+ */}

/tmp/ders-p9bhqa2r/sozluk

The output is the repository’s root directory’s full path; on your own machine the start of the path will differ (the /tmp/ders-… part here is the temporary directory in which this lesson was measured). Run from a subdirectory, it returns the same value; where the root is does not change based on where you are. The relative path between the root and where you are can also be asked for separately:

git rev-parse --show-prefix
temp/sub/

After the experiment, you return to the root and the empty directories are removed:

cd ../..
rmdir temp/sub temp

Because the search goes upward, running a second git init inside a repository does not give the expected result: a new .git is created, and that subtree breaks off from the outer repository. Separate mechanisms exist for cases that require nested repositories; this course will use a single repository.

A command run outside a repository gives a clear error:

fatal: not a git repository (or any of the parent directories): .git

Finding Where a Value Comes From

Which layer an unexpected setting comes from can be asked directly:

git config --list --show-origin --global

{/* mask: (?<=/tmp/ders-)[a-z0-9_]+ */}

file:/tmp/ders-p9bhqa2r/.ev/.gitconfig	user.name=Deniz Kaya
file:/tmp/ders-p9bhqa2r/.ev/.gitconfig	[email protected]
file:/tmp/ders-p9bhqa2r/.ev/.gitconfig	init.defaultbranch=main

At the start of every line is the file the value was read from; the path above is the temporary home directory in which the lesson was measured, and in your output the .gitconfig file in your own home directory appears. If the layer option is removed, all three layers are listed merged together, and it is possible for the same key to appear on more than one line; in that case, the last line is the one that takes effect.

A single key’s effective value is asked for like this:

git config user.name
Deniz Kaya

Initial Status

git status
On branch main

No commits yet

nothing to commit (create/copy files and use "git add" to track)

Three lines give three separate pieces of information. The first is the name of the ref that the HEAD file points to. The second says that the refs/heads/main ref has not yet been bound to an object. The third reports that nothing in the working directory is ready to be recorded.

git status is the most frequently run command in this course. It answers the same question every time: what differences exist between the working directory, the staging area, and the last commit? The next topic will define these three regions one by one.

Summary

  • Configuration is kept in system, user, and repository layers; a narrow scope overrides a wide one.
  • If user.name and user.email are not defined, a commit cannot be written; these values enter the commit’s identity.
  • git init creates the .git subdirectory in an existing directory; the repository is that subdirectory.
  • .git/objects holds the object database, .git/refs holds named refs, .git/HEAD holds the ref currently being worked on.
  • --show-origin shows which file a setting comes from.

Next Step

The repository is set up, but empty. What happens when a file is created — does it go straight into history, or is there an intermediate stage? The next lesson will define the staging area that stands between the working directory and the object database, and show that area’s counterpart inside .git.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ ↵ Esc navigate · open · close