Skip to content

Latest commit

 

History

History
997 lines (769 loc) · 26.2 KB

demo.adoc

File metadata and controls

997 lines (769 loc) · 26.2 KB

Git demonstration notes

1. Preamble

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.

2. A simple project: Hello world!

2.1. Initial files

$ 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!

2.2. Make it Git!

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.

2.3. git status is the basic command to see where you are

$ 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.

2.4. Use the .gitignore file to ignore 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.

2.5. Add the files to the index

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.

2.6. Commit changes that are in the index

$ 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.

2.7. Check the status again

$ git status
On branch main
nothing to commit, working tree clean

All clean!

2.8. Use git log to see the history

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.

2.8.1. Some other forms of git log

This whole sub-section is an aside.

--stat for statistics
$ 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.

-p for patch listing
$ 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.).

3. Basic concepts

3.1. Repository

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.

3.2. Git tracks content

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.

3.3. Commits

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.

3.4. Branches

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.

3.5. Tags

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.

3.6. How content is organized

At any given time, in addition to the repository, we are working with three distinct representations of the files in the project:

  1. The current HEAD commit, which is a saved state of the project.

  2. 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.

  3. The working directory, which is what you edit and what you see with ls.

3.7. An example workflow

  1. 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 current HEAD is.

  2. Modify the working directory, which starts out with the same files as the HEAD commit.

  3. add the changes to the index, which also started out with the same files as the HEAD commit. When a file is added to the index, it is considered staged.

  4. commit the files in the index to make a new commit. The new commit becomes the new HEAD.

Important
After adding 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.

3.8. Timestamps

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.

4. Some other details

4.1. References

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 Use git 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 before HEAD

4.2. File permissions

Git only remembers if a file is executable or not or a symbolic link. The table Git file permission values summarizes the permission values.

Table 1. Git file permission values
File type Internal Git On disk

executable

100755

-rwxr-xr-x

non-executable

100644

-rw-r--r--

symbolic link

120000

lrwxrwxrwx

5. Making a change

Let’s greet more than one world.

5.1. Update the working directory

$ cp /tmp/hellos.c hello.c
$ cat hello.c
#include <stdio.h>
int main() {
   printf("Hello, Worlds!\n");
   return 0;
}

5.2. Our status: modified, not staged

$ 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!

5.3. How is this different than what is in the index

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 diff. Here are just a few:

  • git diff  — difference index to working directory

  • git diff HEAD — difference HEAD to working directory

  • git diff --cached — difference HEAD to cache (index)

You can also add file or directory names at the end of the command to restrict it to specific files or directories.

5.4. Add it to the index

$ 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:

  • git rm <files> — to remove files

  • git mv …​ …​ - to rename files

Using these commands makes the changes easier than manipulating the files in the working directory directly.

5.5. Commit it

$ git commit -m "Change to worlds"
[main 958ad36] Change to worlds
 1 file changed, 1 insertion(+), 1 deletion(-)

5.6. The log

$ 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

6. Add a new feature on a new branch

In this case we are going to expand our greeting to galaxies.

6.1. Create new branch

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.

6.2. Check the log

$ 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.

6.3. Show branches

$ git branch -a
* galaxy
  main

The * marks the active branch.

6.4. Make the change in the new branch

  1. Update working directory

    $ cp /tmp/hellog.c hello.c
    $ cat hello.c
    #include <stdio.h>
    int main() {
       printf("Hello, Galaxy!\n");
       return 0;
    }
  2. 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")
  3. 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
  4. Commit it

    $ git commit -m "Switch to galaxy"
    [galaxy 71b0756] Switch to galaxy
     1 file changed, 1 insertion(+), 1 deletion(-)
  5. 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.

7. Merge the changes into the main branch

This will bring the changes in the feature branch into the *main` branch

7.1. Switch back to 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

7.2. Merge the changes from the feature branch

$ git merge galaxy
Updating 958ad36..71b0756
Fast-forward
 hello.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Note

The merge was Fast-forward. This makes the changes from the galaxy branch look like they were originally added to the main branch (see log output below). This is good for small changes, but sometimes you want to have a separate merge commit to record the path of a significant development. For that case, you can use the --no-ff option.

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 rebase the other branch to the target branch to get a similar effect.

7.3. Look at the log now

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.

7.4. Delete the feature branch

Since we have merged it, we don’t need to leave the feature branch laying around cluttering things up.

$ git branch -d galaxy
Deleted branch galaxy (was 71b0756).

8. Add a tag to a commit

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 v1, you can use:

$ 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

9. Resources

This is a small selection of the available resources.

9.1. man pages

You can get help on any command from the man page by using the --help option, like:

$ git commit --help

9.2. On the web

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:

9.3. Books

  • "Version Control with Git, 2nd Edition" by Jon Loeliger, Matthew McCullough, Released August 2012, Publisher(s): O’Reilly Media, Inc., ISBN: 9781449316389

10. Git commands mentioned

Table 2. Summary of Git commands mentioned
Command Description

git init

Initialize repository

git status

Check the status

git add .

Add current directory structure to the index

git add <files>

Add <files> to the index

git commit -m "Initial commit"

Commit with commit message

git commit

Commit without commit message (an editor is run)

git log

Show history from current HEAD

git log --stat

History with change statistics

git log -p

History with patch listing

git rm <files>

Remove <files>

git mv …​ …​

Rename files

git diff

difference index to working directory

git diff HEAD

difference HEAD to working directory

git diff --cached

difference HEAD to cache (index)

git checkout -b galaxy

Starting at HEAD create a new branch (galaxy) and check it out

git branch -a

List all branches

git checkout main

Checkout branch (main)

git merge galaxy

Merge branch (galaxy) into current branch

git branch -d galaxy

Delete branch (galaxy)

git tag v1

Add tag (v1) to current HEAD

git checkout v1

Checkout tag (v1)

git tag

List tags

git commit --help

Show help