License : Creative Commons Attribution 4.0 International (CC BY-NC-SA 4.0)
Copyright : Hervé Frezza-Buet, Jérémy Fix, CentraleSupelec
Last modified : April 26, 2024 02:04
Link to the source : index.md

Versioning with GIT

Objectives

When you work with a computer, as a team or alone, you often need to save your progress, go back to a previous state of your work, allow for collaborators to contribute. This is what git is used for.

Using git is very simple, it does not require any complicated install or setup. This is why, even for a simple student labwork, handling your project directory with git is a good and lightweight idea.

The structure of git

Spend time to read the docs (homework)

This labwork makes a quite rapid presentation of git and you are recommended to read the git manual (Pro Git) available on the git homepage, as well as some nice contributions:

Git main elements

The main Git elements are :

The main element of git is a “commit”. It can be viewed as a snapshot of your entire project. This snapshot is indeed the list of the blob references corresponding to the files (at the right version) that belong to the picture. The index (or staging area) can be viewed as the current snapshot, under construction. We can progressively add elements to the index and, at some point, create a commit from the index; git then registers the snapshot and assigns it a label.

The files in the working directory can have different status:

Note that a file can be both modified and staged if, at some points, we decided to store a version of the file in the index and the file was later modified without being again added to the index.

The different status of a file. Image from progit2 available in the Pro Git book v2
The different status of a file. Image from progit2 available in the Pro Git book v2

In order to change the status of a file within the index you can call the following command :

The current state of the index (and the working directory) can be seen by the command git status

When you are ready to take a snapshot of the current state of the index, you can do it (and save the snapshot) by creating a “commit”. The command is git commit -m "some message". You are expected to write an informative commit message.

Git commit from xkcd
Git commit from xkcd

The commit is a bit more than a snapshot of the index. As a snapshot, it consists of a list of blob names, i.e. the list of the files in a specific version. It also contains an information about some “parent” commit (there are sometimes several parents). A parent commit of a commit can be seen as the snapshot that was taken just before. So examining a commit, its parent(s), the parent(s) of its parent(s)… we can unroll the history of your project modifications.

The elements gathered by git are a bag of blobs, a bag of commit, each one referencing a list of blobs and some parent commit(s). That is mainly it !

Handling commits

Setup

Once you know the reference of a commit, you can ask git to “realize” it in the working directory, in order to retrieve (we say checkout) the project state corresponding to that snapshot. Then, git writes or overwrites in the working directory the files whose blobs are listed in the commit, i.e. it restores the version of all the files mentioned in the commit.

Nevertheless, the idea is not to be able to list all the commits i,wn the “bags of commits” handled by git and ask for the realization of one of them in the working directory. Commits are indeed accessible by “branches”.

A branch is nothing more than a reference to one specific commit. So when you have a branch at your disposal, you can access to the commit it refers… but also, from that commit, to its parent(s), and the parent(s) of the parent(s)…. recursively.

In other words, a branch is an entry point to some snapshot of your project that enables you to see all the history until this point.

It is time to do some experiments. In some directory, type

mylogin@mymachine:~$ mkdir git_labwork
mylogin@mymachine:~$ cd git_labwork
mylogin@mymachine:~/git_labwork$ mkdir getting_started_project
mylogin@mymachine:~/git_labwork$ cd getting_started_project
mylogin@mymachine:~/git_labwork/getting_started_project$ git init

git may refuse to do anything while you have not filled your name and e-mail in the global configuration files. Do it when you are asked to, git tells you how to proceed.

Now, the directory getting_started_project is the git working directory, and all the necessary stuff to handle versioning of that directory is set up (it is indeed hidden in the .git sub-directory)

mylogin@mymachine:~/git_labwork/getting_started_project$ ls -al

Let us create three empty text files in the directory, and see that they are untracked. Execute the following commands one by one.

mylogin@mymachine:~/git_labwork/getting_started_project$ touch animals.txt objects.txt concepts.txt
mylogin@mymachine:~/git_labwork/getting_started_project$ git status

We have just created three empty files in our working directory.

