This lecture aims at providing a brief review of some advanced topics in Git Version Control System which are essential for effective management of large collaborative projects.

A useful Git-cheatsheet link for future reference:


Getting help for Git commands

There are basically three ways to request information about Git commands on the Git-bash command line:

$ git help <command name>
$ git <command name> --help
$ man git-<command name>

Note that the last command man is not a Git command. All Git commands begin with the word git.

Viewing the Git commit history

You can check the history of project commits to the repository using the Git command git log. For example, here is a snippet from the output of git log on laptop screen:

  $ git log
commit 264043e0d49006b7f59e57639961c333b5d5f124
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 17:48:33 2017 -0600

    data tables for 1D model added.

commit b45ddd780c18736bac42ff27aade301a6b0f09cb
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 10:08:53 2017 -0600

    minor edit

commit b6f4fb69d97a396bde7159c78424100704328634
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 21:03:28 2017 -0600

    *.txt files removed

commit 961ff2d4ac68081a21864b2f1b067b43424d3342
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 21:02:00 2017 -0600

    update

commit 8fd3c49e90d96f4bcef2302c7b227f06ad7a5250
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:36:45 2017 -0600

    minor

commit cd0ed7def385ba29ea134bcc18a4ad0873d9f5ee
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:32:51 2017 -0600

    minor

commit 8096668868ef64189f788669d5f1838cd944f9b6
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:28:22 2017 -0600

    minor

commit c52ba6adea474625cce1493896ecaac40cb976af
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:24:23 2017 -0600

    minor

commit ab5a17f9e913e55cae0f971fd9b485a0a6e8dec3
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:16:27 2017 -0600

    minor

commit ce5c2421db6e76a338a0e6922acead9100a7c4c4
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:10:19 2017 -0600

    minor

commit 4a7d27d81260c2e2a29b6259e6c97befc065c069
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Thu Jan 12 19:05:14 2017 -0600

:


There is a long list of options that can be used with git log command to display exactly what you need on bash screen. Some of the most useful are discussed below.
To lists the differences of the files between different commits use -p flag:

$ git log -p


To lists only a limited number of most recent commits use -<number of commits to display> flag. For example:

$ git log -2
$ git log -2
commit 264043e0d49006b7f59e57639961c333b5d5f124
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 17:48:33 2017 -0600

    data tables for 1D model added.

commit b45ddd780c18736bac42ff27aade301a6b0f09cb
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 10:08:53 2017 -0600

    minor edit


To list the statistics of the commits, use,

$ git log --stat -1
commit 264043e0d49006b7f59e57639961c333b5d5f124
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 17:48:33 2017 -0600

    data tables for 1D model added.

 README.md                |   1 +
 results/tables/README.md |   8 ++-
 results/tables/tgm.mat   | Bin 0 -> 2550889 bytes
 src/defineProject.m      |   3 +-
 src/main.m               |  82 +++++++++------------------
 src/writeTables.m        |  75 ++++++++++++++++++++++++
 9 files changed, 140 insertions(+), 56 deletions(-)


The log data can be even represented formatted via the flag --pretty=format:"<the desired format>". For example,

$ git log -3 --pretty=format:"%h - %an, %ar : %s"
264043e - Amir Shahmoradi, 7 days ago : data tables for 1D model added.
b45ddd7 - Amir Shahmoradi, 7 days ago : minor edit
b6f4fb6 - Amir Shahmoradi, 3 weeks ago : *.txt files removed


A list of available format options is provided below.

Table 1: Some useful options for git log --pretty=format:"<options>". 1
Option Description of Output
%H Commit hash
%h Abbreviated commit hash
%T Tree hash
%t Abbreviated tree hash
%P Parent hashes
%p Abbreviated parent hashes
%an Author name
%ae Author email
%ad Author date (format respects the --date=option)
%ar Author date, relative
%cn Committer name
%ce Committer email
%cd Committer date
%cr Committer date, relative
%s Subject


A list of useful options for git log is given in the table below.

