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/headsholds branch tips,refs/tagsholds 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.nameanduser.emailare not defined, a commit cannot be written; these values enter the commit’s identity. git initcreates the.gitsubdirectory in an existing directory; the repository is that subdirectory..git/objectsholds the object database,.git/refsholds named refs,.git/HEADholds the ref currently being worked on.--show-originshows 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.