Three untracked files in the working directory. Generated with the help of visual-git-guide
Three untracked files in the working directory. Generated with the help of visual-git-guide

Adding to the Index

We now build up our first snapshot by adding entries into the index.

mylogin@mymachine:~/git_labwork/getting_started_project$ git add animals.txt
mylogin@mymachine:~/git_labwork/getting_started_project$ git status
mylogin@mymachine:~/git_labwork/getting_started_project$ git add objects.txt concepts.txt
mylogin@mymachine:~/git_labwork/getting_started_project$ git status

Now, the three files are in the index.

git add copies files from the working directory into the index. Generated with the help of visual-git-guide
git add copies files from the working directory into the index. Generated with the help of visual-git-guide

Edit animals.txt, write “fish” in it, and save. Then try

mylogin@mymachine:~/git_labwork/getting_started_project$ git status

Git tells you that you have added previously this file in the index, but that the working directory contains a newer version. As git suggests, you can either overwrite that new version with the one that is currently in the index (i.e. without the fish), or, on the contrary, overwrite the file in the index with the new version of the file that is in the working directory. The former is the checkout command, the latter, that we will actually do, is the add command.

mylogin@mymachine:~/git_labwork/getting_started_project$ git add animals.txt
mylogin@mymachine:~/git_labwork/getting_started_project$ git status

Let us see that we have no commit yet and no branch. To see the currently available branch (i.e. entry points to commit histories), type

mylogin@mymachine:~/git_labwork/getting_started_project$ git branch

You see… nothing !

Committing

Ok, let us build our first commit. Let us call it “c-001”. Usually, give it a name corresponding to what you have done. Here, it should rather be “Creation of first .txt files”.

mylogin@mymachine:~/git_labwork/getting_started_project$ git commit -m "c-001"

Now, retry

mylogin@mymachine:~/git_labwork/getting_started_project$ git branch
mylogin@mymachine:~/git_labwork/getting_started_project$ git status

The command git branch shows that git has automatically created a branch for you. This branch is indeed an entry to your first commit. It is named “master”. The last commit is indeed the commit we have just created. In git, there is a variable called “HEAD” that corresponds to the “commit currently considered”. Here, HEAD refers to the commit referred to by branch “master”. This is why you have a symbol "*" in front of the branch name “master”.

git commit registers a snapshot by making a copy of what was in the index when the command was issued. Generated with the help of visual-git-guide
git commit registers a snapshot by making a copy of what was in the index when the command was issued. Generated with the help of visual-git-guide

Note that now, the index and the commit “c-001” refer to the same blobs, i.e. the same version of the three *.txt files that are stored in the git internal database. Let us go further in the history.

Modify the objects file and add a “lamp” in it. Let us take a new snapshot of this new version of our project. We should add the modified file in the index and then take a snapshot, like this (do not do it)

mylogin@mymachine:~/git_labwork/getting_started_project$ git add object.txt
mylogin@mymachine:~/git_labwork/getting_started_project$ git commit -m "c-002"

But the commit command has a nice “a” option that tells to automatically put the last version of modified tracked files in the index before actually committing. So instead of the previous lines, you can rather type

mylogin@mymachine:~/git_labwork/getting_started_project$ git commit -am "c-002"

This has many effects :

We can display this by typing

mylogin@mymachine:~/git_labwork/getting_started_project$ git log --oneline

The code at the beginning of the line are the commit identifiers, the “(HEAD -> master)” string means that the HEAD is on the branch “master”, i.e. the commit referenced by that branch. Then, git log recursively explores the commits by accessing their parents. This is why, from commit “c-002”, we can see “c-001”.

git commit issued after modifying objects.txt and adding it into the index. The parent of this commit is where the HEAD was at the time of committing. Generated with the help of visual-git-guide
git commit issued after modifying objects.txt and adding it into the index. The parent of this commit is where the HEAD was at the time of committing. Generated with the help of visual-git-guide

Branching

Let us now create another branch. It consists of creating another reference to the commit designated by HEAD. Let us call this reference “farm”, since we will add farm-related stuff in our text files.

mylogin@mymachine:~/git_labwork/getting_started_project$ git branch farm

