A hands-on introduction to Git
- Intro
- Setting up a Git repository
- Making Changes
- See the history of the repository
- Making changes on an previous snapshot
- Changing to another branch
- Merging branches
- More to go
Intro
This page is an hands-on introduction to the very basics of Git. Git is a powerful version control system, with many features. In the following examples, we will introduce you to the very basics of Git as a version control system. It is supposed that you have a Git installation ready and running on you PC.
Setting up a Git repository
Identify yourself
First things first: let Git know who you are.
$ git config --global user.name "Your name"
$ git config --global user.email your.email@some.domain.com
In the commands above, the flag --global
makes this configuration valid for other repositories you create on the same machine. This needs to be done only once in each machine (because you said it is global
).
Create the repository
The init
command creates the base structure that will support you repo; in other words, init
creates a new repository. Change (cd
) to the directory where you want to create your repository and issue the following:
[pf@localhost git_practice]$ git init
Initialized empty Git repository in [current path]/.git/
The new folder, .git
, will keep all the data required to manage the repository.
Check the repository
status
will probably be the Git command you will use the most. Start now, to see how your repo is:
pf@localhost git_practice]$ git status
On branch master
No commits yet
nothing to commit (create/copy files and use "git add" to track)
Add your first file
Let’s create a small file, that will be used to show how to work with Git. I can be the very basic “Hello, World!” program:
#include <stdio.h>
int main(void)
{
printf("Hello, world!");
}
and save it as prog.c
.
Check again for status:
[pf@localhost git_practice]$ git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
prog.c
nothing added to commit but untracked files present (use "git add" to track)
Two things are worth noticing:
1) although Git is able to detect files in your folder, it considers them untracked and will ignore them.
2) Git will, most of (if not all) the times, give you a (correct) hint about what to do. In this case: use git add
to make the files tracked.
So, add this new file to the repo:
[pf@localhost git_practice]$ git add prog.c
and let us check the status of the repo again:
[pf@localhost git_practice]$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: prog.c
At this point, prog.c
is staged, meaning it is ready to be stored in the repository, but it is not yet there. Moving the files to the repository is done by the command commit
. This command requires a descriptive message, that can be included in the command line, as follows:
[pf@localhost git_practice]$ git commit -m "First version"
[master (root-commit) 93032fb] First version
1 file changed, 6 insertions(+)
create mode 100644 prog.c
The file is now in the repository. We can check this with the status command:
[pf@localhost git_practice]$ git status
On branch master
nothing to commit, working tree clean
There are no pending changes; everything has been stored in the Git repository.
Making Changes
You now realize that the message in the program should terminate with a newline character. Accordingly, you edit the program source code to correct it. After saving the new version, git status
now yields:
[pf@localhost git_practice]$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: prog.c
no changes added to commit (use "git add" and/or "git commit -a")
The file is modified but not staged for commit (meaning that, if you issue a commit, prog.c
will not be stored in the respository). Notice, again, the suggestions made by Git. You should either add
the file or you can use the -a
switch, which tells Git to to automatically stage files that have been modified and deleted (no need to add them explicitely).
To add this new version of prog.c
:
[pf@localhost git_practice]$ git commit -a -m "Added newline after message."
[master 81c1b51] Added newline after message.
1 file changed, 1 insertion(+), 1 deletion(-)
See the history of the repository
Often, you will need to know what has happened to the repo. The log
command show the commit history of the repo, and it can have several variants:
git log
git log --oneline
git log --graph
git log --all
You can test the result or use the command git help log
to check the meaning of these (and many other) switches.
Check the log of your system with the --oneline
and --graph
switches:
[pf@localhost git_practice]$ git log --graph --oneline
* 81c1b51 (HEAD -> master) Added newline after message.
* 93032fb First version
You can see now the two commits, one per line. Notice that each commit is identified by a 7-digit code, that you can find in the git
command output when you performed each of these commit operations. The most recent commit is identified by HEAD
.
Going back in history
Suppose that you made some more changes to your source file and committed them. At some point, you are not happy with the current version, and you want to go back to some previous point in time. To to that, you will use Git’s checkout
command together with the 7-digit code that identifies the point in the development history where you want to go to. Suppose you want to go back to the very first version of you code (the one with the code 93032fb
). In other words, you want to checkout the commit identified by 93032fb
.
[pf@localhost git_practice]$ git checkout 93032fb
Note: checking out '93032fb'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at 93032fb First version
Git issues quite a few messages, warning us that we are in detached HEAD
. Let’s issue a git log
command to see how we are:
[pf@localhost git_practice]$ git log --oneline
93032fb (HEAD) First version
Now the HEAD
is no longer at the most recent version, but pointing at an older version of the code (it is detached). Note also that, as we “went back on time”, all other commits disappeared from the log command output. If that disturbs you, using the --all
log switch above can be of help:
[pf@localhost git_practice]$ git log --oneline --all --graph
* 19c859a (master) Comments added.
* 81c1b51 Added newline after message.
* 93032fb (HEAD) First version
HEAD
is down on the last line, but all the others have reappeared (no, they were not gone from the repo…)
Making changes on an previous snapshot
Let us now make some changes on the code that was recovered from “First version” and try to commit them. We want the message to be now “Hello, Brave New World!”.
[pf@localhost git_practice]$ git commit -a -m "New message: Brave New World"
[detached HEAD 74db8e7] New message: Brave New World
After this commit, the log
command with all the above switches gives us the following:
[pf@localhost git_practice]$ git log --oneline --all --graph
* 74db8e7 (HEAD) New message: Brave New World
| * 19c859a (master) Comments added.
| * 81c1b51 Added newline after message.
|/
* 93032fb First version
The code has now two branches:
- one made by the snapshots with code 93032fb, 81c1b51 and 19c859a, which was the first we created; and
- a new one, composed by the nodes 93032fb and 74db8e7, that runs in parallel with the first one and which is the one we are currently in (
HEAD
is in this branch).
Giving the new branch a name
The first branch created in a Git repository is, by default, called master
1 (note the name in parentheses in the most recent node in the branch). The second branch that was created, is an independent line of development and it has still no name. The command branch
is used to create a new branch:
pf@localhost git_practice]$ git branch new-branch
To check the result, issue the log
command again:
[pf@localhost git_practice]$ git log --oneline --all --graph
* 74db8e7 (HEAD, new-branch) New message: Brave New World
| * 19c859a (master) Comments added.
| * 81c1b51 Added newline after message.
|/
* 93032fb First version
The HEAD
has now a branch name. Notice that branch
command assigns the given name to the branch of the current snapshot.
Changing to another branch
The checkout
command can be used to move between branches. To go back to the master
branch, one must issue:
[pf@localhost git_practice]$ git checkout master
Previous HEAD position was 74db8e7 New message: Brave New World
Switched to branch 'master'
The log
command allows to verify where we are in the development tree:
[pf@localhost git_practice]$ git log --oneline --all --graph
* 74db8e7 (new-branch) New message: Brave New World
| * 19c859a (HEAD -> master) Comments added.
| * 81c1b51 Added newline after message.
|/
* 93032fb First version
The HEAD
is now associated to the master
branch.
Merging branches
At this point, we have contributions in two different branches. In many situations, we reach a point where we want to join the two development lines (or branches) into a single one. That is the job of the merge
command. In this case, we have two different versions of prog.c
in the master
and new-branch
branches.
The following command causes the new-branch
branch merge into the master
branch:
[pf@localhost git_practice1]$ git merge new-branch
Auto-merging prog.c
CONFLICT (content): Merge conflict in prog.c
Automatic merge failed; fix conflicts and then commit the result.
This signals that Git was not capable of solving the differences and creating a single, merged file on its own and user intervention is required. Git signals the differences among versions in the source file:
/*
* Git demo program
*/
#include <stdio.h>
int main(void)
{
<<<<<<< HEAD
printf("Hello, world!\n");
=======
printf("Hello, Brave New World!");
>>>>>>> new-branch
}
You can see the sections of code that correspond to the HEAD
snapshot and to the new-branch
that are conflicting. The section corresponding to the HEAD
version is between <<<<<<<
and =======
, and the one in new-branch
is between ======
and >>>>>>>>
.
You should edit the file selecting the version you want to keep and removing all the conflict markers. Suppose that you want to keep the “Hello, Brave New World” version, the resulting file would now be:
/*
* Git demo program
*/
#include <stdio.h>
int main(void)
{
printf("Hello, Brave New World!");
}
git status
shows the current state:
[pf@localhost git_practice1]$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: prog.c
no changes added to commit (use "git add" and/or "git commit -a")
You should now commit
this new version, having solved the conflicts signaled by Git:
[pf@localhost git_practice1]$ git commit -a -m "Conflits solved. Merge with new-branch"
[master fc8b580] Conflits solved. Merge with new-branch
The graph in the log shows that the two branches have merged into a single one:
[pf@localhost git_practice1]$ git log --oneline --all --graph
* fc8b580 (HEAD -> master) Conflits solved. Merge with new-branch
|\
| * 74db8e7 (new-branch) New message: Brave New World
* | 19c859a Comments added.
* | 81c1b51 Added newline after message.
|/
* 93032fb First version
More to go
The examples above cover just a fraction of what is possible to do with Git. They merely explore the basics of committing and checking out from a local repository. Most usage of Git involves remote repositories, a topic we did not address.
There are many sources of information that you can use to evolve in using Git. The Git Book would be a good choice. Many other pages are also available; feel free to explore them.
-
In recent editions of Git, and following contemporary cultural tendencies, the default name
master
has been changed tomain
↩
Comments
Your comments are welcome. Feel free to leave here your remarks or your opinion!