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 git for 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.