Chapter 3

Clone, Push and Pull

Multiple Repositories

In Subversion, the only way we work with multiple repositories is through a mirror, which has to have exactly the same history to work correctly. Git is designed around multiple repositories; to help work on a project, you have to have not just a working copy but a “clone” of the whole repository so you can commit, push, and pull.

Like Subversion, a lot of teams using Git share a common repository located on some server. Rather than sending changes around to each other, which isn’t efficient, they instead send all their changes to a server and pull everyone’s changes from the server at once.

I’m not going to spend time talking about managing repositories on a server. It brings in a lot of issues with permissions, network accessibility, and those kinds of things. All I will say is that if you’re in the position of setting up Git for a team, and you’re responsible for managing the server repositories, do it through a tool like GitHub, Gitlab, or Atlassian Stash. The extra features you get, and the workflow it enables, are worth it, and it reduces the administrative burden of hosting Git. All three of those tools can be run on your own server and your repositories can be as private as you need them to be.

Anyway, to work with multiple repositories, Git doesn’t care where they are as long as it can get to them. Directories on a file system work just fine.

Bare Repositories

One thing I am forced to talk about is a “bare” repository. This is just a repository with no working copy. We’re going to be using “push” to send commits to a “remote” repository, and Git wants to make sure that we don’t mess up the remote repository’s working copy by doing that. So by default, it’s going to reject any push that isn’t targeted at a bare repository. Server repositories are always bare, so you won’t see this issue in real life.

I could make you create a new bare repository, but then we wouldn’t have the stuff from last chapter. Instead, a quick workaround; we’ll make a bare repository based on the non-bare one we created last time. This workaround also teaches an important command, clone.

Assuming we start back in our scratch directory (so if we’re still in repo, we need to do cd ..), do this:

git clone --bare repo shared

From here on out I’ll ignore repo and just talk about shared. Keep it around, though; we may do something neat with it later in the book.

Cloning

Last time we started with a brand new project, but only one person gets to make that first repository. Everyone else needs to clone it. This is important because it gets the whole history of changes that have been made, so when we make new changes and commit them, other people can apply them easily.

We’ll assume that now there are going to be two imaginary friends helping us on this project; call them Harry and Isabelle. They each need their own space to work.

git clone shared harry
git clone shared isabelle

shared is the “bare” repository we made a minute ago, and it has all the work we did in the last chapter. With these commands, we’re telling Git to copy that repository to a new one called harry (and another new one called isabelle). You can see for yourself that each of these new directories has a .git directory and each directory has the latest content in its working copy.

Each of these new repositories is a self-contained copy of the original repository. It points at its source, but it doesn’t need to reach back to get access to any history, any previous versions of files, or any branches. If we do git log in shared, harry, or isabelle, we’ll see exactly the same history, and git log is pulling this from each local .git directory.

In fact, we could now throw away the shared directory and continue committing to one or the other of these new repositories (and even pull content between them directly). But to be more realistic, we’ll pretend that shared is the single shared repository that both Harry and Isabelle can see.

One other note: since I mentioned git log, which as expected shows history, I should also mention git status. For the following chapters, you won’t see git status in the Git command stream because it would have broken up the flow of what I’m showing. That doesn’t mean it’s not important. It tells you all kinds of useful things, including what files you have that need to be committed and where you stand with respect to other repositories that you’re talking to.

Sharing Commits

Harry wants to make some changes:

cd harry
echo "Adding some more content" >> content01
echo "Second content" > content02
git add .
git commit -m "Harry content"

At this point, git log in harry will look different from git log in shared or isabelle. Not only the working copy in Harry’s directory, but his repository also is different from the others. In order for Isabelle to see Harry’s changes, he could send her the commit directly (via an email with a patch or something), but it’s much better for him to just push it to their shared repository.

git push

That was easy, because only Harry has made changes.

Now git log in harry and shared look exactly the same, but git log in Isabelle’s repository is out of date. She needs to get those changes. For once, the command makes sense; it’s the opposite of “push”, so it must be “pull”.

cd ../isabelle
git pull

Isabelle is now up to date. Here’s where we hit another important Git concept. What Isabelle just did is download a commit from the shared repository. That is, the whole changeset came along, with its author information, information about what files were changed, and information identifying the parent of the commit, so Git can apply it accurately. So Isabelle’s working copy is up-to-date, but her git log is also the same now as shared and harry.

(Note for the pedantic: here is where I am not going to talk about the DAG. Nor am I going to talk about how “pull” is “fetch” plus “merge” and what each of those does. Nor will I talk about local and remote references, or the difference between “master” and “origin/master”. Save that stuff for when we need it, which is very rarely if we use Git the way most people use it.)

Wrapping Up

So far this looks a lot like Subversion. Sure, we saved the change to a local repository first, and we called that a commit, but really the “push” was a lot like a Subversion commit, and “git pull” was a lot like “svn up”.

Of course, this is true! At the end of the day, we’re still managing files and the changes that we make to those files over time. But as we go along, we’ll get into situations where the behavior is a little different from Subversion, so it’s important to recognize that something “happened” when we did git commit, not just when we did git push and git pull.

Next time we’ll get into what happens in the far more likely case that Harry and Isabelle need to change things at the same time.

Previous   |    Index    |   Next