creates the new branch. Let us see it.

mylogin@mymachine:~/git_labwork/getting_started_project$ git branch

Both branches “farm” and “master” points to the same commit “c-002”. The HEAD is still associated to the branch “master”. If we make a new commit, as the head is associated to the branch master, the new commit will be added as the son of “c-002”, the HEAD and the branch master will be shifted to it. Indeed, we rather want the branch “master” to be still referencing commit “c-002”, and the branch “farm” to be shifted (with the HEAD as well) to our new farm related commit. To do so, we only need to tell git that the next shift will rather concern the branch farm.

mylogin@mymachine:~/git_labwork/getting_started_project$ git checkout farm

Check with

mylogin@mymachine:~/git_labwork/getting_started_project$ git branch

that the "*" symbol has moved. In the text files, add farm related elements. Then, commit this change

mylogin@mymachine:~/git_labwork/getting_started_project$ git commit -am "c-003 farm elements added"

Add other farm animals in animals.txt and commit this new change.

mylogin@mymachine:~/git_labwork/getting_started_project$ git commit -am "c-004 farm extra animals added"

Type

mylogin@mymachine:~/git_labwork/getting_started_project$ git log --oneline

To see the new history. All along that history, you visit commit “c-002” and git reminds you that is is actually the commit pointed by branch “master”.

After adding and commiting changes while having the HEAD on the newly created branch farm. Generated with the help of visual-git-guide
After adding and commiting changes while having the HEAD on the newly created branch farm. Generated with the help of visual-git-guide

Let us put back the HEAD to be associated to “master” (and consequently pointing commit “c-002”).

mylogin@mymachine:~/git_labwork/getting_started_project$ git checkout master

and check the status

mylogin@mymachine:~/git_labwork/getting_started_project$ git branch
mylogin@mymachine:~/git_labwork/getting_started_project$ git log --oneline
mylogin@mymachine:~/git_labwork/getting_started_project$ more animals.txt

As you see when inspecting the file animal.txt, moving the head to another commit makes git modify the files in your working directory from the blobs listed in that commit. Let us add a “crocodile” in animal.txt. Then commit.

mylogin@mymachine:~/git_labwork/getting_started_project$ git commit -am "c-005 crocodile"

Add a “snail” in the file animal.txt, and commit.

mylogin@mymachine:~/git_labwork/getting_started_project$ git commit -am "c-006 snail"

and check the status

mylogin@mymachine:~/git_labwork/getting_started_project$ git log --oneline

Indeed, there is a more informative way to check all branches.

mylogin@mymachine:~/git_labwork/getting_started_project$ git log --all --decorate --oneline --graph
After adding and commiting changes while having the HEAD on branch master, we get diverged histories. Generated with the help of visual-git-guide
After adding and commiting changes while having the HEAD on branch master, we get diverged histories. Generated with the help of visual-git-guide

In commit “c-005”, we have added a dangerous animal. Let us create a branch from here, in order to add other dangerous things. We have to go back, i.e. place the HEAD pointer to commit “c-005”. This commit is not a commit designated by a branch. This can be done with the following command, where xxxx should be replaced by the identifier of your commit “c-005”.

mylogin@mymachine:~/git_labwork/getting_started_project$ git checkout xxxx

Git says that our HEAD is “detached”. It means that it is no more associated to a branch. Let us create a branch from there

mylogin@mymachine:~/git_labwork/getting_started_project$ git branch dangerous

Then, modify your text files and add dangerous things in them. Commit this

mylogin@mymachine:~/git_labwork/getting_started_project$ git commit -am "c-007 dangerous"
mylogin@mymachine:~/git_labwork/getting_started_project$ git log --all --decorate --oneline --graph

As you can see, we have made a mistake. We have actually created a branch, but the commit “c-007” is not a part of it. Indeed, we have forgotten to checkout that branch in order to tell HEAD to be associated with it.

Creating a branch without a checkout on it. Committing with the head detached creates the commits but does not move the branch reference. Generated with the help of visual-git-guide
Creating a branch without a checkout on it. Committing with the head detached creates the commits but does not move the branch reference. Generated with the help of visual-git-guide

