Session 1: Alice starts with a private repository

Why is Alice using a version control system:

  • It is easy to manage a few files. But for 100 files and more, she needs tool support.
  • She finds web up- and download clumsy. If the file is on the web, then it is not there where she needs it for work: on her local file system
  • For files used in collaboration, she wants to know who made the change, when, and for what purpose.

Why is Alice using Git:

  • Git development was initiated by Linus Torvalds, the developer of the Linux kernel. That indicates quality.
  • For about two years (or more) Open Source Software (OSS) developers seem to prefer Git and GitHub.
  • She works for the ESA wonderland. The ESA Thematic Exploitation Platform (TEP) Reference Architecture and Standards recommend Git (see OSS Best Practices).
  • She has a friend who has partners and clients who did not choose a common standard. He had to learn them all: ClearCase, CVS, Subversion, Git, etc.
  • She knows EODC. They push collaboration and pull standards from GitLab.

Alice configures her Git client: config --global|--list

Set user's global options (on Linux):

git config --global user.name "Alice from Wonderland"
git config --global user.email alice@wonderland.net
git config --global color.ui true
git config --global core.safecrlf warn
git config --global core.autocrlf input

On Windows, set core.autocrlf to true.

Check user's global options with: git config --global --list

Alice turns a directory into a Git repository: init, add, commit

It's just init, add and commit, but to get standardization of line endings (LF), and to exclude generated files, she does a little bit more:

cd <dir>               # for example `cci_sm_demo_src`
git init
git status
git add .gitattributes
git add .gitignore
git add .
git status
git commit
git checkout HEAD -- . # remove CRLF from working directory, if any
git status
git log

Here is a procedure for automatically fixing all line endings after changing core.autocrlf option or .gitattributes file:

git status           # check status
git add . -u         # Save current files, so that no work is lost
git commit -m "Save files before refreshing line endings."
git rm --cached -r . # remove every file from index (cache, staging area)
git reset --hard     # rewrite index to pick up all new line endings
git add .            # add changed files back
git commit -m "Normalize all line endings."

Alice edits files: status, diff, add, commit, log

vi <file>

git status
git diff
git add <file>
git diff --cached
git status
git commit
git status

git log
git log --oneline
git log --name-status
git log -p

Move and remove files or directories: mv, rm

git mv <old_path> <new_path>
git rm <path>

Commit options: -m, -a, --amend

git commit -m "<message>"
git commit -a             # skip `git add` and commit all changes
git commit --amend        # DO before pushing, DON'T after pushing

Alice knows how to discard undesired changes: reset, checkout, revert

Fix cache (aka index, staging area), then working directory:

git reset HEAD <file>   # resets index, keeps changes in working directory
git checkout -- <file>  # discards changes from working directory

Restore working directory after things got messed up entirely:

git reset --hard HEAD
git reset --hard <commit> # DO before pushing, DON'T after pushing

Compensate a bad commit (can be done any time):

git revert <commit>

Session 2: Bob joins Alice to work together

[bob@sidp ~]$ git config --global --list
user.name=Bob the Builder
user.email=bob@builders.com
color.ui=true
core.safecrlf=warn
core.autocrlf=input

Bob gets a copy of Alice's repository: clone, remote, branch

cd ~/repositories
git clone --origin alice ~alice/repositories/cci_cm_demo_src

git branch        # view local branches
git branch -r     # view remote tracking branches
git branch -a     # view all branches

Bob retrieves changes made by Alice: fetch, pull

git fetch alice # updates remote tracking branches only
git pull        # merge changes into active local branch

Alice integrates changes made by Bob: remote add, remote show

Alice adds a remote alias to Bob's repository:

git remote add bob ~bob/repositories/cci_cm_demo_src
git remote -v
git remote show bob

Alice can pull in Bob's changes any time as follows:

git fetch bob         # update remote-tracking branches
git pull bob master   # merge `bob/master` into active local branch

Session 3: Alice & Bob use a Git server

Reasons:

  • Repositories by Alice and Bob may not always be available.
  • Complex topology if more friends join.
  • A maintained server has 24/7 availability and simple topology.
  • Even with a central server, each collaborator keeps the full history and does not depend on the server (they can work off-line). The architecture is fully distributed, explaining why Git is called a Distributed Version Control System (DVCS).

Alice and Bob setup their ssh keys: ssh-keygen

Alice does this (and so does Bob):

  1. Get account on a Git server, for example EODC's GitLab.
  2. Create an ssh public+private key pair with ssh-keygen.
  3. Upload public key onto Git server.
  4. Load private key into a local ssh agent.

Alice creates an empty repository on the Git server

