Git tutorial exercise - fomics/SummerSchool2013 GitHub Wiki

Introduction

The aim of this session is to introduce users to the main concepts and practices of day to day git usage by providing some steps and excercises to follow. Unfortunately, the summer school software components are split into several modules (repositories) which makes the git usage here a little more advanced than one would prefer for an introductory course. Rather than simplify things into a single repository, we decided to leave things as they are since students may well find themselves confronted by a similar codebase in the near future.

It is highly recommended that students visit pages such as this git tutorial to learn more details about git and the way it works.

Before doing anything else, make sure we have access to git

module load git

because the default version of git on the machine todi is quite old and we prefer to use a new version for the course.


Cloning the Summer School repository to create your local copy

git clone --recursive https://github.com/fomics/SummerSchool2013.git
cd SummerSchool2013

(We use the recursive option because this git repository contains submodules which are themselves git repositories and we wish to check them all out in one go). By default, you will receive the SummerSchool master branch. You will have 4 repositories delivered when this completes (SummerSchool2013, SWE, swe_solvers and geoclaw), arranged as one top level repository, with a submodule, which in turn has two further submodules.

SummerSchool2013
    | 
    - SWE
        |
        - swe_solvers
        |
        - geoclaw          

Adding some local repositories

The 4 repositories setup for this course are located on github, but setting accounts for all students to push to this github account would be painful, so instead, there is a clone of each repo on the CSCS machine. Let's add one CSCS remote to each of our repos ... (a handy script has been created to do this for you, but before you run it, have a look at what it is doing).

cd PRESENTATIONS/git
cat addCSCSrepos.sh
source addCSCSrepos.sh

The contents of the addCSCSrepos script are

cd ../../SWE
git remote add todi todi.cscs.ch:/users/biddisco/gitrepos/SWE
git fetch todi
cd submodules/swe_solvers/
git remote add todi todi.cscs.ch:/users/biddisco/gitrepos/swe_solvers
git fetch todi
cd ../geoclaw/
git remote add todi todi.cscs.ch:/users/biddisco/gitrepos/geoclaw
git fetch todi
cd ../../../PRESENTATIONS/git

The script will change into each of the submodule directories and add a remote called 'todi'. These are just copies of the repos on the fomics github/webpage, but you have access to push to them without extra priviliges. (NB. We use the 'source' command here so you can see what commands are being executed and what git is doing rather than just executing a script where you see nothing).

The organisers would like to apologise for using such a complicated setup (you now have 7 git repositories configured already! - the 4 cloned original ones, plus copies of the 3 submodules, we aren't making an extra clone of the root as you won't need to change anything there). We hope this complicated setup improves your experience in the long term.

Setting up git aliases, diff/merge tools, command prompt and type-completion

From the PRESENTATIONS/git directory, have a look at these aliases, they're very useful and save a lot of time
cat config-git

you will see these aliases

[alias]  
        st = status
        wc = whatchanged
        dn = diff --name-only
        df = diff
        br = branch
        ba = branch -a
        rs = remote show
        ci = commit  
        cp = cherry-pick
        co = checkout
        mb = merge-base
        unadd = reset HEAD --
        l5 = log -5
        l1 = log --pretty=oneline
        lp = lop -p
    # these ones are slightly special as they use non git commands (using the ! prefix)
        la = "!git config -l | grep alias | cut -c 7-"
        k = !gitk --argscmd='git for-each-ref --format=\\"%(refname)\\" refs/heads refs/tags'
        ka = !gitk --all        

Notice that there are also some definitions to add diff and merge tools to your gitconfig. These have been setup specially for the course, and although you might want them on your local machine (during or after the course), the paths will need to be configured correctly as these ones will only work for the machine todi, so you're better off using google to tell you how to do it. We're using meld (for diffs) and kdiff3 (for 3 way merges when resolving conflicts).

One extra setting we've added is to set the default push mode to current. What this means is that when you execute git push, by default git will only push the current branch to the remote and not every branch. Instead of editing the ~/.gitconfig file in this way, one could also have used

git config --global push.default current