Let us undo the last commit.

mylogin@mymachine:~/git_labwork/getting_started_project$ git reset --soft HEAD~
mylogin@mymachine:~/git_labwork/getting_started_project$ git log --all --decorate --oneline --graph

The --soft flag tells to not alter the files in the working directory. The HEAD~ tag means “parent of HEAD”. We could have used the commit reference instead. Now, let us do our commit “c-007” right.

mylogin@mymachine:~/git_labwork/getting_started_project$ git checkout dangerous
mylogin@mymachine:~/git_labwork/getting_started_project$ git commit -am "c-007 dangerous"
mylogin@mymachine:~/git_labwork/getting_started_project$ git log --all --decorate --oneline --graph

Merging

Now let us gather all the partial modifications. This is the merging operations. Let us consider the master branch as the final one. So let us first go there.

mylogin@mymachine:~/git_labwork/getting_started_project$ git checkout master
mylogin@mymachine:~/git_labwork/getting_started_project$ git merge dangerous

Git is supposed to create a new commit, that corresponds to the merge of all file contents. If the merge cannot be done automatically, you have to manually edit the files and then commit your change… that will be the commit corresponding to the merge. Git tells you which files are in conflict and modifies their content so that you can easily see where is the problem when you edit the files.

Once the editing of conflicting files is done

mylogin@mymachine:~/git_labwork/getting_started_project$ git commit -am "c-008 dangerous merged to master"
mylogin@mymachine:~/git_labwork/getting_started_project$ git log --all --decorate --oneline --graph

Note that this new commit has two parents. Once again, this commit may have been done automatically if git was able to merge the two parent commits without requiring any help from you. This is typically the case when the two branches differ on distinct files.

If you do not intend to add commits from the commit that is still referencing “c-007”, you can delete the branch “dangerous” (not the commit it refers).

mylogin@mymachine:~/git_labwork/getting_started_project$ git branch -d dangerous
mylogin@mymachine:~/git_labwork/getting_started_project$ git log --all --decorate --oneline --graph

Let us merge the “farm” branch to the “master” branch (we are still in that branch)

mylogin@mymachine:~/git_labwork/getting_started_project$ git merge farm

If needed, solve the conflicts and commit the merge result. Then you can delete the branch “farm”.

mylogin@mymachine:~/git_labwork/getting_started_project$ git branch -d farm
mylogin@mymachine:~/git_labwork/getting_started_project$ git log --all --decorate --oneline --graph
The dangerous and farm branches have successfully merged and deleted. Generated with the help of visual-git-guide
The dangerous and farm branches have successfully merged and deleted. Generated with the help of visual-git-guide

Remote repository

One of the smartest features of git, as for any versioning system, is the ability to collaborate with others. The principle of git is that every collaborator hosts his/her own version of the shared repository, and s/he synchronizes it with others explicitly.

In this case, all the collaborators need a meeting point, i.e. a place where a common version can be shared. Such a place is a “bare” git repository : git repository without any index nor working directory.

Usually, your institution (e.g. gitlab-student.centralesupelec.fr), free providers (e.g github), provides you with the ability to create or join bare repositories. But you can also create bare repositories on your drive which is what we are going to do for the purpose of this tutorial.

Keep the terminal where we have worked so far open, let us name it “terminal 1”. Open a new one. In the following, some_path means the path of the directory where git_labwork is.

Let us create a bare repository. The usage is to name the repository with a .git suffix.

mylogin@mymachine:~$ cd some_path/git_labwork
mylogin@mymachine:some_path/git_labwork$ mkdir shared_repository.git
mylogin@mymachine:some_path/git_labwork$ ls
mylogin@mymachine:some_path/git_labwork$ cd shared_repository.git
mylogin@mymachine:some_path/git_labwork/shared_repository.git$ git --bare init

Now, let us create the working space of the collaborator “Juliet”. It is obtaines by “cloning” the bare repository.