Alice logs on to EODC GitLab (https://git.eodc.eu), navigates to a GitLab group of her choice, and presses the New project button.

She puts in the repository name (e.g. cci_cm_demo_src), a brief description, and chooses verbosity level private.

Alice puts her repository onto the Git server: remote, push

git remote add eodc git@git.eodc.eu:cci-sm-work/cci_cm_demo_src.git
git remote show eodc

git push -u eodc master:master # semantics: <local_branch>:<remote_branch>
git branch -a

The -u (--set-upstream) flag adds an upstream (tracking) reference, which is used by argument-less "git pull" and other commands.

Bob clones from the Git server: --origin

git clone --origin eodc git@git.eodc.eu:cci-sm-work/cci_cm_demo_src.git

Remote alias origin will be used if --origin flag is not given.

Session 4: Alice & Bob become Git experts by branching and merging

Alice & Bob define the branching policy

Here is a branching policy for a data centre:

 (L3) Temporary branches for hot fixes: '<issue>_hotfix'
 (L2) Permanent branch for operations system: 'ops'
 (L1) Permanent branch for test system: 'tst'
 (L0) Permanent branch for development: 'master'
(L-1) Temporary branches for feature implementation: '<feature>_impl'

Merging from upper to lower level is a safe action and can be done frequently. Merging from lower to upper level must be done with care and is considered a "delivery".

Alice creates the permanent branches: checkout -b, checkout, gitk, push -u

Branch master doesn't need to be created. It is created by Git and always there.

Create local branches (use -b flag):

git checkout -b tst                # starting from HEAD
git checkout -b ops <start_point>  # starting from start_point (e.g. commit)

Switch between branches:

git status # always make sure to have a clean working directory!
git checkout <branch>

Add commits to each branch, then use the graphical repository browser for the inspection (may sometimes be more convenient than git log and git diff):

gitk &

Push branches to Git server (use -u flag):

git push -u <remote> <local_branch>:<remote_branch>
git push -u eodc tst:tst
git push -u eodc ops:ops

Bob gets the new remote branches: checkout -t

git fetch eodc              # update remote tracking branches
git branch -a               # view list of branches
git checkout -t eodc/tst    # use -t to set up "upstream" configuration
git checkout -t eodc/ops

Bob delivers work on a feature branch: checkout -b, push -u, merge request

git checkout master
git checkout -b <feature>_impl

He does some work, then stages and commits it:

git add <file>...
git commit

git push -u eodc <feature>_impl:<feature>_impl

Finally, Bob submits a merge request to Alice (talk, send an email, or press GitLab's New Merge Request button).

Alice integrates Bob's changes: merge, tag, branch -d

git fetch eodc
git checkout -t eodc/<feature>_impl

Alice can and should inspect the changes made by Bob. She talks to him in case of disagreement.

Alice merges feature branch into master:

git checkout master
git merge --no-commit <feature>_impl # Don't abuse --no-commit!
git status
git diff

In case of conflicts, Alice resolves them. She also has the option to abort the merge. See the sections on conflict resolution and aborting below.

If everything OK, Alice commits and tags:

git commit
git tag -a -m "DCR-001: Feature XYZ" DCR-001-0

Alice pushes to Git server and removes obsolete feature branch:

git push eodc master
git branch -d <feature>_impl    # delete local branch
git push eodc :<feature>_impl   # delete remote branch

Alice notifies Bob about integration action.

Bob verifies integration: fetch --prune

git fetch eodc           # fetch is additive, it doesn't delete branches
git fetch --prune eodc   # prune obsolete remote tracking branches
git checkout master
git pull
gitk &
git branch -d <feature>_impl

Alice's options for conflict resolution: checkout --ours|--theirs|-m, add

When a git merge reports about conflicts:

  • Look at the diffs: git diff will show a three-way diff, highlighting changes from both the HEAD and MERGE_HEAD versions.
  • Look at the diffs from each branch: git log --merge -p <path> will show diffs first for the HEAD version and then the MERGE_HEAD version.
  • Look at the originals: git show :1:filename shows the common ancestor, git show :2:filename shows the HEAD version, and git show :3:filename shows the MERGE_HEAD version.

Alice can use the following commands in order to resolve conflicts:

git checkout --ours -- <file>
git checkout --theirs -- <file>
git checkout -m -- <file>        # gets conflicts back
vi <file>                        # find conflict markers and fix things

git add <file>                   # mark conflict as resolved.
git commit

Alice's options for aborting a failed merge: merge --abort, reset --hard, revert -m 1

If things go entirely wrong, the merge can be aborted before committing by one of the following commands:

git merge --abort                # requires a newer Git version
git reset --hard HEAD

Revert the merge after committing but before pushing as follows:

git reset --hard HEAD^

Revert the merge after pushing as follows:

git revert -m 1 <commit>

Session 5: Getting help and support

  • README_GIT.md in repository cci_cm_work_doc.
  • Tutorials and cheatsheets on the Internet.
  • Git manual pages: git help
  • EODC User Support and Configuration Management: A job for Bob.
  • Git homepage
  • Git book
  • GitLab