Chapter 2. Getting Started

In this chapter, you’ll get started working with Git by setting your defaults and preferences, and learn the basics of creating a repository and adding initial content to it.

Basic Configuration

Before starting in with Git, you’ll want to set a few basic parameters using git config. This command reads and changes Git configuration at the repository, personal, or system level. Your personal Git configuration is in ~/.gitconfig; this is a plain-text file, which you can edit directly as well, if you like. Its format is called INI style (after a file extension commonly used for it, though not by Git), and is divided into sections, like so:

[user]
        name = Richard E. Silverman

[color]
        ui = auto # overall default for color usage

[mergetool "ediff"]
        trustExitCode = true

Comments are introduced with a hash sign (#) as shown, as is common in Unix configuration files. The parameters have full names qualified by the section in which they appear using a dot; for example, the parameters mentioned in this example are:

  • user.name
  • color.ui
  • mergetool.ediff.trustExitCode

You use these names when reading or setting parameters with git config, rather than editing the file yourself. To set a parameter with git config:

$ git config --{local,global,system} parameter value

If you give this command when your current directory is inside a Git repository, it implies --local, and it will change the configuration for that repository only, in the file .git/config. Otherwise, the default is --global, which applies to your overall personal Git configuration in ~/.gitconfig. The --system option changes the system-wide configuration on the machine you’re logged into, which applies to all users; its location may vary, but is usually /etc/gitconfig. This file is usually writable only by a system administrator, so you’d need to be root to run this command to make a change. It’s not common to do that anyway; usually this file would be maintained separately, perhaps using a configuration management system such as Puppet or Chef.

Git reads these three configurations, each if available, in the order system, global, then local. Settings made in a later configuration override those from an earlier one so that, for example, you can set your normal email address with --global but change it for commits made in a specific repository if you use a different address when corresponding about that work.

Parameters that take Boolean (yes/no) values can be given as yes/no, true/false, or on/off.

See git-config(1) for more detail on the format of the configuration files, its many parameters (some mentioned in this text and some not), and other uses of git config, such as querying the current setting of a parameter.

Personal Identification

Git will guess your name and email address from the environment, but those may vary from one computer to another and may not be what you want. To set them:

$ git config --global user.name "Richard E. Silverman"
$ git config --global user.email res@oreilly.com

If you use the same ~/.gitconfig in multiple contexts, say at home and at work, then this may be inconvenient. Git will take your email address from the EMAIL environment variable before resorting to a guess, so you can leave it out of your Git configuration and set EMAIL appropriately in the different contexts, usually with your shell startup files, such as .bashrc, .profile, .cshrc, and so on. There are other environment variables for finer control as well, such as GIT_AUTHOR_NAME and GIT_COMMITTER_EMAIL; these refer to the fact that Git maintains a distinction between the author of a change and the person who committed it. See git-commit-tree(1) for details, as well as Defining Your Own Formats.

Text Editor

When you use git commit, you supply some free-form text, which is included in the commit; this is the “commit message.” You can give this on the command line with the -m switch, but you can also use your favorite text editor to compose the message instead. If you omit the -m switch, Git starts a text editor to let you write your message. The default editor varies by platform; on Unix, it is the ubiquitous vi. You can customize this with the environment variables GIT_EDITOR, EDITOR, or VISUAL (the latter two are respected by many other Unix programs as well), or by setting core.editor. For example (reflecting the author’s predilections):

$ git config --global core.editor emacs

Git uses the first of these variables it finds in the order given.

Commit ID Abbreviation

When referring directly to an object identifier, it is usually not necessary to quote the entire 40-character hexadecimal SHA-1 value; any initial substring unique to the current context will do. You can tell Git to abbreviate commit IDs generally with:

$ git config --global log.abbrevCommit yes
$ git config --global core.abbrev 8

This improves readability in various places, especially in log output such as:

$ git log --pretty=oneline
222433ee Update draft release notes to 1.7.10
2fa91bd3 Merge branch 'maint'
70eb1307 Documentation: do not assume that n -> 1 in …
...

where the commit messages would otherwise be pushed halfway off the screen to the right by the full identifiers. core.abbrev is the length of the shortened identifiers in digits; the default is 7 in most cases. To see the full identifiers as a per-command exception, use --no-abbrev-commit. Note that when you’re quoting commit IDs in a public or “for the record” context, it may be best to use the full ID, to avoid any future ambiguities.

Pagination

Git will automatically pipe output from many commands such as git log and git status to less(1) for pagination; you can select a different program with the core.pager variable (or the environment variable GIT_PAGER), and disable pagination entirely by setting this to be simply cat (or something equivalently transparent). You can control pagination on a per-command basis by setting a Boolean pager.command, e.g., pager.status for git status (this can also be the name of the pager program to use for this specific command). You may also want to read the git-config(1) section on core.pager, which discusses specific things Git does with the LESS environment variable to affect the behavior of less(1).

Color

Many Git commands, including diff, log, and branch, can use color to help you interpret their output, but these options are mostly off by default. To enable the use of color generally, set:

$ git config --global color.ui auto

(ui stands for “user interface.”) This will turn on most color options when Git is talking to a terminal (tty/pty device). You can then turn off color for individual commands if you prefer; for example, to disable it for git branch (but leave it on for other functions):

$ git config --global color.branch no

Git’s use of color is very configurable, down to defining new color names, specifying terminal control sequences, and using color in custom log formats. See git-config(1) and git-log(1) for details.

Cryptographic Keys

Git can use GnuPG (“gpg”) to cryptographically sign tags and commits in order to verify the authenticity of sensitive assertions such as, “This tagged commit contains the version 3.0 source code.” See git tag for more on signing tags. By default, Git passes your name and email address to GnuPG to select the signing key. If the combination of your Git and GnuPG settings doesn’t select the correct key, you can set it explicitly with:

$ git config --global user.signingkey 6B4FB2D0

You can use any key identifier GnuPG supports; 6B4FB2D0 happens to be an ID for the author’s personal key. You can also use one of the email addresses bound to the key you want, if it’s unique among your keys.

Command Aliases

Most systems provide a way to abbreviate long commands with user-defined command aliases; for instance, using alias in your Unix bash shell startup file ~/.bashrc. Git has its own internal alias system as well, which may be more convenient. This command:

$ git config --global alias.cp cherry-pick

defines git cp as an alias for git cherry-pick. An exclamation point means to pass the alias definition to the shell, letting you use more complex aliases; for example, this definition in ~/.gitconfig:

[alias]
    setup = ! "git init; git add .; git commit"

defines an alias git setup, which sets up a new repository using the contents of the current directory.

More generally, whenever you type git something, if something is not a built-in command or defined alias, Git searches its installation path (often /usr/lib/git-core) and then your own search path for a program named git-something. So, you can make your own Git command git foo just by placing a program in an executable file named git-foo somewhere on your personal path (usually, the value of the PATH environment variable).

Getting Help

You can get help with a Git command or feature using Git itself, for example:

$ git help commit

This displays the documentation for the command git commit. On Unix systems, this documentation is available via the usual man page system as well; this is equivalent:

$ man git-commit

References

  • git-init(1)
  • git-commit-tree(1)
  • git-config(1)
  • git-log(1) [“Pretty Formats”]

Creating a New, Empty Repository

The command:

$ git init directory

creates the argument directory if needed, and a directory named .git inside it holding a new, empty Git repository. Aside from the repository itself in .git, that directory will hold the working tree: copies of the files and directories under version control that you will edit. The .git directory holds the files and data structures that form the repository itself, including the database of all historical revisions of all project files. Unlike CVS and (until recently) Subversion, there is no control directory in each directory of the working tree (CVS and .svn); there is just the one .git directory at the top of the project tree.

The default with no argument is the current directory; that is, a simple git init creates a new .git in the current directory.

git init is a safe command. It will not remove any existing files in the target directory, the usual pattern being that you are about to add those files to the new repository. It will also not damage an existing repository, even though it gives a somewhat heart-stopping message about “reinitializing” if you do it; all this actually does is make some administrative updates, such as picking up new templates for “hook” scripts made available by the system administrator (see Git Hooks).

Selected Options

--bare
Creates a “bare” repository; that is, one without an associated working tree. The internal repository files that would otherwise be inside .git are instead created in the target directory itself, and certain repository options are set differently, principally core.bare = yes. A bare repository usually serves as a point of coordination for a centralized workflow, in which several people push and pull from that repository rather than directly among themselves; no one works with the bare copy directly.
--shared

Sets group ownership, file permissions, and options to support multiple Unix accounts pushing into a non-bare repository. The normal expectation is that if someone wants to send you an update to a project you’re both working on, she will ask you to pull from her repository, so that one way or another you are the only person who ever actually modifies your repository. The usual file permissions reflect this, allowing only you to modify the repository files. The --shared option arranges permissions to allow others in a common Unix group to push directly into your repository, as well as pull from it. There are several settings for this option, manipulating the details of group ownership, file permissions, interactions with people’s umask settings, and so on; see git-init(1) for details.

This arrangement isn’t much used, though; ordinarily you and your coworker would simply pull from one another’s repositories into your own, or push to a shared bare repository. Pushing into a nonbare repository is awkward, because it will fail if you try to push the branch that is currently checked out in the remote (since that could invalidate the remote’s working tree and index at any moment). A bare repository doesn’t have a working tree or index (since no one is using it directly), and so does not have this limitation.

The .git Directory

Though the repository is usually stored in a directory named .git at the top of the working tree, there are ways to locate it elsewhere: you can use git --git-dir directory to refer explicitly to another loction, or set the environment variable GIT_DIR. For simplicity and to match the common case, we will generally just refer to .git when talking about the repository directory.

Importing an Existing Project

These commands create a new repository and add all content in the current directory to it:

$ git init
$ git add .
$ git commit -m 'Begin Project Foo!'

To illustrate:

$ cd hello
$ ls -l
total 12
-rw-r-----  1 res  res   50 Mar  4 19:54 README
-rw-r-----  1 res  res  127 Mar  4 19:53 hello.c
-rw-r-----  1 res  res   27 Mar  4 19:53 hello.h
$ git init
Initialized empty Git repository in /u/res/hello/.git/
$ git add .
$ git commit -m 'Begin Project Foo!'
[master (root-commit) cb9c236f] Begin Project Foo!
3 files changed, 13 insertions(+)
create mode 100644 README
create mode 100644 hello.c
create mode 100644 hello.h

This creates a new Git repository .git in the current directory, and adds the contents of the entire directory tree rooted there to the repository as the initial commit on a new branch named master:

$ git branch
* master
$ git log --stat
commit cb9c236f
Author: Richard E. Silverman <res@oreilly.com>
Date:   Sun Mar 4 19:57:45 2012 -0500
Begin Project Foo!
README  |    3 +++
hello.c |    7 +++++++
hello.h |    3 +++
3 files changed, 13 insertions(+)

In more detail: git add . adds the current directory to the (initially empty) index; this includes files as well as directories and their contents, and so on, recursively. git commit then creates a new tree object capturing the current state of the index, as well as a commit object with your comment text, personal identification, the current time, and so on, pointing to that tree. It records these in the object database, and then finally sets the master branch to the new commit; that is, makes the ref refs/heads/master point to the new commit ID:

$ git log --pretty=oneline
cb9c236f Begin Project Foo!
$ git show-ref master
cb9c236f refs/heads/master

git log shows the ID of the most recent (and right now only) commit, and git show-ref master shows the commit ID currently referred to by the branch master; you can see that they are the same.

Ignoring Files

While you’re working on a project, you may have files in your working directory that you want Git to simply ignore. If it’s a small project in an interpreted language, this may not happen so much, but it’s definitely an issue for projects that produce compiled code of any sort, or use tools like autoconf, or generate documentation automatically in various formats. Such files include:

object code
*.o, *.so, *.a, *.dll, *.exe
bytecode
*.jar (Java), *.elc (Emacs Lisp), *.pyc (Python)
toolchain artifacts
config.log, config.status, aclocal.m4, Makefile.in, config.h

Generally speaking, anything that is automatically generated you probably don’t want tracked by Git, and you don’t want Git constantly including them in listings or complaining about them either. Git looks at three different kinds of files to determine what to ignore, in order:

  1. Files named .gitignore in your working tree. This is just another file to Git as far as content is concerned—it will list it as “untracked” if it’s present but not in the repository—and so you normally add it to the repository content; thus, if this is shared work, you should only put things there that make sense for other people to ignore as well. You can actually put .gitignore in a .gitignore file, and cause it to ignore itself. All .gitignore files in the current and containing directories in the repository are read, with rules in files closer to the current directory overriding those in files farther away.
  2. The per-repository file .git/info/exclude. This is part of your repository configuration, but not part of the repository content, so unlike a tracked .gitignore file, it is not automatically foisted upon people who clone your project. This is a good place to put things you find convenient to ignore for this project, but about which others might disagree (or if you simply decide not to use .gitignore files as a matter of policy, to avoid confusion).
  3. A file named by the configuration variable core.excludesfile, if you set it. You might do this:
$ git config --global core.excludesfile ~/.gitignore

and keep a set of ignore patterns there that you want Git to always observe. That’s assuming your home directory is not itself inside a Git repository, of course, in which case you might want to name the file something else (and ask yourself if you don’t perhaps like Git just a bit too much).

Syntax of “Ignore Patterns”

See gitignore(5) for precise details; generally speaking, an ignore file uses shell “glob” patterns and comments in the following fashion. Note the use of the exclamation point to introduce a negated pattern, overriding subcases of an earlier pattern. Git reads all patterns in a file to determine the disposition of a given path, rather than stopping at the first match, and the last matching line is the one that applies:

# Ignore this specific file in a subdirectory.
conf/config.h

# Ignore this specific file in the current directory.
# (not “./”)
/super-cool-program

## Patterns without slashes apply everywhere in this
## directory and below.

# Ignore individual objects and object archives
# (*.o and *.a).
*.[oa]

# Ignore shared objects...
*.so

# ... but don't ignore this file, or my boyfriend
# will complain.
!my.so

# Ignore any directories named “temp,” but still
# notice regular files and symbolic links with
# that name.
temp/

In .git/info/exclude or your core.excludesfile, the “current directory” indicated earlier is the top of the working tree.

Note that all this applies only to untracked files; you cannot tell Git to ignore changes to a tracked file this way. The command git update-index --assume-unchanged is useful for that.

Note

Shell “globs” are simple patterns, not as powerful as regular expressions; Git uses them to indicate sets of files and refs. There are many slightly different versions of the glob syntax, as it has been around for a long time; the one used in Git is documented in the fnmatch(3) and glob(3) man pages. Simply put: * matches a sequence of characters not containing /; ? matches a single character (again not /); and [abc] matches one character, which must be either a, b, or c.

Get Git Pocket Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.