mylogin@mymachine:~$ cd some_path/git_labwork
mylogin@mymachine:some_path/git_labwork$ mkdir juliet
mylogin@mymachine:some_path/git_labwork$ cd juliet
mylogin@mymachine:some_path/git_labwork/juliet$ git clone some_path/git_labwork/shared_repository.git
mylogin@mymachine:some_path/git_labwork/juliet$ cd shared_repository

Let us name this terminal “terminal Juliet”. As git says, we have cloned an empty repository, which is usually not the case when you are cloning a repository because you are joining a collaborative project. Let us put what we have done so far in the bare repository so that Juliet can get it.

Swich to “terminal 1”, we are in our initial project. Let us declare a distant repository.

mylogin@mymachine:~/git_labwork/getting_started_project$ git remote add shared some_path/git_labwork/shared_repository.git/
mylogin@mymachine:~/git_labwork/getting_started_project$ git remote -v

We have declared a remote repository, providing its location and giving it the name “shared”. The second line shows the remote repositories that git knows for this project, telling that we can both fetch commits from it or push commits to it.

Switch to “terminal Juliet”, and type

mylogin@mymachine:some_path/git_labwork/juliet/shared_repository$ git remote -v

You will see that, as this repository has been created by cloning another one, it has automatically a declared remote repository. The default name for it is “origin”.

Let us switch back to “terminal 1”

We can push our master branch to the remote repository (named “shared” here).

mylogin@mymachine:~/git_labwork/getting_started_project$ git push shared master

And that’s it. Switch to “terminal Juliet”, and get the new branch available in the shared (bare) repository.

mylogin@mymachine:some_path/git_labwork/juliet/shared_repository$ git pull
mylogin@mymachine:some_path/git_labwork/juliet/shared_repository$ git log --all --decorate --oneline --graph

Open a third terminal, that we will name “terminal Romeo”, and type:

mylogin@mymachine:~$ cd some_path/git_labwork
mylogin@mymachine:some_path$ mkdir romeo
mylogin@mymachine:some_path/git_labwork/romeo$ cd romeo
mylogin@mymachine:some_path/git_labwork/romeo$ git clone some_path/git_labwork/shared_repository.git
mylogin@mymachine:some_path/git_labwork/romeo$ cd shared_repository
mylogin@mymachine:some_path/git_labwork/romeo/shared_repository$ git log --all --decorate --oneline --graph

Being Romeo, add the word “love” in concepts.txt, save and commit :

mylogin@mymachine:some_path/git_labwork/romeo/shared_repository$ git commit -am "c-010 I'm in love"
mylogin@mymachine:some_path/git_labwork/romeo/shared_repository$ git log --all --decorate --oneline --graph

At this stage, Juliet does not know that change. You can see it from the log, since git tells you where the remote HEAD (i.e. origin/HEAD) is. Let us make Juliet know Romeo’s modification. Staying in the “terminal Romeo”, type :

mylogin@mymachine:some_path/git_labwork/romeo/shared_repository$ git push
mylogin@mymachine:some_path/git_labwork/romeo/shared_repository$ git log --all --decorate --oneline --graph

Switch to “terminal Juliet”, and type (one by one, as usual) :

mylogin@mymachine:some_path/git_labwork/juliet/shared_repository$ git log --all --decorate --oneline --graph
mylogin@mymachine:some_path/git_labwork/juliet/shared_repository$ git pull
mylogin@mymachine:some_path/git_labwork/juliet/shared_repository$ git log --all --decorate --oneline --graph

Add the name “reciprocity” in concepts.txt, commit and push.

mylogin@mymachine:some_path/git_labwork/juliet/shared_repository$ git commit -am "c-011 I love you too Romeo"
mylogin@mymachine:some_path/git_labwork/juliet/shared_repository$ git log --all --decorate --oneline --graph
mylogin@mymachine:some_path/git_labwork/juliet/shared_repository$ git push
mylogin@mymachine:some_path/git_labwork/juliet/shared_repository$ git log --all --decorate --oneline --graph

Switch to “terminal Romeo”.

Add a new file musics.txt, edit it and put “Sunday Bloody Sunday” in it. Save. Let us commit and push this change.

