GitLab Tutorial
Based on a tutorial written by Dan Barowy.
The following walks you through:
- Accessing your account.
- Creating a new repository.
- Downloading a repository from GitLab:
clone
. - Saving snapshots and uploading changes:
commit
andpush
. - Updating with changes from GitLab:
pull
andmerge
. - Adding a new project member.
- Undoing private mistakes.
- Undoing public mistakes.
- More commands.
- Troubleshooting.
Accessing your account
- Go to https://evolene.cs.williams.edu.
- Log in with your CS Unix username and password.
Creating a new repository
In GitLab terminology, a source code repository is called a project. I’ll use these terms somewhat interchangably since repository is a more commonly used term in other systems like GitHub.
For assignments involving programming, we will create a repository for you to use. Here’s out to create your own if you would like to ever create a new repository on your own.
First, look for a green “New project” button at the top right of your GitLab browser window.
Enter the name of the project you want to create. All projects on our GitLab server are private and visible only to you. I also recommend selecting the “
Initialize repository with a README
” option.Then click the green “Create project” button at the bottom of the window. This will bring you to a page with some basic information about your project.
Downloading a repository from GitLab: clone
Most of what follows will need to be performed on the command line.
GitLab is a service that “hosts” your repository, meaning that they store your files on their servers for you. However, the tool you will use to interact with your repository is a different tool, called git
.
(For an interesting history about why git
was created, read this.)
We will assume for the purposes of this tutorial that you already have git
installed on your computer. If you are using one of the lab machines, you already have git
. If not, I recommend using Homebrew (macos) to install git
. Installing XCode will also install git
.
Before you can work with a git
repository, you must first download a copy from your git
host, in this case Github. This operation is called cloning.
Every
git
repository has an address. You can find thegit
address of your GitLab repository by looking for a button labeled “Clone
”.For now, we will select the default clone method, “
Clone with HTTPS
”. Just a warning: this method requires that you enter your GitLab username and password frequently. When you eventually tire of this, I suggest using SSH instead. For now, read on.Click the little clipboard button. This copies the address into your clipboard.
Now, open the
Terminal
application. Navigate to the place you want to put your repository, e.g.,/Users/dbarowy/Documents
.
(note that your prompts may look a little different than mine)MyComputer:~ dbarowy$ cd ~/Documents MyComputer:Documents dbarowy$
Now, we use
git
to clone the repository. Typegit clone <repository address>
and press<ENTER>
, e.g.,MyComputer:~ dbarowy$ git clone https://evolene.cs.williams.edu/shazzameter/hula-hoop-o-matic.git Cloning into 'hula-hoop-o-matic'... remote: Counting objects: 4, done. remote: Compressing objects: 100% (3/3), done. remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (4/4), done. MyComputer:Documents dbarowy$
You’ve now made a local copy of your
git
repository.cd
into it to see your files. If youls
your files, you should see that they are the same as those listed on the website.MyComputer:Documents dbarowy$ cd hula-hoop-o-matic/ MyComputer:hula-hoop-o-matic dbarowy$ ls -al total 16 drwxr-xr-x 5 dbarowy staff 160 Jan 31 17:32 . drwx------+ 22 dbarowy staff 704 Jan 31 17:32 .. drwxr-xr-x 12 dbarowy staff 384 Jan 31 17:32 .git -rw-r--r-- 1 dbarowy staff 40 Jan 31 17:32 README.md MyComputer:hula-hoop-o-matic dbarowy$
Now you’re ready to work with your repository.
Saving snapshots and uploading changes: commit
and push
git
is what is referred to as a “distributed version control repository”. This means that every person who works with a given repository (e.g., hula-hoop-o-matic
) gets their very own copy. You obtain your copy by cloning as described above. Everything you do to your clone of the repository is private until you share your changes.
Before we discuss sharing, let’s talk about what a version control repository does. The purpose of version control is to save “snapshots” of your code. Each snapshot, called a commit in git
lingo, is a complete saved copy of your code along with comments about that code and a timestamp. New versions are related to old versions. In fact, the “version history” of your code is a directed graph of these snapshots (commits).

You’ve probably made a complete copy of your code before so that you could undo whatever possibly-ill-advised code experiment you’ve wanted to try. With git
, you don’t need to do any of that. Instead, you can “revert” your code back to any previous commit without having to worry about saving a separate copy. git
does the hard work for you.