Table 2: Common flags for git log command. 1
Option Description
-p Show the patch introduced with each commit.
--stat Show statistics for files modified in each commit.
--shortstat Display only the changed/insertions/deletions line from the --stat command.
--name-only Show the list of files modified after the commit information.
--name-status Show the list of files affected with added/modified/deleted information as well.
--abbrev-commit Show only the first few characters of the SHA-1 checksum instead of all 40.
--relative-date Display the date in a relative format (for example, “2 weeks ago”) instead of using the full date format.
--graph Display an ASCII graph of the branch and merge history beside the log output.
--pretty Show commits in an alternate format. Options include oneline, short, full, fuller, and format (where you specify your own format).


A list of useful options for git log commands that can change the format of the output, is given in the table below.

Table 3. Flags limiting the output of git log command. 1
Option Description
-<n> Show only the last n commits (e.g., -2, -3, ...).
--since, --after Limit the commits to those made after the specified date.
--until, --before Limit the commits to those made before the specified date.
--author Only show commits in which the author entry matches the specified string.
--committer Only show commits in which the committer entry matches the specified string.
--grep Only show commits with a commit message containing the string.
-S Only show commits adding or removing code matching the string.


For example, to list the commit history since a specific time in the past, use --since flag:

$ git log --since=2.weeks
commit 264043e0d49006b7f59e57639961c333b5d5f124
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 17:48:33 2017 -0600

    data tables for 1D model added.

commit b45ddd780c18736bac42ff27aade301a6b0f09cb
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 24 10:08:53 2017 -0600

    minor edit


or, with the following date format:

$ git log --since="2017-01-15"


Undoing, amending, and tagging

Suppose you want to undo the latest addition of files to the stage area. The command to use in order to clean the stage area is,

$ git reset


This command is basically the opposite of git add. There are a lot of options that can accompany this Git command, details of which can be found in Git documentation. However, be careful when using this command, especially with flag --hard, which can result in changes in your working directory as well, thus leading to involuntary loss of latest uncommitted files.

Another useful command is,

$ git commit --amend


which is used when you forget to add some files to your latest commit and now, you would like to add the latest content of your project’s staging area to the last commit, by amending it.

The Git command git tag allows you to tag specific important commits in your commit history, for example a final release of your project/software. If you just type git tag on Git bash command line, it will list all tags in the project, in alphabetical order,

$ git tag
v1.0
v1.1
v2.0


You can create a tag for the latest commit to your repository by using,

git tag -a v3.0 -m "new version of project"


This will add a new tag pointing to the latest commit in the project with name v3.0 and the message new version of project. Now if you want to display the information about a specific tag, use git show,

$ git show v1.0
tag v1.0
Tagger: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Tue Jan 1 18:08:00 2017 -0600

first tag

commit 25129a074aece69d234838764e7df63ed1fb3023
Author: Amir Shahmoradi <a.shahmoradi@gmail.com>
Date:   Fri Dec 27 17:12:08 2016 -0600

    Update README.md


You can also create a lightweight tag by avoiding -a, -s, or -m flags in you tag command,

git tag v4.0


Annotated tags (created with -a flag) are meant for very important project versions, such as a software release, while lightweight tags are meant for private or temporary object labels.
For more information about git tag, see Git documentation.

Working with remote Repositories

In order to see which remote servers you have configured for your project, you can run the following Git command,

$ git remote
origin


In the output above, origin is the default name that Git gives to the server you cloned your project from. If you also specify -v flag, Git will show you the URLs that Git has stored for the remote shortname (e.g., origin) to be used when reading and writing to that remote repository.

$ git remote -v
origin  git@github.com:shahmoradi/foo.git (fetch)
origin  git@github.com:shahmoradi/foo.git (push)


In order to see more information about a particular remote repository in your project, you can use,

$ git remote show origin
* remote origin
  Fetch URL: git@github.com:shahmoradi/foo.git
  Push  URL: git@github.com:shahmoradi/foo.git
  HEAD branch: master
  Remote branches:
    development tracked
    master      tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)


The above information indicates that the current HEAD branch of the project is on master branch, and the remote branches on the remote repository are master and development.

