Git version control system

Links

Manual pages for Git

What to use Git for?

What kind of things would you want to do with Git?

Git clients

For most operations on your local repositories and remote ones, the git commands in your terminal are the most efficient. In some cases though, a graphic client is really useful, e.g. when inspecting changes after fetching from a shared repository or for an intuitive view of the (branching) history of a project. Some examples of clients:
... and many more

Repository, index and working directory

This diagram shows the organisation of your working directory with your files, the repository and the index which resides between them. The commands are explained in the text below.

Getting started with a local repository

Commmand: git init
  • Initialise empty Git repository
  • You'll find a .git directory in the top level directory of your project
  • Add some files in two steps: first add them to the index, then commit the index to the repository:
    git add <some files>
    git status (shows staged, unstaged and untracked files)
    git commit

    Preferably don't add entire folders, as they may contain files you would rather not put in git, such as executables, .o files, (large) media files and other things that either can be made again from the source code or can be obtained from other sources.

    Git status shows which files have changed w.r.t. the most recent version in the repository, which files are in the index and will be commited in the next commit and 'Untracked files': i.e. files that Git knows nothing about because they are not in the repository.

    Instead of 'modified', it can show 'typechange', which means that the file has changed from a proper file into a symlink or vice versa.

    Now take a look what has been stored into the repository:
    git status
    git ls-tree HEAD
    git show (--name-only)

    Make some modifications

    See what has been changed with respect to the HEAD of the repository:
    git status -s
    git ls-tree HEAD
    git show (--name-only)

    git commit doesn't do anything if we don't add some files first. If you want you can skip the 'add to index' step by using
    git commit <some files> -m "Log message" but this is not recommended.

    See our loggging and other information:
    git log
    git show (--name-only)
    git show <a specific commit or HEAD>

    git add -u (add all modified files already under Git control to the index)
    git commit (commits all files in the index)
    or
    git commit -a (adds everything to the index and commits the index)

    make modifications, then
    git diff
    git add <files>
    git commit
    git diff

    Git diff with external DIFF program

    Git can show the differences between two versions stored in its archive or a stored version versus your current file. The default output however is in a format that is hard to interpret for a human reader. A much more useful way is to put both versions side by side with their changes marked in different colors. For this purpose a lot of external viewers are available.

    Asking Git to show the differences between your working copy and the version stored in the repository, Git will first check whether the environment-variabele GIT_EXTERNAL_DIFF exists. If so, it sends both files to an external diff program, otherwise it uses the standard diff view.

    Using the git subcommand 'difftool' helps selecting a tool for displaying side-by-side views of two (or even more) versions of a file. Some useful ways to use this:
    git difftool --tool-help
    git difftool 3 4 (in combination with tags 3 and 4, see 'tagging')
    git difftool --tool=vimdiff

    Using an external differ is also possible with these steps:

    .bashrc


    Put GIT_EXTERNAL_DIFF in your ~/.bashrc
    
    #
    # Git stuff
    #
    export GIT_EXTERNAL_DIFF=$HOME/scripts/gitdiffwrapper
    

    $HOME/scripts/gitdiffwrapper

    The script shown here calls a diff program (opendiff, or vim -d) and sends parameters $2 and $5, which represent the names of the repository file and working file.
    
    #! /bin/bash
    
    /usr/bin/gvim --nofork -R -d $2 $5
    
    or for OSX:
    
    /usr/bin/opendiff $2 $5
    
    **N.B.: Don't forget to make the script executable: chmod 755 $HOME/scripts/gitdiffwrapper)

    .gitignore and .gitkeep

    Files or folders that are necessary for your project but unwanted in the git archive can be hidden from git by creating a file .gitignore in the root folder of your project. In this file you can specify files and even wildcards.

    Empty folders will not be added to the git archive. In case your project needs a folder for e.g. output data, you want to be able to put an empty folder in git. To do this, create a file .gitkeep in the empty folder before committing. Be aware that this is just a convention, not an official feature of git.

    Project export using Git archive

    Suppose you have a project which is under Git control and you want to send only the project files in your working folder to someone else, without the .git folder. Of course you could make a full copy of the entire folder, remove the .git folder and then send the remaining stuff, but there is a better way: **git archive**. An easy way to use it is as follows:
    
    git archive master -o myproject.tar
    
    This puts all of the most recent files in the master branch into a tar file which you can compress with gzip and send. For more use cases see 'man Git-archive'.

    patch

    
    git add --patch 
      lets you select the areas that have changed
    

    Tracking the history

    
    Various ways to diff:
    
    $ git diff          # difference between the index file and your
                        # working directory; changes that would not
                        # be included if you ran "commit" now.
    
    $ git diff --cached # difference between HEAD and the index; what
                        # would be committed if you ran "commit" now.
    
    $ git diff HEAD     # difference between HEAD and working tree; what
                        # would be committed if you ran "commit -a" now.
    
    $ git diff HEAD --name-only    # difference between HEAD and working tree,
    just a list of the files
    
    $ git diff <commit>     # difference between a specific commit and working tree
    
    $ git status        # a brief per-file summary of the above.
    
    The most recent commit in the currently checked-out branch can always be
    referred to as HEAD, and the "parent" of any commit can always be referred
    to by appending a caret, "^", to the end of the name of the commit.
    
    HEAD~n can refer to the nth previous commit
    
    Examples:
    git diff HEAD^:jack_flanger.cpp HEAD^^:jack_flanger.cpp 
    git diff HEAD~3:jack_flanger.cpp HEAD~4:jack_flanger.cpp
    git checkout master~2 jack_flanger.cpp
    
    #
    # since/until
    #
     Git log --since="1 week ago"
     2077  Git log --until="1 week ago"
    
    
    git ls-files  -- shows a list of (managed ?) files
    
    git ls-files -- stage  -- shows a list of staged files
    
    git add path/to/new/file
    
    git whatchanged  -- geeft history
    If you also want to see complete diffs at each step, use
    git whatchanged -p
    

    Tagging

    With tags you can mark commits with your own labels. Suppose you want to create your own version scheme like v1.2, v1.3, v2.0 etc. then you can attach these as tags to specific commits.
    Another application is to create a stack of tutorial examples containing incremental changes. The student can check out the examples by their labels, like 1, 2, 3, etc.

    Tagging is described here: Git-Basics-Tagging.

    An example:

    
    git tag -a "1"
    commit ...
    git tag -a "2"
    commit ...
    git tag -a "3"
    
    then you can do
    git checkout 1
    git checkout 2
    git checkout 3
    

    Note that -a when creating a tag makes the tag visible in the git logging.

    If you want to tag a commit that is further down in history, just point to it by its hash or the first 7 or so digits of its hash, like this:

    
    git tag -a "mytag" ff67480
    

    Retrieving older files from the repository

    Using git log, find the commit-id of the commit you want to retrieve. The commit-id is indicated by the SHA hash or the tag you have to it. Then check out the commit as follows:

    git checkout  .
    

    N.B.: don't forget the final '.' If you don't add it, this will take you into a "detached head state" where you can make changes and make commits, but nothing will be saved and commits you make will be lost.

    How to deal with a detached head

    N.B: this applies to a git HEAD. In all other contexts, call for help.

    A detached head can have several causes. One is given above, when you forget to type the '.' at the end of a git checkout command.

    Another cause is using a tag/SHA-hash to check out a previous commit, which makes HEAD point to an earlier node in the repository history.

    Solutions:
    git checkout master
    
    Return to the master branch with HEAD pointing to its most recent commit.
    git checkout -b newbranch
    
    Switch to another branch.

    
    git branch tmp
    git checkout master
    git merge tmp
    

    Using and/or creating a remote repository

    Before you can push your code to a remote repository you first need to tell Git what your name and e-mail address are. These are meant to identify you when you submit code to a group project. The following lines do that:
    
    git config --global user.name "John Doe"
    git config --global user.email "john.doe@joesplace.com"
    
    To see your global configuration:
    git config --global --list
    

    Common case: start with a project and a local GIT development repository. In this case you don't create an empty remote repository first but start with the one you already have.

    First, clone that repository into a __bare__ repository which will serve as a common remote repository for all developers.
    git clone --bare <original_directory>
    <ProjectName>.git

    Second, add a remote to your original development repository, pointing to the freshly created bare repository.
    git remote add origin <path_to_repository/ProjectName>

    Now push your repository contents to the remote repository and from now on let all new developers clone the remote repository.
    git clone <path_to_project/ProjectName>.git

    useful commands:
    git fetch
    git stash
    git stash pop
    git merge
    git pull
    git push
    git ls-remote

    How to start a project on Github


    On Github (or Gitlab) you start a project and make a clone on your own computer. More often you will start a project on your local computer, put it in a local GIT repository and after a while you may decide to put it on github or Gitlab.

    Create an SSH-KEY and put it on Github

    To be able to push data to Github you will need an ssh-key. By giving Github your **public** ssh-key, the data you transfer back and forth will be encrypted. So how do you make a key and give it to Github? One last thing: the other file (id_rsa) contains your private key, which is used for decoding crypted replies. NEVER give this key away! You only need to give your **public** key to others. If you want to know more about ssh, public/private keys and cryptograph this is a nice place to start.

    Bring your project to Github

    One way to do this is to create a new project on Github/gitlab and give your local repository a new remote that points to the Gitlab project. Take these steps:

    New repository on Github

    On Github, create a new repository without any contents, so no README.md, no .gitignore or other files. You can add these later.
    The site shows the SSH-address for the repository, which will be something like git@github.com:<your-name>/<your-repo>.git
    Copy or remember that link.

    Make sure you add the ssh link, not https. Otherwise github will keep asking for your username/password every time you do a push or pull.

    Go to your local repository

    We assume you have already created your local repository and commited files to it. Go to the root of your working copy and (if needed) check out the main branch.

    Add the new remote

    git remote add origin <the-link-to-your-new-repo>

    Verify the new remote

    git remote -v

    Synchronize both repositories

    In your local folder:
    git push -u origin main

    Working together on a project

    Suppose you have been working on some files in your working folder. Before synchronising with other developers through the shared repository, make sure you commit all changes that you want to share. Only then can Git do a merge.

    Fetch all changes from the remote, inspect the changes and then merge, either file-by-file or all at once, or fetch another branch and switch to that branch.

    git fetch origin master
    git diff origin/master
    Consider a git stash before merging, to temporarily store all your local changes. Then:
    git merge [file...]
    .. or to merge the entire branch:
    git merge origin/master
    Getting back what you just stashed:
    git stash pop
    If necessary, fix all merge conflicts.

    If you did a 'git stash' before merging, you may want to restore your local changes:
    git stash list
    git stash apply


    Now push your local project to Github:
    git push origin master

    Remote (shared) repository from scratch

    git init --bare
    GIT over SSH or proprietary GIT protocol
    git clone ssh://yourhost/~you/repository

    git push ssh://yourserver.com/~you/proj.git master:master
    or
    git push ssh://yourserver.com/~you/proj.git master

    Bare repository: a repository that only includes the files/folders inside the .git directory and as no working directory.
    The best practice is when you want to push changes, don’t push to a non-bare repository. If you’re going to push changes, make a bare repository clone with Git clone --bare

    On the client: git remote add @:

    push local tree to server: git push [branch]
    git rm file -- remove
    git rm -cached file -- untrack
    git mv file path/file -- rename

    Git does not record renames but mv deletes and stores under a different name
    git log -p HEAD~2.stable
    git show HEAD~2
    git show HEAD.file

    Working with branches

    Useful commands for working with branches
    git branch [-a] Show available branches. Currently active branch is marked with *
    optional -a also shows remote branches
    git branch <name> Create a branch
    git checkout <name> Check out (or.. switch to) an existing branch
    git checkout -b <name> Create and immediately switch to the new branch
    git fetch
    git checkout -t <name>
    Create a working copy of a remote branch
    git merge <branch> Merge given branch into the currently checked-out branch

    For pulling all branches from a remote repo, use 'git fetch origin' first.

    Be careful when pushing branches to a remote repository!
    git push origin myBranch
    This will push the code from the branch you are locally working in to the branch myBranch on the remote repository, possibly causing un unwanted merging of two branches. So pay attention to where you come from and where you go to.

    Use cases of branches

    Go back in history to a point where you want to make a change. First checkout that specific commit by its HASH and make a branch (e.g. "oldstuff") where you can freely make changes. When you want to go back to the latest version, i.e the most recent commit use 'git checkout <branchname>'
    
    git log -- to find the commit you want to go back to
    git checkout ..a840083h17u803..
    git checkout -b oldstuff -- create a branch for making changes to old stuff
    

    Using hooks on a remote repository

    The idea is to create a mechanism for working with several team members on a remote repository and still be able to automatically perform some post-commit actions like installing the newly committed changes on a remote web site or render a document.

    Prerequisites

    Link your local repository to the remote using
    
    git remote add  
    
    where
  • origin is the name you want to indicate the remote repository with, e.g. when using "git push origin"
  • url is the full url for the remote repository In the remote repository you will find a directory **hooks** which contains a number of examples. Create your own version of post-receive with the following contents:
    
    #!/bin/sh
    GIT_WORK_TREE=/home/me/public_html/mywebsite Git checkout -f
    
    ... and don't forget to make it executable:
    
    chmod 755 post-receive
    
    That should be all. Try it out by pushing some change in your local repository to the remote and see if it indeed checked out a copy of your files.

    Submodules

    See this...

    Primary set of git commands

           git-clone
               Clone a repository into a new directory.
    
           git-init
               Create an empty git repository or reinitialize an existing one.
    
           git-log
               Show commit logs.
    
           git-add
               Add file contents to the index.
    	   to undo: "git reset HEAD <file>" will remove a file from
    	   the index but leave the working copy intact
    
    
           git-commit
               Record changes to the repository.
    
           git-checkout
               Checkout a branch or paths to the working tree.
    
           git-diff
               Show changes between commits, commit and working tree, etc.
    
           git-status
               Show the working tree status.
    
           git-diff-files
               Compares files in the working tree and the index.
    
           git-diff-index
               Compares content and mode of blobs between the index and repository.
    
           git-diff-tree
               Compares the content and mode of blobs found via two tree objects.
    
           git-ls-files
               Show information about files in the index and the working tree.
    
           git-ls-tree
               List the contents of a tree object.
    

    Secondary set of git commands

           git-branch
               List, create, or delete branches.
    
           git-merge
               Join two or more development histories together.
    
           git-mv
               Move or rename a file, a directory, or a symlink.
    
           git-pull
               Fetch from and merge with another repository or a local branch.
    
           git-push
               Update remote refs along with associated objects.
    
           git-fetch
               Download objects and refs from another repository.
    
           git-reset
               Reset current HEAD to the specified state.