Getting Started

The first thing to do before using git is to configure it. This only needs to be done once.

In the command line, type the following code, line by line, replacing #<XXXX># with your personal information:

git config --global user.name #<A USERNAME>#
git config --global user.email #<YOUR EPFL EMAIL>#

Next, if you like color in the terminal, you can add:

git config --global color.diff auto
git config --global color.status auto
git config --global color.branch auto

Let's also add some aliases. To do this, create/edit a ~/.gitconfig file (i.e., a file named ".gitconfig" in the root of your home directory.
On CO machines, don't forget to to copy it to your myfiles directory in order not to loose once logged off).
Add the following lines to it:

[alias]
    lg = log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
    glog = log --graph --decorate --oneline --all
unstage = reset HEAD --
    last = log -1 HEAD

Basic Concepts

Before diving into using git, you need to understand its purpose and logic:

  • the goal is to collaborate on some shared content and to archive different versions of it (git is a "version control system");

  • the key idea is to have three abstraction levels:

    1. a shared reference repository (called a "repo" or "main server");
    2. a local repository specific to each user, serving as a local reference (which you don't see);
    3. a local working directory where you can make and undo changes without any consequence as long as you haven't committed them to level 2; this is the directory you've cloned or will clone with git clone and where you'll be working (each of you).

Level 2 is actually quite "abstract" in the sense that you don't see it concretely; it is entirely managed by git commands.

To move something from level 1 to levels 2 and 3 at the same time (i.e., retrieve something made available by others on the main server), you use (all these commands are detailed below; here we're focusing on the concepts):

git pull

I recommend doing this fairly regularly and, in any case, always before your commit/push (explained below).

To commit something locally, i.e., move from level 3 to level 2 only:

  • either, to commit a new version of an existing file:

      git commit -m "MESSAGE" FILE

    be sure to include a relevant message each time;

    e.g., to commit a new version of the core.c file:

      git commit -m "fixed calculation bug" core.c
  • or, to add a new file (all of this is explained in details below):

      git add FILE
      git commit -m "added EXPLANATION"

    e.g., to add the new file io.c:

      git add io.c
      git commit -m "added input/output"

    Note: There is no need to repeat the filename in the commit following one or more add commands; this actually allows you to add multiple files at once; for example:

      git add io_core.c
      git add io_errors.c
      git add io.h
      git commit -m "adding input/output"

To push your local changes to the repository, i.e., move from level 2 to level 1:

git push

We therefore emphasize that to publish a local change to everyone, you must do TWO things:

git commit

then

git push

Let's go over all of this (and more) in details.

Git keeps all successive states (snapshots)

Let's look at the first principle of git: archiving the work done.
To do this, create a directory and go into it:

mkdir alice
cd alice

Add a file to it:

echo "This is a README file" > README.md

then archive the current state of this directory in git:

git add .
git commit -m "Initial commit: README"
  • git add allows you to propose (without yet confirming) a change;
    here we added the entire current directory using its short name: " .", but we could also have added just one file, for example:

      git add README.md

    We DO NOT RECOMMEND using git add ., as this often adds a lot of unwanted items: all files in the current directory, including temporary files, drafts, etc.

    We also recommend running git status BEFORE performing your git commit to carefully verify what you are adding.

  • git commit confirms the (local) saving of the proposed changes;
    it is strongly recommended (if not mandatory ;-)) to comment on your changes by adding a message to the commit; this is what the -m option does.

If something has been modified, Git can tell you:

echo "A second line for the README" >> README.md
git status

We could propose adding this new change for a future commit:

git add README.md

This two-steps (and later three-steps, as we'll see in a moment) process may seem tedious, but it's a good safety net against mistakes and a good way to do things little by little, one at a time.

Once you're ready to save (locally) your changes, make a commit...
...without forgetting to add a relevant comment with -m:

git commit -m "Adding a second line to the README file"

With Git, you can view all the saved states (snapshots) and even move from one to another (but this is more advanced and you shouldn't need it):

git log

or:

git lg  # if you defined the alias earlier...

To switch (used here for illustration purposes, but it's not necessary to understand this part for this course):

git checkout 5d340  # Choose an APPROPRIATE commit number here, an older one
cat README.md

Notice that this is an older version. Let's return to the current state:

git checkout master
cat README.md

You now understand the concept of Git snapshots, and thus the difference between the current working directory and the repository.

The second concept you need to understand is that there are TWO repositories.
Git allows multiple people to work together (see next section) and uses two different repositories for this:

  • a local repository, which we have been using so far;
  • a global repository, on the chosen Git server, where everyone working on the same project will merge their changes.

To "push" your locally saved changes (with commit) to the central server, you must do:

git push

I strongly recommend that you first run a

git pull

before each of your

git push

The pull command synchronizes in the other direction: it fetches the changes saved on the server and applies them locally.

More details below...

Git can handle different "concurrent" states

Git is first and foremost a collaborative tool that many people use precisely to work "in parallel." It is therefore designed to facilitate the management of "concurrent" (or at least "parallel" ;-)) changes.

Suppose Alice has a collaborator, Bob, on her project, and he has made changes on his end:

echo "Hey, this is a line added by Bob" >> README.md
git commit -m "Add greeting from Bob" README.md

Meanwhile, Alice continued working in parallel:

sed -i 's/This is a README file/This is a README file with a twist/' README.md
git commit -am "Add a twist to the first line of the README"

Where are we now? What are the Git states?

Starting from the last common state (the last pull on both sides), there are actually two distinct commits:

  • one locally on Bob's machine (who doesn't see Alice's new commit yet);
  • and one locally on Alice's machine (who doesn't see Bob's new commit yet).

So far, there's no confusion.

Alice and Bob can now collaborate by sharing their contributions. Let's assume Bob "pushes" first (no need to pull here):

git push

When Bob does this, the central server receives and saves Bob's change. No problem here.
A git lg on Bob's side shows that origin/master (the server's) and master (the local one) are now the same.

Alice, on her end, doesn't know that Bob's change has been propagated to the central server. When she tries to "push" her changes to the server:

git push

she encounters a problem: a message tells her that her push has failed and that she must first fetch the changes stored on the central server...
Which she obediently does:

git fetch

With a

git lg

on her side, Alice sees that she now has two commits:

  • one called origin/master, which corresponds to Bob's;

  • and another called master or HEAD, which corresponds to hers.

If Alice wants to "push" her changes to the central server, she must first merge these two different states. If there are no conflicts (non-overlapping changes), this is done simply as follows:

git merge
# Enter a commit message...

She can check the status:

git lg

then "now" push this new state, resulting from the merge of the two changes:

git push

From there, Alice and the central server are synchronized.

For Bob to be synchronized as well, he must also run:

git fetch

or, more simply, a

git pull

This command (git pull) performs a fetch and a merge in one go.

"Tags"

A "tag" is simply a name given to a saved state (snapshot).
We won't be using them specifically in our labs. But you can use them, if you wish, to label a specifiv version of your work; typically to keep track of a stable version. But that's a minor detail.

To tag the current state, simply run:

git tag -a TAG_NAME -m "message"

For example, if you want to name the current state "version1.1," you would do:

git tag -a version1.1 -m "Version 1.1 stable"

The command

git tag

simply lists all your "tags".

To see what a given "tag" corresponds to: git show TAG_NAME; for example:

git show version1.1

To push the tag to GitHub, add --tags to the push command:

git push --tags

Learn More

If you'd like to learn more about Git and GitHub, check out this tutorial.