All you need to remember to do in order to use this handy facility is to commit your changes periodically. Most developers commit their changes anytime they make an important change. Let’s try committing.
- First, make sure the working directory of your shell is inside your repository (e.g., run
cwd
) and then create a file. Runtouch testfile.txt
at the command line. If you feel like adding text to your file, fire upemacs
and add some text to it. git
needs to be informed which files you plan to commit. It does not track files automatically.MyComputer:hula-hoop-o-matic dbarowy$ git add testfile.txt
Once you’ve added a file,
git
will save snapshot of that file the next time you commit. You can askgit
at any time which files will be updated by using thegit status
command:
Here you can see thatMyComputer:hula-hoop-o-matic dbarowy$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: testfile.txt MyComputer:hula-hoop-o-matic dbarowy$
git
markedtestfile.txt
as a new file.Let’s commit our changes.
Note that theMyComputer:hula-hoop-o-matic dbarowy$ git commit -m "my first commit" [master 5b796b6] my first commit 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 testfile.txt MyComputer:hula-hoop-o-matic dbarowy$
-m "my first commit"
part of the command specifies a commit message. If you do not supply a commit message,git
will open up an editor for you. The default editor for most UNIX machines isvi
, and given that we encourage you to learnemacs
in this department,vi
will likely seem quite foreign to you. I recommend supplying a commit message to circumvent this problem; you may also change your default editor by changing theEDITOR
environment variable. If you do find yourself in avi
session, remember: to quit without saving, pressESC
then type:q!
; to save and then quit, pressESC
then type:wq
.Now if we run
git status
again, we should see that all changes have been committed.MyComputer:hula-hoop-o-matic dbarowy$ git status On branch master Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) nothing to commit, working tree clean MyComputer:hula-hoop-o-matic dbarowy$
We can also view our commit history by typing
git log
.
The history shows two commits,MyComputer:hula-hoop-o-matic dbarowy$ git log commit 5b796b6d901b0b77b22e55afd7c35bd94a598a77 (HEAD -> master) Author: shazzameter <shazz@cs.williams.edu> Date: Thu Feb 1 10:16:46 2018 -0500 my first commit commit 72a90420a759b172e4f8aebaf1d259e58e913cb4 (origin/master, origin/HEAD) Author: shazzameter <shazz@cs.williams.edu> Date: Wed Jan 31 16:52:53 2018 -0500 Initial commit MyComputer:hula-hoop-o-matic dbarowy$
my first commit
, which we just did, andInitial commit
. Wait, didn’t I only just commit once? What gives? Remember that we cloned an existing repository from GitLab. It turns out that when we initially created the repository, the first thing that GitLab did was to add aREADME.md
file and then commit it. When we cloned the repository, we inherited all of the commits up to that point. Therefore, we see two commits.- At this point, we are 1 commit ahead of the repository on GitLab. Remember the
git status
command we ran earlier? Notice that it saidYour branch is ahead of 'origin/master' by 1 commit.
Pictorially,
the local branch, onMyComputer
:
In order to share our changes back to GitLab, we need to push the repository. Pushing will make the remote repository look like the local repository. We push using the
git push
command. Note that, at this point,git
will prompt you for your github username and password.MyComputer:hula-hoop-o-matic dbarowy$ git push Username for 'https://evolene.cs.williams.edu': shazzameter Password for 'https://shazzameter@evolene.cs.williams.edu': Counting objects: 3, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 322 bytes | 322.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) To https://evolene.cs.williams.edu/shazzameter/hula-hoop-o-matic.git 72a9042..5b796b6 master -> master MyComputer:hula-hoop-o-matic dbarowy
- If we now visit our GitLab repository using our web browser, we can see that
testfile.txt
now appears.
Updating with changes from GitLab: pull
and merge
When two or more collaborators use git
, since each collaborator maintains their own separate copy, a fundamental problem arises: merge conflicts. To illustrate, imagine the GitLab repository, User A, and User B all have git
repositories in the following states:
GitLab:

User A:

User B:

No matter whether User A or User B pushes to GitLab first, an important thing will happen: the other person will not be able to push because that user’s local history will not agree with the history on GitLab. For example, suppose that User B pushes first. GitLab’s history will look like:

while User A’s history will look like:

User A’s changes cannot be mechanically incorporated into the changes in the GitLab repository because the two histories diverge: User A’s “snazzy design
” is based on “my first commit
”, but User B’s “new layout
” is also based on “my first commit
”. When git
encounters this situation, you will see the following message when trying to git push
:
MyComputer:hula-hoop-o-matic dbarowy$ git push
To https://evolene.cs.williams.edu/shazzameter/hula-hoop-o-matic.git
! [rejected] master -> master (fetch first)
error: failed to push some refs to 'https://evolene.cs.williams.edu/shazzameter/hula-hoop-o-matic.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
MyComputer:hula-hoop-o-matic dbarowy$
It is tempting to want to panic at this point. But you shouldn’t, because this is an everyday occurrence when using version control. What is the message telling us? Essentially, that “Updates were rejected because the remote contains work that you do not have locally.
” It even suggest what you should do “hint: (e.g., 'git pull ...') before pushing again.
”
The solution is for User A to pull changes from GitLab to their own repository, merge those changes, commit, and then push. Let’s go through each of those steps.
First,
git pull
the changes from GitLab:MyComputer:hula-hoop-o-matic dbarowy$ git pull remote: Counting objects: 3, done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Unpacking objects: 100% (3/3), done. From https://evolene.cs.williams.edu/shazzameter/hula-hoop-o-matic 5b796b6..457aa8b master -> origin/master Auto-merging README.md CONFLICT (content): Merge conflict in README.md Automatic merge failed; fix conflicts and then commit the result. MyComputer:hula-hoop-o-matic dbarowy$
git
seems to be saying scary things here because of the wordCONFLICT
in angry capitals, but it’s just trying to be helpful, reminding you that you will need to manually incorporate changes.We can see what files
git
thinks needs our attention, referred to as conflicts, by runninggit status
.MyComputer:hula-hoop-o-matic dbarowy$ git status On branch master Your branch and 'origin/master' have diverged, and have 1 and 1 different commits each, respectively. (use "git pull" to merge the remote branch into yours) 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: README.md no changes added to commit (use "git add" and/or "git commit -a") MyComputer:hula-hoop-o-matic dbarowy$
git
says that weboth modified
the file calledREADME.md
. Let’s open that file up in our editor and take a look.
Aha. Our local repository, referred to as# hula-hoop-o-matic You know, for kids. <<<<<<< HEAD Snazzy design. ======= And stuff. >>>>>>> 457aa8b909bd15a77e55cda20d8c34a317c839c6
HEAD
here conflicts with the commit (helpfully?) called457aa8b909bd15a77e55cda20d8c34a317c839c6
on GitLab. The difference is that User A wroteSnazzy design
whereas User B wroteAnd stuff.
The conflicting region will be delimited by<<<<<<<
,=======
, and>>>>>>>
symbols. We need to manually merge the two regions and remove these symbols beforegit
allows us to proceed.I’ll change the file to the following:
I have “manually merged” the two sets of changes.# hula-hoop-o-matic You know, for kids. Snazzy design and stuff.
Now, I can
git commit
those changes.MyComputer:hula-hoop-o-matic dbarowy$ git add README.md MyComputer:hula-hoop-o-matic dbarowy$ git commit -m "merge" [master 15b90e0] merge
Finally, I am now allowed to
git push
.MyComputer:hula-hoop-o-matic dbarowy$ git push Counting objects: 6, done. Delta compression using up to 4 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (6/6), 765 bytes | 765.00 KiB/s, done. Total 6 (delta 0), reused 0 (delta 0) To https://evolene.cs.williams.edu/shazzameter/hula-hoop-o-matic.git 457aa8b..15b90e0 master -> master MyComputer:hula-hoop-o-matic dbarowy$
git log
shows the current history,
which is the same asMyComputer:hula-hoop-o-matic dbarowy$ git log commit 15b90e01ab92c80b501ef3a235d20d36156dd17d (HEAD -> master, origin/master, origin/HEAD) Merge: 9c05f64 457aa8b Author: shazzameter <shazz@cs.williams.edu> Date: Thu Feb 1 11:47:04 2018 -0500 merge commit 9c05f6451c78817160e20b3cdd91b454298b4436 Author: shazzameter <shazz@cs.williams.edu> Date: Thu Feb 1 11:30:02 2018 -0500 snazzy design commit 457aa8b909bd15a77e55cda20d8c34a317c839c6 Author: hoopsucker <hoop@cs.williams.edu> Date: Thu Feb 1 11:29:26 2018 -0500 new layout commit 5b796b6d901b0b77b22e55afd7c35bd94a598a77 Author: shazzameter <shazz@cs.williams.edu> Date: Thu Feb 1 10:16:46 2018 -0500 my first commit commit 72a90420a759b172e4f8aebaf1d259e58e913cb4 Author: shazzameter <shazz@cs.williams.edu> Date: Wed Jan 31 16:52:53 2018 -0500 Initial commit MyComputer:hula-hoop-o-matic dbarowy$
Adding a new project member
For some assignments, you may wish to give your partner or group access to your repository. To do this:
- Go to your project’s page on GitLab and select “Members” from the “Settings” menu:

Then enter the name or names of your collaborators, give them
“Developer”“Maintainer” access and then click “Add to project”.
Keep in mind that you should only permit access according to the collaboration rules for the given assignment. Doing otherwise will be considered a violation of the Honor Code.
Undoing private mistakes
Occasionally you want to revert a repository back to a state that it was in earlier. This is the specific scenario I described earlier, where you want to undo a change. Suppose you have a repository with the following history

and you want to undo the commit called “experiment
” so that it looks like

This process is called reverting a repository. You need to be aware that there is a small wrinkle, depending on whether you’ve pushed the “experiment
” commit to GitLab or not. If you have not pushed it, reverting a repository is trivial. If you have pushed it, see Undoing public mistakes instead.
Let’s assume that you have not pushed your “experiment
” commit to GitLab. In other words, only your local repository knows about the “experiment
” commit.
First, determine how many commits you want to undo by running
git log
.MyComputer:hula-hoop-o-matic dbarowy$ git log commit 1d8fbf3cfd54a40745afe4b7f032625e1d8fbf3c Author: shazzameter <shazz@cs.williams.edu> Date: Thu Feb 1 11:30:02 2018 -0500 experiment commit 417249d510519449393c55756200f127417249d5 Author: shazzameter <shazz@cs.williams.edu> Date: Thu Feb 1 11:29:26 2018 -0500 added button commit 4d3d7e62db2123ac94c2a144d084e9a14d3d7e62 Author: shazzameter <shazz@cs.williams.edu> Date: Thu Feb 1 10:16:46 2018 -0500 added form commit c9756fbd2778520c337fe3a7b7ce35aec9756fbd Author: shazzameter <shazz@cs.williams.edu> Date: Wed Jan 31 16:52:53 2018 -0500 new project MyComputer:hula-hoop-o-matic dbarowy$
We run
git reset --soft HEAD~n
wheren
is the number of commits to revert. In this case, we only want to undo 1 commit.MyComputer:hula-hoop-o-matic dbarowy$ git reset --soft HEAD~1
Suppose that what we did in the “
experiment
” commit was to add a file calledfoo.txt
. When you checkgit status
, you will see the previously committed changes are now shown as pending changes in yourgit status
.MyComputer:hula-hoop-o-matic dbarowy$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: foo.txt
which means that foo.txt
is now uncommitted. If you want git
to forget about foo.txt
, run
MyComputer:hula-hoop-o-matic dbarowy$ git rm -f foo.txt
Now git status
won’t know anything about foo.txt
at all.
There are more concise commands for uncommitting changes (e.g., git reset --hard
) but I do not show them here because it is easier to make unrecoverable mistakes when using them.
Undoing public mistakes
Sometimes you want to undo a commit that you’ve already pushed to a remote GitLab repository. Seasoned git
developers often rewrite history in this case. Rewriting history is a bad idea for a couple reasons:
- Collaborators will all need the rewritten history, otherwise their normal workflows will break.
- Rewriting history breaks the contract that you can always undo changes you don’t like. Rewriting breaks undos.
There are some scenarios where rewriting is appropriate. For example, when you commit and push a file containing a difficult-to-change password to GitLab, you should probably rewrite history. GitLab repositories are often public. You probably do not want your passwords accessible to the general public.
(Fun disgression: I once accidentally committed to GitLab using my password as my username. This was the result of late-night coding and a tight deadline. Even normal history rewriting did not work in this case because of extra information tracked by GitLab itself, outside of git
. I ended up speaking with a technical support person at GitLab who incredulously told me that he didn’t think fixing such a stupid mistake was even possible. After he was done berating me, I figured out how to remove it using the GitLab API.)
A better approach is to fetch the changes from the commit you want to revert to and then to commit them as if they were new changes. I caution you not to use the git revert
command. Despite the name, git revert
interacts in subtle ways that are hard to appreciate until you’ve been working with git
for awhile.
Let’s try this approach instead.
Suppose we want to revert the repository to the commit “
snazzy design
”. Rungit log
to find the commit’s ID.MyComputer:hula-hoop-o-matic dbarowy$ git log commit ce9d029b7b7202b93da264f9bf0f39406c6037d4 (HEAD -> master, origin/master, origin/HEAD) Author: shazzameter <shazz@cs.williams.edu> Date: Thu Feb 1 14:18:49 2018 -0500 experiment commit 15b90e01ab92c80b501ef3a235d20d36156dd17d (HEAD -> master, origin/master, origin/HEAD) Merge: 9c05f64 457aa8b Author: shazzameter <shazz@cs.williams.edu> Date: Thu Feb 1 11:47:04 2018 -0500 merge commit 9c05f6451c78817160e20b3cdd91b454298b4436 Author: shazzameter <shazz@cs.williams.edu> Date: Thu Feb 1 11:30:02 2018 -0500 snazzy design commit 457aa8b909bd15a77e55cda20d8c34a317c839c6 Author: hoopsucker <hoop@cs.williams.edu> Date: Thu Feb 1 11:29:26 2018 -0500 new layout commit 5b796b6d901b0b77b22e55afd7c35bd94a598a77 Author: shazzameter <shazz@cs.williams.edu> Date: Thu Feb 1 10:16:46 2018 -0500 my first commit commit 72a90420a759b172e4f8aebaf1d259e58e913cb4 Author: shazzameter <shazz@cs.williams.edu> Date: Wed Jan 31 16:52:53 2018 -0500 Initial commit MyComputer:hula-hoop-o-matic dbarowy$
The commit ID for “
snazzy design
” is9c05f6451c78817160e20b3cdd91b454298b4436
. Now run the following somewhat-magical incantation:MyComputer:hula-hoop-o-matic dbarowy$ git checkout -f 9c05f6451c78817160e20b3cdd91b454298b4436 -- .
git status
shows that this will change theREADME.md
file:MyComputer:hula-hoop-o-matic dbarowy$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: README.md
We now commit and push.
MyComputer:hula-hoop-o-matic dbarowy$ git commit -m "Revert to snazzy design." [master 8e75b4e] Revert to snazzy design. 1 file changed, 1 insertion(+), 1 deletion(-) MyComputer:hula-hoop-o-matic dbarowy$ git push Counting objects: 3, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 336 bytes | 336.00 KiB/s, done. Total 3 (delta 1), reused 0 (delta 0) remote: Resolving deltas: 100% (1/1), completed with 1 local object. To https://evolene.cs.williams.edu/shazzameter/hula-hoop-o-matic.git ce9d029..8e75b4e master -> master MyComputer:hula-hoop-o-matic dbarowy$
Checking GitLab for the change should confirm that the repository was reverted but that all history was retained.
More commands
You can do a lot more with git
than what was discussed here. For example, it is common for developers to work with multiple branches, “squash” commits, and stash changes. There are many good online references, but a good, concise reference is the following.
Troubleshooting
git
is a very powerful tool, but it is famously unfriendly to new users, inspiring lots of jokes. Still, git
is worth learning because you should get into the habit of using version control, and if you’re going to do that, you might as well use the most popular one currently being used. I use git
for all of my collaborative projects, but I prefer Mercurial for personal projects.
The important thing to remember is that you probably aren’t the first person to encounter such a situation. There are lots of great resources online to help you understand why git
behaves the way it does. Atlassian (a GitLab competitor) has an excellent set of git turorials, including this great section on collaborating. As always, StackOverflow is also an excellent resource for specific questions.
If you find yourself in a tough spot, you can also ask the TAs or instructors for help—between the bunch of us, we’ve probably gotten ourselves into and out of most of the sticky situations you might find yourself in.