You can edit any of the global configuration settings in the same way. The ~/.gitconfig file just contains a set of options which can be edited by hand, or using the git config command (not that git config --local xxx can be used to change settings only for the current repo in which you are working, but we won't use that during the course.

When you make commits, git will want you to supply a username and email address, so set these up once using

git config --global user.name "User Name"
git config --global user.email [email protected]

Also have a look at what we're going to add to your bash startup to make your prompt show your git branch and auto-completion of typing when you're trying to remember commands/branches/etc.

cat config-bash

and now lets set everything up. From the PRESENTATIONS/git directory, execute the script ONLY ONCE!

source setup.sh

If you execute this more than once, it will concatenate the commands to your terminal session initialization multiple times and make a mess. To clean up mistakes, just delete the duplicated lines from ~/.bashrc and ~/.gitconfig respectively.

To activate your new 'git enabled' command prompt, the easiest thing to so is logout and log back in, remember to connect using ssh -Y [email protected] to enable X forwarding so that the diff/merge tools we have installed are able to display a gui on your remote desktop - (NB. if you logged from your machine, to ela and then to todi, you'll need to use X forwarding for both connections).


Creating your own branch and doing something with it

Change into the SummerSchool2013/SWE directory where you are now inside a submodule. This submodule is the Shallow Water Equation repository which we've hosted 'inside' the SummerSchool repository. The SummerSchool repo is just a place for us to hold presentations etc, the real code is in SWE and its submodules. If you type the commands

cd ../../SWE (or cd /path/to/SummerSchool2013/SWE)

git branch -a

You will see a number of branches which are present on the origin (github) and a number which are present on todi (copied from github, but possibly modified). You should also see that you're not currently on any branch. This is an annoying feature of the way git handles submodules. It's called a 'Detached Head' and just means you're on a commit, but that commit isn't marked as being a branch tip, even if it is actually the same point as a genuine branch tip. (When you have a submodule, you have to tell the parent repo which commit to use for the submodule and this can be committed to the parent repository just like any other commit. It's confusing at first).

To begin our excercises, we would like to check out the gitcourse1 branch from todi, so use the following command

git co -t todi/gitcourse1

The -t option tells git to track this branch so if you push it, it goes back to where it came from. In general when you checkout someone else's branch, you'll want to use the -t option so that whenever you make a pull or push request on the branch, it operates on the same remote branch you originally copied from.

Now create your own branch which we will fork off from gitcourse1, use a name based on your user, like user_001 (or similar), use the command (remember that git co is an alias for git checkout)

git co -b BRANCH_NAME (you insert your chosen branch name instead of BRANCH_NAME).

The -b option says, checkout a new branch with this name. When you switch between branches that already exist, you checkout without the -b option. Try the following

git co master
git co BRANCH_NAME

right now, your branch has nothing on it that is interesting. if you type

gitk --all (or its alias git ka) then you will see how your new branch sits on the existing ones.

Try creating a new file and committing it. Between steps, use the git status to see how things change.

vi make_up_a_file_name.txt (add some text to this file, you may use another editor if you wish).

git st

See how git tells you there is a new untracked file present. It is untracked because you just created it and git does not yet mark it as being part of its files.

git add make_up_a_file_name.txt
git st

Now you see that you have added the file to git's tracking and it is marked as ready for commit.

git commit
git st
git log

After typing git commit (or alias git ci) you'll get an editor where you must enter a text message to say what this commit represents. git st will now show the repository as clean, but git log will show that the head of your branch is now your latest commit and you'll see your message as the commit log.

Preparing for a merge

You have successfully commited something to your branch, the next step is to make a change to a file and then merge changes which other users have made to the same file. In the SWE directory, edit the file example.txt. You must make 3 changes as follows

  • Add your name to the list of authors
  • Replace the contents of the currently empty license section with a brief message stating that anyone can do anything they want with the code.
  • Edit the description section by adding some useful purpose of your own choosing (i.e. make something up).

Once you've edited the file, add it to the set of changes to commit, and then commit it with a suitable commit message when you are prompted.

git add example.txt
git commit

and now just to check that all has gone well, try viewing the diff between the repo before and after the commit.

git diff HEAD~

This means, show me the diff between the current state and the parent(~) of HEAD. (Note that when a merge takes place, a commit might have two parents, where you use ^ instead of ~ to get 2nd parent). The command above is the same as

git diff HEAD~ HEAD

in this case because we have a clean tree with no changes in it since we last committed. But do be careful because if you try

git diff HEAD HEAD~

you'll see the diffs the other way around, get used to this because when you start merging changes you need to remember which is which. Note that a quite concise blog entry for exploring parents and grandparents of commits is here.

Merging branches and handling conflicts

You have made changes to the text file and now you wish to merge in changes that someone else has made to the same file. We've prepared a branch to merge in, it's called gitcourse3. Before you merge that branch into yours, have a look at the differences beteen your current HEAD and the other branch

git diff todi/gitcourse2 HEAD (or just git diff todi/gitcourse2)

See how your changes and the ones in the other branch are probably going to cause trouble. Try a merge

git merge todi/gitcourse2

you should get a conflict in the file example.txt, have a look at it, by doing

cat example.txt

and you'll see markers where you (=HEAD, because you are merging changes from elsewhere into your HEAD or branch tip) and the branch you're merging in (=gitcourse2) differ.

<<<<<<< HEAD
...
=======
...
>>>>>>> gitcourse2

You can edit these conflicts by hand using a text editor until you're happy that the file looks as you want it to, but for real fun, use

git mergetool

to fire up kdiff3 - and now you can see the places where conflicts have occured. Select A,B,C for each region that needs resolving. A is the original before either set of changes, B is yours (local), and C is theirs (remote).

When you've fixed it all up. Quit with a save and the command prompt will tell you that you're still in a merge. This is because although you've fixed the conflicts, you haven't commit the final result.

git add example.txt
git commit

An editor will appear showing that this is a merge commit where 1 conflict was resolved. You can just leave this text as is (or change it if you prefer) and once the commit is done, you've got a new clean merged branch. Have a quick look at the branches using

git ka (or gitk --all)

and now you've completed the introductory git course.