Everyone is welcome here --- except those who have borrowed books from me for and have not returned them yet!

Version control at your fingertips: a quick start with Git

Posted on February 28, 2018 in computer-science

Introduction

A version control system allows to keep the history of changes made to a set of documents and to recall specific versions later.

The most basic task is to compare two files. Your text editor may already have such a function --- for example, in Emacs, it is accessible through the command ediff or the menu Tools/Compare).

You can compare two files on the command-line with the command diff:

$ diff file1 file2

Another command, with a more user-friendly output is meld:

$ meld file1 file2

(you may need to install medl with sudo apt-get install meld)

These two tools also work on directories. To quickly check if there is any difference between two directories:

$ diff -r -q dir1 dir2

You could use a numbering to keep track of the evolving versions of your files, but this is not a good idea, especially when collaborating with several people.
To keep track of changes made to files in a directory, I highly recommand that you use a version control software. Personnaly, I use git.

You can read the Git Parable to understand the principles of Git. Here, I will describe just a few basic git commands. The Git Book is the definitive documentation.

To install git:

$ sudo apt-get install git gitk

Creating a local repository

From scratch:

$ mkdir git-test
$ cd git-test
$ git init
Initialized empty Git repository in /home/pallier/cours/Python/version_control/git-test/.git/

Importing an already existing repository

Alternatively, you can also import an existing repository, either from another directory, or from the Internet:

$ git clone git clone https://github.com/chrplr/pyepl_examples

If you plan to share your repository, it can be a good idea to first create a repository on http://github.com or http://bitbucket.org, and then clone it on your local hard drive. The internet location will be added to the list of remote repositories under the name origin (see below for remote repositories)

Importanly, with git, you can still do version control locally, and only transfer your changes to the remote repository whenever you want, or never, because git is a decentralized version control system and all repositories are equal.

Adding files to the local repository

The command 'git add' is used to tell git which files it must track:

$ echo 'essai1' > readme.txt
$ git add readme.txt

Interestingly, you can also add whole directories, for example:

$ git add .

Note that it is possible to prevent certain files to be tracked (see https://help.github.com/articles/ignoring-files)).

To check which files are currently being tracked (or staged in git's terminology), use the command `git status``:

$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   readme.txt
#

Creating a snapshot (committing)

Once you are satisfied with the files in your working directory, you can take a snapshot. This is also called commiting your changes:

$ git commit
[master (root-commit) a7a3a47] First commit
1 file changed, 1 insertion(+)
create mode 100644 readme.txt

This saves a snapshot of the staged files the hidden directory .git at the root of your project. Unless you delete this directory, this version of your files is saved there forever and always accessible.

Modifying the project

Let us now modify the file readme.txt in the working directory:

$ echo 'line2' >readme.txt

The command git status allows us to check the state of the files in the working directory:

$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   readme.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
$ git add readme.txt
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   readme.txt
#

Let us create a new file, `readme2.txt``:

$ echo 'trial2' >readme2.txt
$ ls
readme2.txt  readme.txt
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   readme.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       readme2.txt

We now add readme2.txt to the repository:

$ git add readme2.txt
$ git commit
[master a7e25a1] First revision; added readme2.txt
2 files changed, 2 insertions(+), 1 deletion(-)
create mode 100644 readme2.txt

Let us consult the history of the project:

$ git log
commit a7e25a158ce52a75c62381420f7dc375de631b1b
Author: Christophe Pallier <christophe@pallier.org>
Date:   Mon Aug 27 10:49:54 2012 +0200

First revision; added readme2.txt

commit a7a3a47edfae9d7c720356b691000a81ded73906
Author: Christophe Pallier <christophe@pallier.org>
Date:   Mon Aug 27 10:47:32 2012 +0200

First commit



$ git status
# On branch master
nothing to commit (working directory clean)

Renaming a file

To rename a tracked file, you should use git mv rather then just mv:

$ git mv file.ori file.new

Recovering a file deleted by accident

Let us delete readme2.txt "by accident":

$ rm readme2.txt # oops
$ ls
readme.txt
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       deleted:    readme2.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

To recover it:

$ git checkout -- readme2.txt
$ ls
readme2.txt  readme.txt
$ cat readme2.txt
trial2

Checking for changes

Let us now modify readme2.txt and then compare the file in the current directory from the ones in the last commit:

$ echo 'line2 of 2' > readme2.txt
$ git diff
diff --git a/readme2.txt b/readme2.txt
index 33d1e15..e361691 100644
--- a/readme2.txt
+++ b/readme2.txt
@@ -1 +1 @@
-trial2
+line2 of 2
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   readme2.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

You prefer meld, you can use

$ git difftool -t meld

Inspecting the history of the project

For a graphical view of the history of the project:

$ gitk

Faster commit

The command git commit -a performs both a git add and a `git commit':

 $ git commit -a [master a74359e] Second revision 1 file changed, 1
 insertion(+), 1 deletion(-) $ git log commit
 a74359e148aff0c369b6ddd482d0fbe0e7ad93ab Author: Christophe Pallier
 <[christophe@pallier.org](mailto:christophe@pallier.org)\> Date: Mon
 Aug 27 10:52:50 2012 +0200

 Second revision

 commit a7e25a158ce52a75c62381420f7dc375de631b1b Author: Christophe
 Pallier <[christophe@pallier.org](mailto:christophe@pallier.org)\>
 Date: Mon Aug 27 10:49:54 2012 +0200

 First revision; added readme2.txt

 commit a7a3a47edfae9d7c720356b691000a81ded73906 Author: Christophe
 Pallier <[christophe@pallier.org](mailto:christophe@pallier.org)\    
 Date: Mon Aug 27 10:47:32 2012 +0200

 First commit

Downloading the most recent changes from the distant repository

If you imported your repository from the internet with 'git clone', you can import the recent changes with:

$ git pull

Pushing your changes to the distant repository

You can send your modified repository (after commiting) to the original remote internet repository:

$ git push

Compare the current folder with the remote origin/master

$ git fetch
$ git diff origin

Working with several remotes

To add a remote

git remote add -f nameforremote path/to/repo_b.git
git remote update

To list the remotes

git remote -v

To compare the current branch with one in a remote

git diff master remotes/b/master

To see branches on remotes

git branch -r

(To see local branches: git branch -l, all branches, git branch -a)

Compare the working version of a file with the one in the last commit

git diff HEAD

Compare two branches

For visual diffs, I use meld:

sudo apt install meld
git config --global diff.tool meld

To list all branches:

git branch -a

Then, to see the differences between the branches

git difftool branch1..branch2

To compare a specific file:

git difftool branch1..branch2 -- filename

Another approach!

Git command line for merging without commiting:

git checkout branchA
git merge --no-commit --no-ff branchB
git gui

When done:

git merge --abort

Handling very large files (e.g. data)

git-annex allows you to leave large files in some of the repositories and keep only links in others.

See https://writequit.org/articles/getting-started-with-git-annex.html and https://git-annex.branchable.com/walkthrough/