2015:03:09:Make&Git

De wiki-prog
Révision de 11 mars 2015 à 11:08 par ASM (discuter | contributions) (Submission)

(diff) ← Version précédente | Voir la version courante (diff) | Version suivante → (diff)
Aller à : navigation, rechercher

Hello, and welcome to this Make and Git session.

Submission

You have to create the directory 2015-03-login_x-Make&Git in your tp-s4 repository. In this folder copy the content of the Skeleton : Media:Skel-make&git.tar.bz2

NOTE: the submission's deadline will be the 15th March, 11h42 for the A2 and 19h42 for the A1.

Make and Makefiles

Make is a tool that automatically builds executable programs and libraries from source code by reading files called makefiles.

Reminder

Here is a quick (or not) reminder of all the knowledge you NEED for this whole part.

What are makefiles for ?

Makefiles are basically like a local configuration file for the make command.

  • How does it work ?

For example we will take the following architecture as your working tree:

  |- AUTHORS
  |- Makefile
  |- src
      |- main.c
      |- pony.c

Here we want to compile the file main.c and only main.c, so everyone should try something like

  > gcc src/main.c

Ok I stop being obvious... Here with a Makefile we are going to automatize all of this. So if we want to translate this in the makefile way we will have something like this in our Makefile:

   compilation:
       gcc src/main.c

and then by running the command make we can see our file being compiled... tadda ! So what really happend ?... When you run the command make your Makefile is read by make. In our makefile we have indicated a rule. Rules are always formatted like this behind:

   target: prerequisites
       recipe

When you call make, it will call the first rule found. But if we want to call a specific rule we can, like here:

   angel:
       echo Don't blink
   dalek:
       echo Run !

Here make dalek will echo "Run !" and make will echo "Don't blink"

You talk about prerequisites in target... EXPLAIN ! EXPLAIN !!

Prerequisites are a rule or a list of rules that would be called before the target's command is executed. For example:

    all: print1 print2

    print1: print3
        @echo is a

    print2:
        @echo lie !

    print3:
        @echo The cake

Here "The cake is a lie !" will be printed when invoking the command make

For now you should be able to start exercice 1

Note: The @ character before a command avoid the command to be printed

Please I need variable, can I have some ?

Actually, in makefiles, there are 2 types of variables:

Simple Variables

In a makefile you can define a variable by this way:

MY_VARIABLE=i am a nice variable
 
all:
    echo $(MY_VARIABLE)

Here $(MY_VARIABLE) is just replaced by the content of your variable... Nothing more, nothing less.

Intern Variables

Variable Description
$@ Target's name
$< name of the first prerequisite
$^ List of all the prerequisites

Note: $? and $* also exist but we will not use them in this session.

Now you are able to do exercise 2.

Matching name

One of the pretty awesome things that makefile has are the pattern specific variables. It's not a simple concept in a fisrt time but you have to understand it. I'M NOW IN CAPS LOCK TO TELL YOU TO CONCENTRATE... Let's take an example:

SRC= main.o
 
all: $(SRC)
    gcc -o exe $^
 
main.o: main.c
    gcc -c $< -o $@

Here I am building a rule nammed main only for compiling main.c. But what if I want to change the name of main.c by test.c... Then I also have to change the rule name and the rule prerequisites. A way to get out of this is pattern specific variable. You create a pattern "%.o" that will match anything terminated by ".o" So for our example we get:

SRC= main.o
 
all: $(SRC)
    gcc -o exe $^
 
%.o: %.c
    gcc -c $< -o $@

Warning: When you defines a pattern you have to use it like in the example:

   pattern: variable definition
       command

And your command DOES NOT CONTAIN ANY %

Now you are able to do execise 3

Implicit (or internal) Rules

Here is the most important part to understand in this seesion about makefiles philosophy.

Implicit rules tell make how to use customary techniques so that you do not have to specify them in detail when you want to use them. For example, C compilation typically takes a `.c' file and makes a `.o' file. So make applies the implicit rule for C compilation when it sees this combination of file name endings.

