Table of Contents

Introduction

In the previous articles on Git we explored the basic concepts of this tool and you should now feel more confident in using it. In today’s post we will complete our understanding facing the last two situations that cover more than 90% of the use cases:

  • Remote Repositories
  • Conflict Resolution

Remote Repositories

The most common scenario includes a person working in a team of people and developing a small feature in a complex application. How do we collaborate effectively with our teammates?

The idea is to have a central repository of code with the current status of the project. This is hosted on a server so we can call it a remote repository as opposed to a local repository which is you local copy of the project. In the first article of this series on Git, we explained how to use the command git init to create a new local repository, however the most common case is to clone a remote repository with git clone $REPO_URL. The local copy is a full copy of all the commits and branches. We typically have the master branch that contains stable code and a development branch with all the new features not yet released but tested.

If you run git br -a you will see some green branches - at least master - and some red branches with the prefix origin/ in their name; origin is the default name of your remote repository, so origin/master is the master branch on the server. In Git you develop your work locally and create commits on local branches, then you push the commits to the server and move the remote branch as well.

For example you can create a free account on GitHub (www.github.com) and create a repository from the web console. Then you can clone the repo and start working:

# replace "myuser" and "myproject" with your data
git clone https://github.com/myuser/myproject.git .
cd myproject
# create a text file called "data.txt"
git add data.txt
git commit -m "initial data"

You are on master since you didn’t switch the branch after cloning the repository and you have one commit in your local repository. The server doesn’t know about your new commit until you explicitly let him know. It’s time to use the command git push to send our local work to the remote repository. Let’s do this and then create a new local commit:

git push origin master
# add new data to "data.txt"
git add data.txt
git commit -m "Added additional data"
git lo

What do you see in the output of git lo?

As you can predict, the green branch master is pointing to the last commit and, since we didn’t perform another push, origin/master (in red) is still pointing to the previous commit.

Now one of our teammates clones our repo (you can go ahead and clone it again in another folder to simulate your colleague). If you ran git lo in the new folder you see master and origin/master are aligned to the commit with the message “initial data”. Now add a new file and run a push to the repo:

# create the file settings.json
git add settings.json
git commit -m "Added configuration"
git push origin/master

If you run git lo you will see both master and origin/master have moved to point to the most recent commit.

Now go back to your previous folder and try git lo and git push origin master. The first command showed the exact same state as before since we didn’t tell the local repository to sync the new data from the server. The push failed because it was trying to send a new commit to the server and move the remote branch to point to it, however the master branch of the server is already pointing to a different commit and if our last push were successful we had overridden the work of our teammate. So, we can peek at what is on the server with:

git fetch
git lo

The command git fetch checks if the remote branches on the server are changed and downloads all the new commits. It also moves the pointers origin/. So git lo will show a green master, which is our local branch pointing to our newest commit, and origin/master pointing to the commit with the message “Added configuration”. If we now try to push master it will fail. To fix the situation we can either run git merge origin/master -m "merge" or git rebase origin/master. Then you can check if now the graph of the commit looks better and we can proceed with git push origin master. In this case the push is successful because the local master is a child of origin/master. Go back to review the second article on Git if you are still not too sure about the difference between merge and rebase.

Instead of running git fetch and then git merge, we can run git pull origin master, however keep in mind this can lead to unexpected consequences if, like before, our local master is not a child of origin/master. The behavior is configurable and I suggest making the pull fail instead of configuring an automatic merge.

To avoid these ambiguous situations we usually don’t develop directly on remote branches, but it’s better to create a local branch and then pull the remote branch before merging our changes to it and then push. In particular let’s say we have two branches in our remote repo master and dev:

$ git clone https://github.com/myuser/myproject.git .
$ cd myproject
$ git br -a
master
origin/master
origin/dev
$ git lo
$ git co dev
# this will create a local branch "dev" which track "origin/dev"
$ git lo

We want to add a new shell script to run our application, test it and then merge the local branch into dev.

# let's start by creating a new local branch called "script"
git co -b script
# create the new shell script "run.sh"
git add run.sh
git commit -m "First version of run.sh"
# test your script and let's say you made a small fix
git add run.sh
git commit -m "Fixed path in run.sh"

We now want to push the new code to the server on the branch dev:

git co dev
git pull
git merge script -m "merge script file"
git push origin dev

Feel free to run git lo before and after every command to investigate changes of origin/dev and local branches during the exercise.

Conflict Resolution

Let’s say I and another teammate make changes to the same file at the same line. The first one who merges and pushes to the remote branch will have no problem. But the second will see a failure when he tries to merge his local branch. This is known as a “conflict”. The most common scenario is when you have a file with constants and both you and your teammate add a new constant at the bottom. When the second person tries to merge the local repository will be stuck in an “ongoing merge” state where whatever can be merged has been (and the file is added to the staging area) while the file in conflict is marked by a double U in the output of git st:

git st
 M run.sh
UU data.txt

If you are scared and want to restore the state of the repository as it was before running the merge, you can abort the process with git merge --abort. If you want to see what went wrong in data.txt and resolve the conflict you can open the file in a text editor:

data1
data2
>>> HEAD
data3a
---
data3b
<<< 39cd015

In this example you have added data3a in your local branch and your teammate added and pushed data3b. Most of the time it is obvious what is the correct thing to do to keep both changes in the final commit (in this case we want both data3a and data3b), but sometimes it requires contacting the other developer and discuss with him what is the best way to proceed. In our example you can simply remove the lines beginning with >>>, --- and <<< and proceed with git add data.txt followed by git merge --continue.

If you want a better tool to merge and compare the differences I highly recommend installing Meld and running git difftool instead of opening the file in the text editor.

Conclusion

In this article we saw how to work in a team with remote repositories and how to solve conflicts in Git when we collaborate with other people.

Cheatsheet

Command Alias Explanation
git branch -a git br -a show all branches: local and remote
git clone $REPO_URL clone a remote repository in a local folder
git fetch download all the new commits from the server and update remote pointers
git push origin $BRANCH push the local commits to the remote server
git merge --continue continue a merge after conflict resolution