You can find more information about git remote command in Git documentation.

Git branching

Branching is powerful idea in Version Control Systems, which significantly facilitates collaboration or parallel development of a project. Suppose you have already a project which has reached its first release version, meaning that it works all fine. Now, you like to add a new feature to your project. To do this and to avoid further commits to your original project, you can create a new branch of the project which is specifically devoted to adding the new feature. See the following figure, for an example of Git branching.

An example illustration of Git branching for a project with three branches: master, development, topic.

When you create a Git project, the default single branch in your project is master. So, for example if you search for your project branches, you’d see,

$ git branch
* master


If you already have created other branches in your project, for example development, then you may see a Git message like the following,

$ git branch
* development
  master


The * behind the branch name indicates that the project HEAD (i.e., your working directory) points to that branch, that is, all commits will be made to that branch that is highlighted with *. In the example above, the working directory points to development branch.

In order to create a new branch in your project, use,

$ git branch <branch name>


For example,

$ git branch development


When you create a new branch, Git creates an exact duplicate of the current branch that you are in, but with the requested name for the new branch. In order to switch to a specific Git branch in your project, use,

$ git checkout <branch name>


For example,

$ git checkout development
Switched to branch 'development'


A shortcut for both creating a new branch from the current branch, and switching to the new branch automatically is to use,

$ git checkout -b <branch name>


$ git checkout -b test
Switched to a new branch 'test'


Now suppose, you have made some significant progress on your project development branch, and now you want to add the newly developed feature to your master branch. All you need to do, is,

$ git checkout master
$ git merge development


By doing this, you first switch to master branch, then request Git, to merge all changes made to the project on the development branch to be copied and transferred to the master branch. For example, if I have added a single test.txt file, and committed the change to the development branch, then upon mergin with master, Git will display something like the following,

$ git merge development
Updating 25129a0..57ed7c2
Fast-forward
 test.txt | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 test.txt


Now suppose there was no change in the development branch, since branching it from master. Then upon merging, you would get a message like the following,

$ git merge development
Already up-to-date.


Occasionally, conflicts may arise between branches upon merging. This happens when a common file between the two branches, has been modified on both branches in parallel. In such cases, you will need to resolve the conflict by choosing one of the file edits in one of the branches and discard the other. The following is an example merge conflict message by Git.

$ git merge development
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.


For example, if you use git status command, the following is the typical message that will be displayed,

$ git status
On branch test
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:   test.txt

no changes added to commit (use "git add" and/or "git commit -a")


To resolve the conflict, open the conflicting file. There you will see the segments of the file from both versions which are the cause of the conflict. Edit the file as you wish. and then perform the normal git add and git commit on the file, to resolve the conflict and complete the merge process.

In addition to git merge, there is another more elegant and powerful Git command, git rebase for integrating changes in different branches into a single branch. But, the subject goes beyond the goals of this class, and the interested reader is referred to Git documentation for this command and this pedagogical Git page for git rebase.

Deleting a branch

Normally, when a branch merge is done, the development branch is not needed anymore, so it is always wise and cleaner to delete the unwanted branches, using the command git branch -d <branch name>. To do this, on the same branch that you just performed git merge use,

$ git checkout master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)
Switched to branch 'master'
$ git branch -d test
Deleted branch hotfix (3a0874c).


Note that you cannot delete a branch while you are on it (i.e., while it is checked out). For example, Git will give you the following error message,

$ git branch -d test
error: Cannot delete branch 'test' checked out at 'C:/Users/Amir/git/foo'


You cannot delete a branch from another branch with which you have not performed the merging either. In this case, Git will give the following error,

$ git branch -d test
error: The branch 'test' is not fully merged.
If you are sure you want to delete it, run 'git branch -D test'.


Advice for good project management

Your master branch should always contain the latest clean version of your project, that is, the complete and working version/release. You should always make your new developments in a separate branch, and once it works and the new feature/development is complete, you merge the new development into the master branch.


Footnotes

1. See Git documentation for more details and updates.

Comments