This tutorial will be a guide to reduce the pain of using the git command line for the first time. This requires you to have at least another person to work with. The fictitious setup is to move an existing project into version control and further develop it with your friends / colleagues.
This tutorial was written by me without use of ChatGPT. I do not have a Nobel in literature, but please read every sentences, I promise they are necessary and concise.
After having installed Git we first configure git. Open a terminal: Windows Git Bash
, MacOS Terminal.app
, Linux: use your favourite. The first thing you have to do is tell git your information
$ git config --global user.name "Your Name" # Name shown in commits
$ git config --global user.email your_email@domain.ch # E-Mail shown in commits
Next define a new command git graph
to visualise the commit graph in the command line. We will use this command throughout the tutorial:
$ git config --global alias.graph "log --all --decorate --graph --oneline"
We set that we want that pull is an alias for fetch + merge (it can also be configured to be fetch + rebase, more on this later):
$ git config --global pull.rebase false
Then the following commands to set some sane defaults, mostly taken from this article if you are interested in the details:
$ git config --global init.defaultBranch master # default branch is called master
$ git config --global diff.algorithm histogram # improves output of git diff
$ git config --global fetch.prune true # delete remote branches is they no longer exist
$ git config --global fetch.pruneTags true # same for tags
$ git config --global fetch.all true # always get all remote branches
Finally we set up a simple text editor to write the commit messages. Unless you have customised it differently during the installation the default editor is VIM, which is very powerful but not user friendly at all.
For Windows we can use Notepad.
$ git config --global core.editor notepad
On MacOS this will set the editor to Text Edit
$ git config --global core.editor "open -e -W -n"
We provide the following sample projects for you to work with Git:
- A Python application (Game)
sample_game.zip
- A LaTeX Document
sample_report.zip
Pick one based on your skills and download the zip file.
Tip
Each zip contains a README.md
and a TODO.md
(both are plain text files) with further information and things to do respectively. For example in the README.md
of sample_game.zip
there are instructions on how to run the game.
Note
Only one person in the team must do this part. The others team members can read ahead and / or give instructions while enjoying their drink. Alternatively, all members can do this part, but then for the later parts, you will use only one person’s work to continue the tutorial.
-
Unzip your chosen project somewhere. Hereinafter we will refer to the unzipped folder as your "project directory".
-
Open a terminal in the directory of the project. To do so right click on the directory and then for Windows:
Git Bash Here
, MacOS:Services > New Terminal at Folder
, Linux: you got this. -
Initialize a Git repository:
$ git init
This will initialise a Git repository. If you enable hidden files in Windows Explorer (
View > Hidden Items
) or Finder (CMD + Shift + .
) you will see that it has created a.git
directory.Then you can also see that Git has no commits and is not tracking anything (yet) by reading the output of the command:
$ git status
Next, we need to add the existing files to Git. Before doing so it recommended to avoid adding useless files (e.g. temporary files generated by the OS such as Thumbs.db
or .DS_Store
), so we must tell git to ignore such files:
-
Create an empty text file in the project directory called
.gitignore
(yes, it must start with a dot). -
Go to gitignore.io and in the box type what you are using, for example
Windows
,MacOS
,Linux
,Python
orLaTeX
, hitCreate
and copy the text generated by the website into the.gitignore
file. Alternatively, you can also do this directly from the terminal:$ curl https://www.toptal.com/developers/gitignore/api/linux,macos,windows,python,latex > .gitignore
You can also do it by hand (without the website), the syntax is not difficult to learn.
-
Run in the terminal:
$ git status
You should see under
Untracked files:
a list of files in red that git is detecting are not part of the repository. Check that there are no useless files such asThumbs.db
,.DS_Store
,Report.aux
. You can see git tells youuse "
git add <file>...
" to include in what will be committed -
Add the existing files or directories to the staging area using:
$ git add .gitignore README.md TODO.md pyproject.toml snake # add all files
Adding directories will recursively add all files within them. Alternatively a shorter command if you want to add all files at once is:
$ git add --all
Caution
Do not abuse the --all
flag. In general you should carefully chose what files to add. By carelessly using --all
in large projects you might accidentally add cryptographic secrets, files that are gigabytes in size, etc..
Warning
If you are using sample_game.zip
and have ran the game, do not add the venv
directory because it contains a lot of files that are not directly part of your project. If you have accidentally added it e.g. using git add --all
(though this should not have happened if .gitignore
was configured correctly) reset your staging area by running:
$ git reset # remove everything from the staging area
and then add the files again more carefully.
-
Run again
$ git status
Now you will see the files have turned green, they are in the staging area and git says
Changes to be committed:
above them. -
If the list of file looks good you can create your first commit with
$ git commit
This will open your editor of choice. You will see a summary of the changes, these will not be part of the commit since if you read you will see the message
# Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit.
Above these you can add your commit message. In this case there is not much to say except, for example write:
Move project into version control
.You can save the file and close the editor. In the terminal you should see a summary
[master (root-commit) 132f122] Move into version control 7 files changed, 962 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 TODO.md create mode 100644 pyproject.toml create mode 100644 snake/__main__.py create mode 100644 snake/game.py
You have successfully created your first commit in the master
branch (the default branch name used by Git).
All members go to GitHub and register an account if they don’t have one yet. After that each user must configure their account such that they can authenticate their computer with GitHub. You will only need to perform this task once.
Note
Git uses the SSH protocol which uses public-key cryptography to synchronise the repositories across computers. Git can also work with HTTPS authentication but then for security reasons GitHub requires you to use authentication tokens, which are annoying. We stick to SSH.
If you have never used SSH or don’t have an SSH key pair you need to generate it:
-
Run the following command replacing the email address with yours and follow the instructions on screen. You can press
ENTER
a few times to use the default settings.$ ssh-keygen -t ed25519 -C "your_email@example.com" # replace with your email
This will generate an SSH key pair using elliptic curve cryptography.
-
Copy your public key. If you read the output of the previous command you will see where it was saved
Your public key has been saved in ...
. By default it will be in your home directory. To read your public key you can open the file with your editor or in the terminal run:$ cat ~/.ssh/id_ed25519.pub # or wherever it was saved
-
Copy your public key which should look something like this
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLVAByZGT6xWM2kg7AeEkXbujOETnHdw2FOUx3/mpow your_email@example.com
-
On your GitHub profile in the browser click your profile icon in the top right and go to
Settings > SSH and GPG Keys > New SSH Key
and paste your public key, leave the key type settings to anAuthentication Key
.
You will now be able to authenticate your computer with GitHub.
The person that has done all of the steps so far (in part 1) needs to create a remote repository on their GitHub profile.
-
Go to github.com/new and create a new repository. Set the name to
gitws25
. Leave it to public, do not pick any templates and hitCreate Repository
.After a while you will see the repository has been created on your profile. There will be instructions on the website on how to upload your code. We will follow the instruction under "... or push an existing repository from the command line". Before following the instructions make sure that in the blue shaded area you have selected SSH
-
In your terminal you can add the remote with
$ git remote add origin git@github.com:YOUR_USERNAME/gitws25.git # replace with your remote URL
This will let Git know that there is a server named
origin
(name is local to your machine) at the GitHub URL. You can also call it something else, likegithub
instead oforigin
(note that for the rest of this tutorial we will useorigin
, you can use a different name but you will need to update the next command accordingly). You can then check that Git has saved the remote by reading the output of$ git remote --verbose # show remote url
-
Now we can push our first commit to
origin
with$ git push --set-upstream origin master
The
--set-upstream
flag will setorigin
to be the default remote to push to. After setting this once you will be able to write justgit push
, and it will push toorigin
. -
Now, if you refresh the browser you should see that your file have been uploaded to GitHub.
The other members can finally do something. Hopefully they are not drunk yet.
-
For the person that had been leading so far: Add your other team members as collaborators by going to
Settings > Collaborators > Add People
and adding their GitHub usernames. -
The other members can see on their email inbox or at github.com/notifications an invitation to join the project. Accept the invitation.
-
The other members can clone the project onto their machines. Open terminal where you want your project to go. In the browser from the green
Code
button under SSH you can get the URL to clone the repository with the command:$ git clone git@github.com:YOUR_TEAM_MEMBER/gitws25.git # replace with your remote URL
We will finally start to do something interesting with the Git commit graph. First we do a fast-forward merge, we need two people Alice and Bob. You can choose who plays which character. To make it interesting for the group, we will work with remote branches, even though the theory is the same for local branches.
-
Alice: In the project folder there is a file
TODO.md
with a list of things to do, pick any task you like and perform the change in the project code. After saving the files you can see your changes underChanges not staged for commit: ...
in the output ofalice $ git status
You can quickly add the files you have changed with the staging area using the
--update
flag:alice $ git add --update # add all files that are tracked
Commit your changes with an appropriate commit message, and push your changes to the remote repository (GitHub). The
--verbose
will allow you to see the changes in the editor for the commit message:alice $ git commit --verbose alice $ git graph # see your new commit. or git log if you do not have the alias alice $ git push origin master
Tip
If you have used the --set-upstream
flag earlier, you can just write $ git push
instead of repeating always $ git push origin master
.
-
Bob: Fetch Alice’s changes and take a look at the commit graph before merging. You have no local changes, only the ones from remote. You only need to “catch up”:
bob $ git fetch origin # download new commits from remote bob $ git graph # take a look at the commit graph bob $ git merge origin/master # merge the changes into your local master branch
If you read the output of the last command, it will say that is it a fast-forward merge. Now the two computers are in sync. If you are team of 3, for this part there can be “two Bobs”, i.e. two people can play the role of Bob.
You should be starting to get how this work. We will give less detailed instruction from now on. We will now perform a true merge (aka 3-way-merge).
-
Alice and Bob: Pick any task from
TODO.md
and do it. Pick different tasks. Add your changes to the staging area and commit$ git add ... # add the files you changes $ git commit # with a good commit message
-
Alice: Upload your changes to the remote repository
alice $ git push origin master
Tip
If you struggle visualising what is happening in your head. Take a piece of paper and draw the commit graphs on each machine, as was done in the presentation slides.
-
Bob: Download Alice’s changes and observe that your commit graph has a branching commit (histories diverge)
bob $ git fetch origin # get changes bob $ git graph # histories diverge bob $ git merge origin/master # get the new changes
After running the last command, the editor will pop up, because to perform this merge you need to create a new commit, with a commit message. You can leave the default
Merge branch origin/master into master
, save it and close. You should now have both your and Alice’s changes.
Caution
Bob: Make sure the are no uncommitted (therefore unsaved) changes, otherwise the merge will fail, telling you that it would overwrite your unsaved changes. If you have some changes by mistake, you can discard (delete) them using
$ git restore FILE # DELETES your changes in file!
-
Bob: Push the newly created merge commit to the remote
origin
bob $ git push origin master
-
Alice: Download Bob’s changes and do a fast-forward merge
alice $ git fetch origin alice $ git merge origin/master # will be a ff-merge
Or in one command:
alice $ git pull origin # fetch and merge
Now the two computers should be in sync. If there are other team members, they can also pull from origin to sync their repository.
Note
Now we will repeat the same process, but we will also intentionally cause a merge conflict, so that you can learn how to deal with them. You can switch roles if you want.
-
Alice and Bob: Pick the same task from
TODO.md
and edit the same file at exactly same lines, but make sure they are different on each machine. Add your changes and commit them. Make sure you do not have uncommitted changes using$ git status
. -
Alice: Push your changes to the remote.
-
Bob: Fetch the changes and merge, or do both at once using pull. You will get an error message. Git will tell you that there was a merge conflict, it should also tell you in which file it happened. Now, if you open the editor and take a look at the file with the merge conflict you will see something like this
<<<<<<< HEAD Your local changes (Bob's) ======= Remote changes (Alice's) >>>>>>> 75d041...
Git has added
=======
where the conflict happened, on one side you should have your changes, while on the other Alice’s. If you run in the terminalbob $ git status
You will see that you are in the middle of a merge
You have unmerged paths.
, and Git will tell you which files caused the merge conflict underUnmerged paths:
. -
Bob: To manually resolve the conflict. You need to choose one of the changes (for this tutorial it does not matter which one), then delete the other changes you do not want, the line
=======
, as well as the other two lines with<<<<<<<
and>>>>>>>
, and save the file. Then you can continue the merge that had failed by completing the merge commit:bob $ git add ... # add files that caused conflict, and have been fixed bob $ git commit --verbose bob $ git graph # take a look at the commit graph
Tip
It is good to resolve a merge conflict by hand at leat once as learning experience. For actual work though, there are many graphical tools that show you the two options and do the deleting automatically for you (though usually not adding and committing in Git). See slide on Graphical User Interfaces.
-
Bob: Push your changes to remote
-
Alice: Fetch and merge (pull) Bob’s changes. The two machines should be in sync.
We will now repeat this process again but instead of merging we will use rebase. This is typical if you work in a team that wants to have a linear history or simplify pull requests (more on them later). To make it more interesting we will also start to use local branches.
-
Alice: Pick any task from
TODO.md
and do it. Add your changes to the staging area and commit:alice $ git add ... # add the files you changes alice $ git commit # with a good commit message
Bob: Do the same but on a local branch
feature-x
. To create a new local branch from your local master (which is one commit behind Alice’s):bob $ git branch feature-x master # create a new branch feature-x from master bob $ git switch feature-x # move your HEAD to feature-x
Then pick a different task from Alice (to avoid conflicts for now), make your changes and commit them
alice $ git add ... # add the files you changes alice $ git commit # with a good commit message
-
Alice: Upload your changes to the remote repository
alice $ git push origin master
-
Bob: Fetch alice’s change and rebase your changes on top
bob $ git fetch origin bob $ git rebase master
Note
This subsection is about software development and project management. You can skip to the next part if you are not interested in the topic.
Now that you have gone through the pain of understanding Git at its lowest level using the command line, you may optionally install a graphical interface that hides away all of these details. I suggest Sublime Merge.