Font conventions:
-
italics is used for program names, emphasis, and definition of technical terms
-
monospace
is used for file names, code, program output, and commands/text to be typed by user
IMPORTANT admonitions are used to emphasize critical points.
NOTE admonitions are used to provide additional information that may be useful, but are not part of the main discussion.
Note
|
The output in some examples may differ in small ways on the
version of Git being used. These notes were prepared using version
2.26.2 .
|
Note
|
Extra blank lines have be added between commands in the listings to improve readability. Also the colorization of the Git output has been lost. |
$ pwd /Users/whimwich/Git/hello $ ls Makefile hello.c $ cat hello.c #include <stdio.h> int main() { printf("Hello, World!\n"); return 0; } $ cat Makefile hello: hello.o $ make cc -c -o hello.o hello.c cc hello.o -o hello $ ./hello Hello, World!
Use git init
to start
$ git init Initialized empty Git repository in /Users/whimwich/Git/hello/.git/
All the Git data is in the .git
sub-directory:
$ tree .git .git ├── HEAD ├── config ├── description ├── hooks │ ├── applypatch-msg.sample │ ├── commit-msg.sample │ ├── fsmonitor-watchman.sample │ ├── post-update.sample │ ├── pre-applypatch.sample │ ├── pre-commit.sample │ ├── pre-merge-commit.sample │ ├── pre-push.sample │ ├── pre-rebase.sample │ ├── pre-receive.sample │ ├── prepare-commit-msg.sample │ └── update.sample ├── info │ └── exclude ├── objects │ ├── info │ └── pack └── refs ├── heads └── tags 8 directories, 16 files
Note
|
You can live a quite happy Git life and never delve into the
.git sub-directory. You may need to use it for some advanced
operations. The most likely things to be useful are config and
refs . The main store of data is in the objects directory.
|
Note
|
If you delete the .git sub-directory, you have removed Git and
all the history from your project.
|
$ git status On branch main No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) Makefile hello hello.c hello.o nothing added to commit but untracked files present (use "git add" to track)
The git status
output is fairly verbose, including giving suggestions for what you might want to do next.
The presence of untracked files is a problem because they represent either content that you have not added to Git yet or files you don’t want keep track of. In the latter case, this usually includes binary files. Typically, you don’t want to see any untracked files.
Git works best with ASCII files. It can store binary files when needed, but it is most useful, and efficient, for source files. It is usually better to ignore binary files.
$ cp /tmp/.gitignore . $ cat .gitignore *.o hello $ git status On branch main No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) .gitignore Makefile hello.c nothing added to commit but untracked files present (use "git add" to track)
Note
|
The .gitignore file has a syntax to allow fine-grained control
of what is ignored. The basic syntax, as shown in the example above,
is fairly simple.
|
Add the files that aren’t ignored to the index. The use of .
adds
all files in the current directory and below.
$ git add . $ git status On branch main No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: .gitignore new file: Makefile new file: hello.c
Now the files are no longer untracked, but are staged to be committed. The .gitignore
file is just another file to be tracked by Git.
Note
|
The status command gives hints about how to undo an action.
This is just one example of the many ways Git has to undo what has
already happened.
|
$ git commit -m "Initial commit" [main (root-commit) bdca79e] Initial commit 3 files changed, 8 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 hello.c
Note
|
If you leave out the -m and the commit message, Git will
schedule an editor (vi by default) so you can compose a, possibly
multi-line, message. The comments in the message (lines starting
with # ) are only informational. If you close the file
without adding anything, Git will abort the commit.
|
$ git status On branch main nothing to commit, working tree clean
All clean!
The history comes out in reverse (mostly chronological) order. Of course that doesn’t matter when you only have one commit.
$ git log commit bdca79e9499918240c27ff43c6598b26db6c0893 (HEAD -> main) Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:31:42 2020 -0400 Initial commit
Note
|
The commit line shows the SHA of the commit (see
Git tracks content
below) and that this is the HEAD commit and the tip of
the main branch.
|
The log is automatically piped through less, which can be used to search the output.
This whole sub-section is an aside.
$ git log --stat commit bdca79e9499918240c27ff43c6598b26db6c0893 (HEAD -> main) Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:31:42 2020 -0400 Initial commit .gitignore | 2 ++ Makefile | 1 + hello.c | 5 +++++ 3 files changed, 8 insertions(+)
Since the output is piped through less, you search it for file names to see when they change.
$ git log -p commit bdca79e9499918240c27ff43c6598b26db6c0893 (HEAD -> main) Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:31:42 2020 -0400 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..59b32b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +hello diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..da75d00 --- /dev/null +++ b/Makefile @@ -0,0 +1 @@ +hello: hello.o diff --git a/hello.c b/hello.c new file mode 100644 index 0000000..03bde36 --- /dev/null +++ b/hello.c @@ -0,0 +1,5 @@ +#include <stdio.h> +int main() { + printf("Hello, World!\n"); + return 0; +}
Since the output is piped through less, you search it for code (and file names, etc.).
A repository (repo for short) is a database that represents the
history of the project as a set of commits (see Commits below). It
is stored in the .git
sub-directory, along with other things, like
the index (see below).
So what is so great about having all that history? Among other things this means that every released version of the project, even every individual file version, is preserved and can be recovered. That is helpful for a lot of reasons. One is that you can potentially undo changes if you regret them later. Another is that you can research the origin of bugs to help figure out better ways to fix them. The tools in Git are designed to be useful for examining and manipulating the database in a targeted and non-destructive way.
Every object stored by Git (files, commits, trees, etc.) is identified by its SHA1 (or sometimes just SHA) hash. This is a cryptographic 160-bit (shown as 40 hex characters) digest of the object. It represents the content of the file. There are about 1048 possible values. The value is essentially unique for every unique object in your repository. In fact, it is essentially unique for every unique object in all repositories. So if two objects, say files, in two different repositories have the same SHA1, they can be expected to have the same content.
A commit consists of:
-
the hash of the tree that represents the files (and directory structure)
-
author information: name, e-mail, time
-
committer information: name, e-mail, time
-
commit message
-
the hash(es) of parent commit(s)
that corresponds to the state of the project at that point. It also is represented by its SHA1 hash, which as said before, is essentially unique. So if two commits in two repositories have the same SHA1, they can be expected to have the same content (including history) to that point.
Note
|
The commit where you are working is referred to as the HEAD commit
and is usually the tip of a branch (see Branches below). If it is
not the tip of a branch, it is a detached HEAD .
|
A branch, or branch pointer, is a reference to a specific commit.
The term branch is also used to refer to the sequence of commits
that ends with the commit pointed to by a branch pointer. These
sequences of commits represent lines of development. The most recent
commit on a branch, pointed by the branch pointer, is referred to as
the tip. The main branch is usually called main
(older versions of
git may use a different default branch name); it usually has the
main line of development. There can be other branches for various
uses, including:
-
feature branches to develop new features
-
maintenance branches for fixes to an existing release.
Note
|
When you checkout a branch, you are automatically put at the tip. |
Note
|
Branching is intended to be easy and fast. It can be used for even the smallest changes. That is a good idea. You never know when a change is going to get bigger, or take longer, than you expected. |
A tag is used to mark a specific commit for future reference. It is basically just a pointer to the commit. This could be to identify a commit that was used for a release or just to keep track of it. There are annotated tags and lightweight tags.
At any given time, in addition to the repository, we are working with three distinct representations of the files in the project:
-
The current
HEAD
commit, which is a saved state of the project. -
The index (or cache) which is where changes are staged for the next commit. When you make a commit, you are storing what is in the index. If there are no changes in the index, there is nothing to commit.
-
The working directory, which is what you edit and what you see with
ls
.
-
Checkout a specific commit (by branch, tag, hash, or other reference). This becomes the
HEAD
commit. If you don’t checkout a specific commit, you are just working at whatever the currentHEAD
is. -
Modify the working directory, which starts out with the same files as the
HEAD
commit. -
add
the changes to the index, which also started out with the same files as theHEAD
commit. When a file is added to the index, it is considered staged. -
commit
the files in the index to make a new commit. The new commit becomes the newHEAD
.
Important
|
After add ing changes to the index, you can make further
changes to the affected files, but you will need to add the
additional changes if you want to commit them. Each add stages the
content you have changed; it is not a "marker" that the working
directory has changed.
|
Note
|
You don’t have to add all your changes to the index before committing. A commit will only use the changes in the index. This allows you flexibility to commit subsets of changes. |
Git does not preserve timestamps. The modification times are in the commits. When you checkout a file or version, all files that are updated get the current time. It is better this way. For example, if you checkout an old version, you want make to realize that the files were updated and need to be compiled.
You don’t have to use the full 40 character hash to refer to a commit. You can use:
-
unique prefix of the hash e.g.,
bdca
for the initial commit in the Usegit log
to see the history example above. -
symbolic names,
HEAD
-
the name of a branch,
main
which is the commit at the tip of the branch -
a tag you assign,
v1
. -
a relative reference to a hash or other reference, e.g.,
HEAD~~
, i.e., two beforeHEAD
Git only remembers if a file is executable or not or a symbolic link. The table Git file permission values summarizes the permission values.
File type | Internal Git | On disk |
---|---|---|
executable |
|
|
non-executable |
|
|
symbolic link |
|
|
Let’s greet more than one world.
$ cp /tmp/hellos.c hello.c $ cat hello.c #include <stdio.h> int main() { printf("Hello, Worlds!\n"); return 0; }
$ 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: hello.c no changes added to commit (use "git add" and/or "git commit -a")
Note
|
More undo hints! |
Use the diff
feature.
$ git diff diff --git a/hello.c b/hello.c index 03bde36..1e2cfea 100644 --- a/hello.c +++ b/hello.c @@ -1,5 +1,5 @@ #include <stdio.h> int main() { - printf("Hello, World!\n"); + printf("Hello, Worlds!\n"); return 0; }
Note
|
There are a lot of very useful features in
You can also add file or directory names at the end of the command to restrict it to specific files or directories. |
$ git add . $ git status On branch main Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: hello.c
Note
|
Adding files that have not changed has no effect on the index.
In this case, adding . picks up only the modified file.
|
Note
|
Other useful index (and working directory) manipulation commands:
Using these commands makes the changes easier than manipulating the files in the working directory directly. |
$ git commit -m "Change to worlds" [main 958ad36] Change to worlds 1 file changed, 1 insertion(+), 1 deletion(-)
$ git log commit 958ad362fe6a1661e07e0765b13b74ae7475fd57 (HEAD -> main) Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:35:39 2020 -0400 Change to worlds commit bdca79e9499918240c27ff43c6598b26db6c0893 Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:31:42 2020 -0400 Initial commit
In this case we are going to expand our greeting to galaxies.
Using checkout -b
will create create a new branch at the current
HEAD
commit and leave it checked out.
$ git checkout -b galaxy Switched to a new branch 'galaxy'
Note
|
The same approach can be used to create a maintenance branch. |
$ git log commit 958ad362fe6a1661e07e0765b13b74ae7475fd57 (HEAD -> galaxy, main) Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:35:39 2020 -0400 Change to worlds commit bdca79e9499918240c27ff43c6598b26db6c0893 Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:31:42 2020 -0400 Initial commit
Note
|
HEAD is the tip of galaxy and also happens to be the same
commit as the tip of main .
|
-
Update working directory
$ cp /tmp/hellog.c hello.c $ cat hello.c #include <stdio.h> int main() { printf("Hello, Galaxy!\n"); return 0; }
-
Check the status
$ git status On branch galaxy 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: hello.c no changes added to commit (use "git add" and/or "git commit -a")
-
Add it to the index
You can add files selectively depending on what you want to commit.
$ git add hello.c $ git status On branch galaxy Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: hello.c
-
Commit it
$ git commit -m "Switch to galaxy" [galaxy 71b0756] Switch to galaxy 1 file changed, 1 insertion(+), 1 deletion(-)
-
View current log
$ git log commit 71b0756022a3c2d1d2008a582d9ccc2ebec40688 (HEAD -> galaxy) Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:40:45 2020 -0400 Switch to galaxy commit 958ad362fe6a1661e07e0765b13b74ae7475fd57 (main) Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:35:39 2020 -0400 Change to worlds commit bdca79e9499918240c27ff43c6598b26db6c0893 Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:31:42 2020 -0400 Initial commit
Note
|
The latest commmit is the HEAD commit, which is the tip of the
galaxy branch. The preceding commit is still the tip of main . We
see it because it is in the history of this branch.
|
This will bring the changes in the feature branch into the *main` branch
$ git checkout main Switched to branch 'main' $ git status On branch main nothing to commit, working tree clean $ git log commit 958ad362fe6a1661e07e0765b13b74ae7475fd57 (HEAD -> main) Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:35:39 2020 -0400 Change to worlds commit bdca79e9499918240c27ff43c6598b26db6c0893 Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:31:42 2020 -0400 Initial commit
$ git merge galaxy Updating 958ad36..71b0756 Fast-forward hello.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Note
|
The merge was You won’t be able to Fast-forward if the branch you are merging onto
was changed since it was branched off from. However, you can
|
The new commit has the same SHA as the HEAD
commit on galaxy
$ git log commit 71b0756022a3c2d1d2008a582d9ccc2ebec40688 (HEAD -> main, galaxy) Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:40:45 2020 -0400 Switch to galaxy commit 958ad362fe6a1661e07e0765b13b74ae7475fd57 Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:35:39 2020 -0400 Change to worlds commit bdca79e9499918240c27ff43c6598b26db6c0893 Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:31:42 2020 -0400 Initial commit
Note
|
Now HEAD is the tip of main and
also happens to be the same commit as the tip of galaxy .
|
To add a lightweight tag:
$ git tag v1 $ git log commit 71b0756022a3c2d1d2008a582d9ccc2ebec40688 (HEAD -> main, tag: v1) Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:40:45 2020 -0400 Switch to galaxy commit 958ad362fe6a1661e07e0765b13b74ae7475fd57 Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:35:39 2020 -0400 Change to worlds commit bdca79e9499918240c27ff43c6598b26db6c0893 Author: Ed Himwich <51408905+wehimwich@users.noreply.github.com> Date: Sat Jun 6 15:31:42 2020 -0400 Initial commit
The tag is listed for the commit.
Note
|
The galaxy branch is no longer shown, since it has been
deleted. Its former commits are still there since they were merged
onto main .
|
Note
|
The tag can be used to checkout that commit even after the branch has
moved past it. For example to create a maintenance branch for old
commit tagged as $ git checkout v1 $ git checkout -b m1 Life is easier if branch and tags names don’t conflict. |
Note
|
List all tags: $ git tag v1 |
This is a small selection of the available resources.
You can get help on any command from the man page by using the
--help
option, like:
$ git commit --help
There is a lot of useful information, at all levels, that is only a search away on the web. Most of it seems to be trustworthy, but it is obviously better if you understand enough to evaluate it before using it.
Some useful links:
-
https://www.atlassian.com/git/tutorials — Tutorials
-
https://git-scm.com/ — Git community online
-
https://chris.beams.io/posts/git-commit/ — How to write a Git commit message
-
https://ohshitgit.com/ — Some recovery strategies
-
https://stackoverflow.com/questions/16562121/git-diff-head-vs-staged
Command | Description |
---|---|
|
Initialize repository |
|
Check the status |
|
Add current directory structure to the index |
|
Add |
|
Commit with commit message |
|
Commit without commit message (an editor is run) |
|
Show history from current |
|
History with change statistics |
|
History with patch listing |
|
Remove |
|
Rename files |
|
difference index to working directory |
|
difference |
|
difference |
|
Starting at |
|
List all branches |
|
Checkout branch ( |
|
Merge branch ( |
|
Delete branch ( |
|
Add tag ( |
|
Checkout tag ( |
|
List tags |
|
Show help |