mylogin@mymachine:some_path/git_labwork/romeo/shared_repository$ git add musics.txt
mylogin@mymachine:some_path/git_labwork/romeo/shared_repository$ git commit -am "c-012 I love U2"
mylogin@mymachine:some_path/git_labwork/romeo/shared_repository$ git log --all --decorate --oneline --graph
mylogin@mymachine:some_path/git_labwork/romeo/shared_repository$ git push

The push fails, since the remote branch has commit “c-011” that we (Romeo) don’t have. We have first to pull the branch (as suggested), and merge it with our current commit.

mylogin@mymachine:some_path/git_labwork/romeo/shared_repository$ git pull

Indeed, git adds as if commits “c-011” and “c-012” were developed in parallel branches, which is the case due to synchronization issues between Romeo and Juliet. Git opens an editor for you to name that new merged commit, you can simply quit the editor and accept the proposed name.

Now, Romeo can push (but Juliet will have to pull before pushing something else).

mylogin@mymachine:some_path/git_labwork/romeo/shared_repository$ git log --all --decorate --oneline --graph
mylogin@mymachine:some_path/git_labwork/romeo/shared_repository$ git push

Some git scenarios

Fix the release while developing.

Create a new git repository from scratch

mylogin@mymachine:~$ cd some_path/git_labwork
mylogin@mymachine:some_path/git_labwork$ mkdir big_project
mylogin@mymachine:some_path/git_labwork$ cd big_project
mylogin@mymachine:some_path/git_labwork/big_project$ git init

Download application.py and title.py in your repository, add them to the index, and commit with a message “first commit”. Let us tag this commit as the version-1.0 of our project.

mylogin@mymachine:some_path/git_labwork/big_project$ git tag version-1.0

A tag is like a branch that cannot move to another commit. So here, we have tagged in our history that this commit was the stable version of our project.

Of course, read the python files, and try the application

mylogin@mymachine:some_path/git_labwork/big_project$ python3 application.py Schwarzenegger male
mylogin@mymachine:some_path/git_labwork/big_project$ python3 application.py Kardashian female

We would like to add a new feature in the greeting function that colors the name of the person in red or blue according to his/her sex.

As this is a further development, let us create a branch for this.

mylogin@mymachine:some_path/git_labwork/big_project$ git branch colors
mylogin@mymachine:some_path/git_labwork/big_project$ git checkout colors

add this function definition in application.py, and commit with name “color_from_sex implemented”.

import termcolor

def color_from_sex(msg, sex) :
    if sex == 'male' :
        return termcolor.colored(msg, 'blue')
    else :
        return termcolor.colored(msg, 'red')

Modify the greeting function so that it prints the name with the appropriate color, using color_from_sex. Save, test, but do not commit yet.

Let us suppose that you receive a bug report from the users. Indeed, whatever the sex, your greeting always print “Mr.”. This requires a hotfix. You have to create a branch for this, from the master branch. Your working directory needs to be saved. A commit could do the job, but it is inelegant to create commits just for saving your working directory temporarily.

The solution is to stash. It saves your working directory as well as the index on a specific stack, so that you can retrieve it later.

mylogin@mymachine:some_path/git_labwork/big_project$ git stash push

You can display that stack…

mylogin@mymachine:some_path/git_labwork/big_project$ git stash list

… or see the stash operation in your history :

mylogin@mymachine:some_path/git_labwork/big_project$ git log --all --decorate --oneline --graph

Then, let us go back to the release branch, i.e. bring it on our working directory and index. This is what checkout is.

mylogin@mymachine:some_path/git_labwork/big_project$ git checkout master

You can visualize the status of your git session.

mylogin@mymachine:some_path/git_labwork/big_project$ git status
mylogin@mymachine:some_path/git_labwork/big_project$ git log --all --decorate --oneline --graph
mylogin@mymachine:some_path/git_labwork/big_project$ git stash list

let us create a branch to fix de bug and go on that branch.

mylogin@mymachine:some_path/git_labwork/big_project$ git checkout -b hotfix
mylogin@mymachine:some_path/git_labwork/big_project$ git log --all --decorate --oneline --graph