Here are 2 part from the GNU/make documentation wich greatly concerns us:

  • Compiling C programs:
`n.o' is made automatically from `n.c' with a command of the form `$(CC) -c $(CPPFLAGS) $(CFLAGS)'.
  • Linking a single object file:
n' is made automatically from `n.o' by running the linker (usually called ld) via the C compiler. The precise command used is `$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)'. This rule does the right thing for a simple program with only one source file.
It will also do the right thing if there are multiple object files (presumably coming from various other source files), one of which has a name matching that of the executable file.
Enought documentation, let's take an example (and I advise you to read again the doc after the example which should be much clearer):
    OBJ= toto.o lala.o
 
    main: $(OBJ)
 
    clean:
	rm -rf *.o main

Here the main rule depends on toto.o and lala.o so it apply the Compiling C programs rule for toto.o and lala.o. Thus it creates lala.o and toto.o from respectivly from their '.c' files. Then the main rule will try to create the target with the Linking rule and then compile main.c with all the object files precedently generated

Note: In the doc you can see variables used by implicit rules. This mean if you want to change the C compiler you just have to change the variable CC in the top of your Makefile.

--> the doc page ;)

You can now go to exo4

Functions

Functions allow you to do text processing in the makefile (and sometimes more). It can be very usefull for example if you want to substitute a part of a string you can call the subst function like this:

  $(subst from,to,text)

In real life it becomes:

    $(subst ee,EE,feet on the street) #substitutes the string ‘fEEt on the strEEt’.

For the rest I let you with the beautiful GNU/make manual

Other stuff I don't have enough time to talk about

/!\ Warning for the exercise part

The exercises are all independant but you can reuse the makefiles you already done. Each part has to be in the directory related to the execise.

For each exercise, execpt specific instructions, only the command make (without parameters) will be used

You can get the tarball of the skeleton here ---> Media:Skel-make&git.tar.bz2

Please respect the architecture given for your submit otherwise your code will probably not be read.

Exercice 1: Making brownies (10 min)

In this execise you will have to use a makefile to print a brownies recipe. You have to make 6 rules (very short rules):

  1. brownies: will call each other rules in the correct order (see below)
  2. melt_down_butter: print (with echo of course) Melting butter
  3. melt_down_chocolate: print Melting chocolate
  4. add_stuff: call add_eggs add_flour and add_sugar
  5. add_eggs|add_flour|add_sugar: Respectively print Adding eggs|flour|sugar
  6. bake: print Baking

Final output:

    $ make
    Melting butter
    Melting chocolate
    Adding eggs
    Adding flour
    Add sugar
    Baking

[[ Submit:]]

Before make After make
.
└── Makefile
.
└── Makefile

Tips: @ before a command avoid the printing of this command by make

Exercice 2: Back to C compilation... (15 min)

Here is a simple makefile that you will have to fill:

CC=gcc
EXEC=hello
 
all: $(EXEC)
 
hello: hello.o main.o
        $(CC) -o FIXME FIXME
 
hello.o: hello.c
        $(CC) -o FIXME -c FIXME
 
main.o: main.c hello.h
        $(CC) -o FIXME -c FIXME
 
clean:
        rm -rf *.o $(EXEC)

Rules:

  1. No raw name. Only intern variables!
  2. Each FIXME is an intern variable
  3. make will build the binary only if necessary. If you use make 2 times it has to say "Nothing to be done for 'all." the second time.

Explain!:

  • Rule hello creates the binary with the same name
  • Rule hello.o and main.o creates the objects files with repectivly the same name


Submit:

Before make After make
.
├── hello.c
├── hello.h
├── main.c
└── Makefile
.
├── hello
├── hello.c
├── hello.h
├── hello.o
├── main.c
├── main.o
└── Makefile

Exercise 3: Pattern (10 min with some bugs, 10 sec without)

Take back the makefile in exercise 2 and change the rules in order to avoid any specific rule name (rule hello.o and main.o should disapear). The output/submit should be the same as exercise 2.

Exercice 4: Implicit Rules (15 min)

You have to compile all the files in the exo4 directory.

  • Rule 1: you are allowed to write ONLY 1 rule in your makefile.
  • Rule 2: RESPECT Rule 1 !

Submit:

Before make After make
.
├── dwho.c
├── fzero.c
├── main.c
├── Makefile
└── test.h
.
├── dwho.c
├── dwho.o
├── fzero.c
├── fzero.o
├── main
├── main.c
├── Makefile
└── test.h

Exercise 5: Makefunctions (10 min with Google)

Now I want to have all my .c files in a src directory but with a Makefile at the root of my project directory. Use functions to add a prefix to the sources and compile your binary at the root of your project directory.

Submit:

Before make After make
.
├── Makefile
└── src
    ├── main.c
    ├── marwan.c
    └── test.c
.
├── exe
├── main.o
├── Makefile
├── marwan.o
├── src
│   ├── main.c
│   ├── marwan.c
│   └── test.c
└── test.o

Exercise 6: Help me compile my project please (20 min)

Now I want your makefile to create a build directory. This directory will contain all objects files generated by your makefile. Note: Make sure that make works even if the build directory is already created.

Before make After make
.
├── include
│   ├── bullshit1.h
│   ├── bullshit2.h
│   ├── bullshit3.h
│   ├── bullshit4.h
│   ├── bullshit5.h
│   ├── bullshit6.h
│   ├── bullshit7.h
│   ├── bullshit8.h
│   └── bullshit9.h
├── Makefile
└── src
    ├── bullshit1.c
    ├── bullshit2.c
    ├── bullshit3.c
    ├── bullshit4.c
    ├── bullshit5.c
    ├── bullshit6.c
    ├── bullshit7.c
    ├── bullshit8.c
    ├── bullshit9.c
    └── main.c
.
├── build
│   ├── bullshit1.o
│   ├── bullshit2.o
│   ├── bullshit3.o
│   ├── bullshit4.o
│   ├── bullshit5.o
│   ├── bullshit6.o
│   ├── bullshit7.o
│   ├── bullshit8.o
│   ├── bullshit9.o
│   └── main.o
├── exe
├── include
│   ├── bullshit1.h
│   ├── bullshit2.h
│   ├── bullshit3.h
│   ├── bullshit4.h
│   ├── bullshit5.h
│   ├── bullshit6.h
│   ├── bullshit7.h
│   ├── bullshit8.h
│   └── bullshit9.h
├── Makefile
└── src
    ├── bullshit1.c
    ├── bullshit2.c
    ├── bullshit3.c
    ├── bullshit4.c
    ├── bullshit5.c
    ├── bullshit6.c
    ├── bullshit7.c
    ├── bullshit8.c
    ├── bullshit9.c
    └── main.c

BONUS: Make 4 life (Good luck)

Find out a way to using implicit rules to compile the file main.c given with most tiny makefile you can have. You are allowed to call make as you want.

Explain in the README file (IN THE BONUS-M4life directory !) how make should be called and put your makefile in Makefile.

BONUS: Cmake

Try to create a CMakefile.txt to make compile the files in src/ with it. I let you have some googling :D


Git


Introduction

Git is a distributed version control system created by Linus Torvalds. Yes that's the same guy that created the Linux kernel.

The word "Git" is an old British word meaning "connard". When a journalist from PC World asked Linus Torvalds why he had called it like this, he said : "I'm an egotistical bastard, and I name all my projects after myself. First 'Linux', now 'git'."

Configuration

Git configuration (.gitconfig)

In your home, you should have a file called .gitconfig. If not, you should create it. This file contains your git configuration.

In this file, you can configure informations about your name, which will be the name displayed on your commits.

   [user]
       email = snow_j@epita.fr
       name = Jon Snow

As you can guess, your name is not Jon Snow so you should change this to your name and e-mail.

You can also add configuration lines. Here are some useful lines :

   [color]
       status = auto
       ui = true
       branch = true
       diff = true
       interactive = true
   [push]
       default = simple

You can also use the .gitconfig to create useful aliases. For example, you can create an alias git lg wich gives you a very beautiful git log. You can also create alias like git st to type git status quicker. Here are some examples :

   [alias]
       cm = commit -m
       pr = pull --rebase
       co = checkout
       lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative
       ls = ls-tree -r master --name-only
       st = status

SSH Configuration

If you use GitHub or BitBucket, you probably cloned your repository using a https link. When you do, the server asks you a username and a password. But to use most git repositories, you can use ssh. You have to generate a sshkey with this command :

   ssh-keygen -t rsa -b 4096 -f ~/.ssh/my_git_key_file

ssh-keygen is the program that generates the key. -t rsa specifies that the type of the key is RSA. -b 4096 specifies that this is a 4096 bits key.

This will generate two files in the ~/.ssh directory. my_git_key_file.pub contains a public key that you will give to the server (Github, git.acu.epita.fr...) so that it can identify you. my_git_key_file is your private key. You must not give it to anyone. It is used to prove you are you. When you create the key, ssh-keygen will ask you for a password (passphrase). You don't have to specify it, it is another security so that even a person who have your private key cannot identify. If you leave it empty, it will never ask you for as password when you clone, pull or push, because your private key is enough to indentify you.

However, we DO encourage you to add a passphrase because it's a very useful protection !

In ~/.ssh, you should also create a file called config which contains this :

   Host git.acu.epita.fr
   ForwardAgent yes
   User git
   IdentityFile ~/.ssh/my_git_key_file

This specifies that git can connect the server git.acu.epita.fr using the ssh key ~/.ssh/my_git_key_file.

Using Git

Remotes

One of the main advantages of Git is that it's not centralized. Each client that clones a repository possesses every commit and the entire history of the Git depository. So if you lose the server, you can become the server and your teammates can connect their git to you and continue to work. A server in Git is called a "remote". When you clone a repository that was created, the origin server is a remote called "origin". This is why you push origin master.

If you want to create a repository without a server, you just have to use the command git init inside a directory, and this will create a repository. Then, if you want to link it to a server, you can add the remote "origin" manually with this command :

   git remote add origin "url.of.the.remote.git"

When you create a new git on GitHub or BitBucket using the web interface, it basically creates a directory and executes git init in it.

Commits

Concept

Git is versioning system. This means it is designed to store the history of your code. But it is not automatic. You can't just CTRL-Z and go back modification by modification. When you edit your code, you have to create commits regularly. A commit is basically a version of your code. Every commit information is stored in the .git directory present in your repository.

The commit is not a copy of your entire project. It just contains the differences with the previous commit. So you can create a lot of commits. It is not heavy.

Practice

git add

If you do a git status in a blank repository, it will say no modifications were found. Now create a AUTHORS file :

   echo "* snow_j" > AUTHORS

Please change snow_j to your login. YOUR NAME IS NOT JON SNOW, even if you know nothing too.

Now that you have done a modification in your repository, if you execute git status again, it should show you the AUTHORS file in Red. This means git knows you edited (in this case created) this file, but it is not tracked, which means if you try to commit, it will not take modifications on AUTHORS. In this case it will fail because you cannot create an empty commit.

You have to use this command :

   git add AUTHORS

This will tell git that modifications on AUTHORS are parte of the next commit.

If you type git status again, AUTHORS will be displayed in Green, which means git knows it is part of the next commit. If you edit AUTHORS again, you have to git add it again, because you must tell git which modifications you want to take in the commit.

PROTIP : You can use git add -A to add everything at the same time, but you MUST check the git status before, so that you don't add unwanted files.

  • How can I tell git to always ignore certain files ?

You can create a file called .gitignore in your repository. In this file, you can specify files you want git to ignore like vim temporary files, or binary files. Here is a small .gitignore example :

   *.o
   *.swp
   a.out

git commit

Now that you have selected the modifications you want to store in a commit (You can git add several files), you should check your git status to see if every file you want is added (green) and you didn't add unwanted files.

Then type :

   git commit -m "Description du commit"

The message inside the quotes is very important. It describes what you added or changed in the commit. It will be very useful if you want to go back to a previous commit.

Now if you use the command

   git log

or git lg if you took the configuration above and want a nicer log, you will see your commit in the list. The git log contains the list of all commits on your branch.

Warning : This commit is stored only locally. You still have to send it to the server

Synchronizing with remote (server)

Push

Now that you have created local commits, you want to send them to the server. To do so, you have to use

   git push origin master

or if you want, you can use

   git push -u origin master

the first time, so that git remembers that origin master is the main remote branch, and then

   git push

everytime you want to push.

Pull

If the server contains commits you don't have locally (commits that were push by a teammate or by you on another computer), you can get the modifications with this command :

   git pull --rebase

The --rebase option tells git to use rebase instead of merge when it fetches the new commits from the server. We will see more about rebase and merge later in the "Branches" part. They both have advantages but we advise you to use rebase when you pull. You can create an alias like in the configuration above to make it quicker.

Clone

The command

   git clone url.of.the.repository

creates a directory in your current directory and downloads the entire repository in it. It also registers the url as the "origin" remote, and every commit present on the server is stored. You can cd in this directory. git log will show you every commit.

Conclusion

Regular synchronization with the server is very important. If you lose data, you can get everything you pushed back. It is also very useful to work with a team. If everybody makes a lot of explicit commits and push it to the server, everybody has access to the work of the others and the history is very complete.

Branches

Now that you how commits work, let's talk about one of the killer-features of Git : the branches.

Concept

When you look at the git log, you can see it as a linked list. The most recent commit is the head of the list, and links to the previous one, which looks to the previous... It's really concepted like this. Do you want a proof ? The commit you are looking at is called HEAD in git and you see only the more ancient commits in the log..

But actually, git is way more complicated that that. It's actually a tree. And trees have branches.

  • But why would we need branches ?

That's a good question. The answer is simple. Imagine you are working on an OCR. Your teammate works on image processing, and you are working on the neural network. You know your code may not compile before a week because it's a big amount of work. But your teammate will need to test his code. So if you push code that doesn't compile, it's not good for business.

That's why you need to create a branch. While your teammate works on image processing on the main branch (master), you work on another branch. When you push, you don't push to master, but to your branch.

Your teammate will not see your commits and you won't his. Like this you can commit regularly and push your code without bothering your teammate if it doesn't work. When you both have working code, you can join the branches using merge or rebase (see later).

   ########   ########   ############
   # Cmt1 #<--# Cmt2 #<--# Commit3  #<--master
   ########   ########   ############
                    \
                     \    ###########   ########
                      \___# Cmt3bis #<--# Cmt4 #<--my_branch, HEAD
                          ###########   ########

Checkout

Before we can talk about manipulating branches, I have to explain the git checkout command. git checkout is the command you use to move inside your repository.

When I say move, it means it will unapply or apply the differences contained in the different commits to change your entire directory back to how it was at this commit. It really changes the content of your source files.

As I said earlier, HEAD is the position you are looking at on the git. By default, it points to the same location as master : the last commit of master branch. So if you do

   git checkout HEAD

it will do nothing because HEAD represents YOU.

If you do

   git checkout HEAD~1

it will move you to the commit before HEAD. master~1 would have brought you one commit before the last commit on master.

You can also checkout to a specific commit using its git tag if you gave it one, or using the unique hashkey that represents it (this key is displayed in the git log) :

   git checkout d45ea65

When you want to go back to the last commit or your branch, you just have to checkout to the name of your branch.

   git checkout master

Interesting tip : If you do

   git checkout main.c

it will remove every modification you did to main.c since the last commit.


Now I suppose you have guessed it. I needed to explain this to you because checkout is also the way to move between branches.

Manipulate branches

Now let's pratice branches manipulation. Imagine you are currently working on your project. You commited 10 minutes ago a part of the work that you finished. Since then, you've began working on a very hard part. You realise you need to create a branch but you don't know how to save the modifications you did after the last commit. Don't worry ! You just have to do this :

   git checkout -b newbranch

This will create a branch called "newbranch", and move your HEAD to it. And the best of it, it will checkout to the new branch and bring all your uncommitted modifications with you. The git status will still be the same and the log too, except that if you commit, it will create the commit on "newbranch" instead of master.

You can use

   git checkout master    AND
   git checkout newbranch

to move from one branch to another. If you want to push your modifications, don't forget to

   git push origin newbranch

instead of master.

You can use

   git branch -D newbranch

to delete a branch. Be careful with this. It will destroy every commit inside this branch.

To list all the branches, including the branches on remotes, use

   git branch -a

If you just wan to list the local branches :

   git branch

Now that you know how to work on a branch, let's see how you get your modifications back to master.

Merge and Rebase : Handling conflicts

To fuse two branches, Git gives you two powerful tools : Merge and Rebase. They have very different philosophies.

Merge concept

If you have two branches, the way to fuse them that merge offers you is to create a MERGE commit on master that will contain every modification you did on "newbranch". It fuses every commit of "newbranch". The big problem of this method is that you lose the history of the branch. If you delete the branch, the entire history is contained in one commit. But in very big projects with very complicated branches and really too much commits, it can be good.

Rebase concept

Rebase is based on a totally different concept. Instead of merging all commits in one, it will add every commit of newbranch on by one on its place (the commits are sorted by date). It means that after the rebase, you delete "new branch" because every commit contained in it is also in master. The log of master is exactly the same as it would have been if everybody had kept working on master. It's a really cleaner method and you should prefer it in most cases.

Merge pratice and conflicts

If you want to merge your "newbranch" with master, and continue the work on master, do the following :

   git checkout master
   git merge newbranch

If you see in the log that it's a "fast-forwarding", it means you are lucky and no conflicts were created. The merge is finished. However, if you don't it means git found conflicts. A conflict is a part of your code that was modified differently in both branches. Git can't know which version it prefers so you have to choose. Use the command

   git status

to see where the conflicts were found. Every file in red contains a conflict. Now, file by file, look for conflict and remove them. A conflict looks like this :

   <<<<<<<<<<< HEAD
   int main(void)
   ===========
   int main(int argc, char *argv[])
   >>>>>>>>>>>>

Between HEAD and the equal signs is the line contained on the branch you are on (in this case, master), and after the equal signs, it's the code of the other branch. You have to select the best and remove the other line and the conflict flags. When you have removed ALL conflicts in a file (main.c in this example), you have to execute

   git add main.c

When you did this for all the files containing a conflict (when all files in git status are green), you call this :

   git commit

to create the MERGE commit. Specify MERGE and what you merged in the name.

Rebase practice and conflicts

If you want to rebase newbranch on master (bring all commits from newbranch to master), you have to do this :

   git checkout master
   git rebase newbranch

It will begin the rebase. Like with merge, if you see this is a "fast-forwarding", then git did not encounter any conflict and the rebase is finished. If you don't see "fast-forwarding", then it means you have conflicts. Conflict handling with rebase is close to conflict handling with merge but it has some differences. Like with merge, look at the

   git status

and remove the conflicts in the red files the same way you would do with merge. When you removed all conflicts with a file,

   git add thisfile

But when all conflicts are removed, instead of calling git commit, you will do

   git rebase --continue

because the main difference with merge is that rebase puts commits one by one. So you will encounter exactly the same conflicts, but step by step. After calling git rebase --continue, call

   git status

If it is empty, then the rebase is finished. If not, then you have to do the same until it's empty. You will not encounter more conflicts than with merge. And this will create a very clean log. You should really use rebase instead of merge in most cases.

Important : If you separate the work correctly with your team, you shouldn't work on the same files, and never have conflicts. In most cases, it is a fast-forwarding

Pull : Rebase or merge ?

By default, when you git pull and have conflicts, it stores these conflict in a temporary branch and begins a merge procedure. But if you call

   git pull --rebase   OR
   git pr  (if you took the Aliases in the .gitconfig above)

it will do the same but will rebase. To fuse two branches, merge can be good in some rare cases. But it's a real pain in the ass with pull. Never use the default git pull. It will create useless MERGE CONFLICT commits with ugly temporary branches in your log.

Last pieces of important advice

  • Use the git lg we gave you in the .gitconfig example. It's better than git log in every way.
  • Commit often ! One commit every 5 minutes is not too much at all. Some people commit every 30 seconds. Vim and Emacs have plugins to commit quicker.
  • Push everytime you commit if you have access to internet. A loss of data can come quicker than you think.
  • When you don't have the internet, continue to commit, and push everything at the same time when you get internet back.
  • WHEN YOU USE MERGE, A KITTEN DIES !!!


Exercise

Practice

Now that you know the essential, let's do some practice. You will create a repository in a directory called "first" with

   git init

Then you will create two commits : one adding a valid AUTHORS file, one adding a README. Then you will create a new branch called "jonsnow". On this branch, you will create a file called main.c containing the following code :

   #include <stdio.h>
   int main(void)
   {
       int i = 8;
       if (1)
           i = 0;
       printf("You kn%dw nothing Jon Snow", i);
       return 0;
   }

You will commit this. Then you will go back to master and create a file called main.c (it doesn't exist on master) containing this code :

   #include <stdio.h>
   int main(int argc, char *argv[])
   {
       int i = 8;
       if (1)
           i = 0;
       printf("You kn%dw nothing Jon Snow", i);
       return 0;
   }

You will commit this on master and go back to the branch "jonsnow". Here you will edit the file main.c so that it contains this code :

   #include <stdio.h>
   int main(void)
   {
       int i = 8;
       if (1)
           i = 0;
       printf("You kn%dw NOTHING Jon SNOW", i);
       return 0;
   }

You will commit it to the branch "jonsnow", and go back to master. On master, you will replace main.c with this code :

   int main
   {
       return 1;
   }

You will commit it. Now you will rebase jonsnow on master and resolve all the conflicts.

  • In this case, you will keep the code from master so that at the end of the rebase, your main is 4 lines long.

Then you will go back to the parent directory (cd ..), and create a new repository in a folder called "second". You will do exactly the same but you will merge jonsnow on master instead of rebasing it. You will also resolve all conflicts.

Delivery

When all this is done, you will check that you have the same git alias as our git lg above, and do the following : First you will go back to the parent directory of the two repositories. Then you will do the following :

   cd first
   git lg > ../snow_j-gitpratice.txt
   cd ..
   cd second
   echo "\n=================================" >> ../snow_j-gitpratice.txt
   git lg >> ../snow_j-gitpratice.txt

Remember to change snow_j to YOUR login in these commands !

Then you will add this file to your git repository so that your ASMs can correct it.

Here is the reference you must match : REF

You know nothing Jon Snow !

Now that you read this very long document, you probably think you're a pro of git and make. Sorry to disappoint you but you are still far from this. You now have the basics necessary to use these tools efficiently, but these two pieces of software are so complete, so immense, that you will probably never know everything about them. Actually by writing this TP we learned a lot of thing by ourself.

Jonsnow-ly,

Rafael 'Yayg' Gozlan and Pierre-Alexandre 'Ares' VEYRY