Your First Repository
Setting up Git and making a first commit: installing and configuring Git, creating or cloning a repository, and the everyday cycle of status, add and commit.
Installing Git
Git is a command-line program on every platform; graphical tools and editors such as Visual Studio Code use the same program underneath. Install it from your platform's usual source:
- Linux: from the distribution's package manager. On many servers it is already installed.
-
Windows: Git for Windows, from git-scm.com or
with
winget. It includes Git Bash, a Bash shell in which the commands on these pages work as shown, and Git Credential Manager, which stores your login for hosting services securely. -
macOS: Apple's command line developer tools
include Git (running
gitfor the first time offers to install them); Homebrew provides a more recent version.
$ sudo apt install git # Debian, Ubuntu
$ sudo dnf install git # Fedora, RHEL and derivatives
$ winget install --id Git.Git -e # Windows
$ xcode-select --install # macOS: Apple's developer tools
$ brew install git # macOS with Homebrew
$ git --version
git version 2.56.0
Distributions often ship an older Git than the newest release. That is fine for everything in this topic: the examples here run on Git 2.39.
Configuring Git
Git configuration is a set
of key–value settings, such as user.name or
init.defaultBranch, stored in plain text files at three
levels. You read and change them with git config.
| Level | Option | File (Linux) | Applies to |
|---|---|---|---|
| System | --system |
/etc/gitconfig |
Every user on the machine |
| Global | --global |
~/.gitconfig (or
~/.config/git/config)
|
All repositories of your user account |
| Local | --local (the default when writing) |
.git/config in the repository |
That one repository |
When the same key is set at several levels, the most specific one
wins: local overrides global, global overrides system. On Windows
the global file is in your user profile folder
(%USERPROFILE%\.gitconfig).
Setting your identity
The two settings you need before your first commit are your name and
email address. Set them once, globally; reading a key back is
git config <key> (or the more explicit
git config --get <key>):
git config --global user.name "Sam Rivera"
git config --global user.email sam@example.com
git config user.name
git config --get user.email
Sam Rivera sam@example.com
A repository can override the global value — useful when you commit
to a work repository and a personal one from the same account.
Without --global, git config
writes to the local level of the repository you are in:
git init -q net-tools
cd net-tools
git config user.email sam.rivera@example.com
git config --global user.email
git config --local user.email
git config user.email
sam@example.com sam.rivera@example.com sam.rivera@example.com
The last command shows the value Git will actually use: the local
one. To see every setting and which file it came from, run
git config --list --show-origin — the first thing to
try when Git does something you did not configure.
Why your identity matters
Name and email are copied into every commit as its author. They are
what git log shows, and hosting platforms such as
GitHub use the email address to link a commit to an account. If you
forget to set them, Git refuses to commit or guesses from your login
and machine name, producing commits by something like
sam@laptop-04.localdomain that nobody can trace.
The default branch name
A new repository starts with one branch. Its name comes from the
setting init.defaultBranch (available since Git 2.28).
When the setting is missing, current Git 2.x versions use
master and print a hint explaining how to choose a
name:
$ git init net-tools
hint: Using 'master' as the name for the initial branch. This default branch name hint: is subject to change. To configure the initial branch name to use in all hint: of your new repositories, which will suppress this warning, call: hint: hint: git config --global init.defaultBranch <name> hint: hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and hint: 'development'. The just-created branch can be renamed via this command: hint: hint: git branch -m <name> Initialized empty Git repository in /home/sam/net-tools/.git/
(This is Git 2.39's wording. Since Git 2.52 the hint says the
default will change to main in Git 3.0.)
Most teams and hosting services now use main, and Git
3.0 is planned to make it the built-in default. Set it once globally
(git config with a value sets it; without a value it
prints the current one), or choose a name for one repository with
-b:
git config --global init.defaultBranch main
git config --global init.defaultBranch
git init -q net-tools
git -C net-tools branch --show-current
git init -q -b trunk lab
git -C lab branch --show-current
main main trunk
(git -C <folder> runs a command as if you were in
that folder.) More about the default branch and what a branch is in
Branches.
Creating a repository with git init
git init turns a folder — empty or full of existing
files — into a repository by creating a hidden
.git folder in it. That folder is the
repository: the history, the branches and the local configuration.
Nothing else changes, and no files are tracked yet. The examples use
-q (quiet) to suppress the "Initialized empty Git
repository" message, because it contains the full path of the
folder.
git init -q net-tools
cd net-tools
ls -A
git status --short
.git
Everything in the project folder outside .git is the
working tree: the files you see,
edit and run, as they currently are on disk. Git compares the
working tree with the last commit to find out what you changed. (Do
not confuse it with the shell's
working directory, the folder you are in.) What is inside .git is
explained in
How Git Stores Your Work.
Copying a repository with git clone
More often you start from a repository that already exists. A
clone is a complete, independent copy
of a repository — every commit and tag, with the source's branches
available as remote-tracking branches (origin/…) —
together with a working tree checked out at the default branch, and
a remote called origin that remembers where it came
from.
On these pages the "server" is a bare repository (a repository without a working tree, used only for sharing) in the same folder. Sam creates it, adds a first commit and pushes it; then Amara clones it:
git init -q --bare shared.git
git clone -q shared.git sam 2>/dev/null
cd sam
printf '# Network tools\n' > README.md
git add README.md
git commit -q -m "Add README"
git push -q origin main
cd ..
git clone -q shared.git amara
ls amara
git -C amara log --oneline
README.md e9f93d2 Add README
(The first clone is of an empty repository, which makes Git print a
warning; 2>/dev/null hides it.) Amara now has the
same commit, with the same hash, as Sam. In real life the address is
a URL on a hosting service, over HTTPS or SSH:
$ git clone https://github.com/example-org/net-tools.git
$ git clone git@github.com:example-org/net-tools.git # over SSH
$ git clone git@github.com:example-org/net-tools.git tools # into the folder tools
SSH clones need an SSH key pair registered with the host. Remotes, fetch and push are covered in Remotes and Collaboration.
git init |
git clone |
|
|---|---|---|
| Starts from | Nothing (or existing, untracked files) | An existing repository |
| History after the command | Empty: no commits yet | The complete history of the source |
| Remote | None until you add one | origin, pointing at the source |
| Typical use | A brand-new project | Joining a project, setting up a new laptop |
The everyday cycle: status, add, commit
Making a commit is a two-step process: you first
stage the changes you want with git add, then
record them with git commit. In between, and whenever
you are unsure, you ask git status. Here is each step
with the exact output.
1. A new file: git status
git init -q net-tools
cd net-tools
printf '# Network tools\n' > README.md
git status
On branch main No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) README.md nothing added to commit but untracked files present (use "git add" to track)
Git sees the file but does not track it yet. Read the hints in
parentheses: git status tells you the next command.
2. Staging: git add
git init -q net-tools
cd net-tools
printf '# Network tools\n' > README.md
git add README.md
git status
On branch main No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: README.md
git add copied the current content of
README.md into the staging area: the proposal for the
next commit. If you edit the file again now, the staged copy does
not change — you would need another git add. The
staging area is explained in detail in
Staging and Committing.
3. Recording: git commit
git init -q net-tools
cd net-tools
printf '# Network tools\n' > README.md
git add README.md
git commit -m "Add README"
git status
[main (root-commit) e9f93d2] Add README 1 file changed, 1 insertion(+) create mode 100644 README.md On branch main nothing to commit, working tree clean
The commit summary shows the branch (main), that this
is the first commit (root-commit, it has no parent),
its short hash and its message. create mode 100644
means a new, ordinary file that is not executable. "Working tree
clean" means the files on disk match the last commit exactly.
Without -m, git commit opens a text editor
for the message (configure which one with
git config --global core.editor nano, for example);
saving and closing the editor completes the commit, and an empty
message cancels it.
Tracked and untracked files
Every file in the working tree is in one of two groups. A
tracked file is one that Git
knows about: it was in the last commit or has been staged. Git
watches it and reports when it is modified. An
untracked file is in the
working tree but was never added; Git lists it but leaves it out of
every commit until you git add it. (A third group,
ignored files, are untracked files that a
.gitignore
tells Git not to mention at all.)
git init -q net-tools
cd net-tools
printf '# Network tools\n' > README.md
git add README.md
git commit -q -m "Add README"
printf 'Scripts and configs for the network team.\n' >> README.md
printf 'all:\n hosts:\n r1:\n ansible_host: 192.0.2.1\n' > inventory.yml
git status
On branch main Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: README.md Untracked files: (use "git add <file>..." to include in what will be committed) inventory.yml no changes added to commit (use "git add" and/or "git commit -a")
README.md is tracked and modified;
inventory.yml is new and untracked. Both need a
git add before the next commit will include them. The
hint about git restore is a way to throw your edits
away — see Undoing Things before
you use it.