Modify title.py to make the gender function actually sensitive to the sex. Commit this change with message “bug fix”. Test it. It should work… but colorization is not there. As the last commit works fine, we need to merge the hotfix branch and remove it.

mylogin@mymachine:some_path/git_labwork/big_project$ git checkout master
mylogin@mymachine:some_path/git_labwork/big_project$ git log --all --decorate --oneline --graph
mylogin@mymachine:some_path/git_labwork/big_project$ git merge hotfix
mylogin@mymachine:some_path/git_labwork/big_project$ git branch -d hotfix
mylogin@mymachine:some_path/git_labwork/big_project$ git log --all --decorate --oneline --graph

Here, the merging only consists in adding the “bug fix” commit to the master branch. Indeed, while we were working on the bug fix on the hotfix branch, there has been no further commits on the master branch, so the merge is very easy (i.e. so called “fast forward”).

Now, the bug issue is resolved, we can go back on our ongoing work on colors.

mylogin@mymachine:some_path/git_labwork/big_project$ git checkout colors
mylogin@mymachine:some_path/git_labwork/big_project$ git log --all --decorate --oneline --graph

We are now at the level on our last commit on the colors branch, that commit is “color_from_sex implemented”. Remember that there had been a supplementary work from that commit, saved in the stash. Let us retrieve it.

mylogin@mymachine:some_path/git_labwork/big_project$ git stash pop
mylogin@mymachine:some_path/git_labwork/big_project$ git log --all --decorate --oneline --graph

Now, we can commit a new “greetings modified to display colors” commit.

mylogin@mymachine:some_path/git_labwork/big_project$ git commit -am "greetings modified to display colors"
mylogin@mymachine:some_path/git_labwork/big_project$ git log --all --decorate --oneline --graph

Test with

mylogin@mymachine:some_path/git_labwork/big_project$ python3 application.py Schwarzenegger male
mylogin@mymachine:some_path/git_labwork/big_project$ python3 application.py Kardashian female

Of course, the bug is still there, in that branch. As we intend to develop the colors branch further before merging to the master branch, but as we want the bug to be solved in our current branch, we have to merge master in it.

mylogin@mymachine:some_path/git_labwork/big_project$ git merge master
mylogin@mymachine:some_path/git_labwork/big_project$ git log --all --decorate --oneline --graph
mylogin@mymachine:some_path/git_labwork/big_project$ python3 application.py Kardashian female

Indeed, we would like the word “Hello” to be displayed in green. Change this, test, and commit “Hello in green”.

Our work with color is finished now. We can integrate the change in the master branch, and remove the colors branch.

mylogin@mymachine:some_path/git_labwork/big_project$ git checkout master
mylogin@mymachine:some_path/git_labwork/big_project$ git merge colors
mylogin@mymachine:some_path/git_labwork/big_project$ git log --all --decorate --oneline --graph
mylogin@mymachine:some_path/git_labwork/big_project$ git branch -d colors
mylogin@mymachine:some_path/git_labwork/big_project$ git tag version-2.0
mylogin@mymachine:some_path/git_labwork/big_project$ git log --all --decorate --oneline --graph

If you want to play with version 1 of your project again, you can go to the tag.

mylogin@mymachine:some_path/git_labwork/big_project$ git checkout version-1.0
mylogin@mymachine:some_path/git_labwork/big_project$ git log --all --decorate --oneline --graph
mylogin@mymachine:some_path/git_labwork/big_project$ python3 application.py Kardashian female

You see that there are no colors and the bug is still there. You could start another branch from here, as git suggests, so that the HEAD would be associated with a branch (this is not the case here, tags are not branches and so a checkout on a tag renders your HEAD detached).

Let us go back to the last version.

mylogin@mymachine:some_path/git_labwork/big_project$ git checkout version-2.0
mylogin@mymachine:some_path/git_labwork/big_project$ git log --all --decorate --oneline --graph
mylogin@mymachine:some_path/git_labwork/big_project$ python3 application.py Kardashian female

Animation

Try to play with this animated site

Hervé Frezza-Buet, Jérémy Fix,