Chapter 9
Exchanges between Repositories
Git is distributed. A repository can have many clones. Each developer has his or her own clone, maybe even several. Usually there is a project server with a central repository. This central repository represents the “official” status of a project and is also called the blessed repository. Often there are more clones, for example, for backups or on the server for continuous integration. Each clone by itself is an independent and full-fledged repository. In every clone, new commits and branches may be created. The exchange of information is therefore extremely important. For this purpose, there are fetch, pull and push commands.
Cloning A Repository
Repository cloning plays a major role in Git. There are many reasons to clone:
The clone command is very easy to use. You specify the location of the original repository as a parameter and Git will create a clone in the current working directory.
Normally, right after creating a clone, Git does a checkout in the workspace. If you do not want this, you can use the --bare option to create a repository without a workspace. This is useful for repositories on the server side, on which a developer works directly.
How to Tell Git Where the Other Repository Is
If the other repository is local, you can simply specify the path to the directory, eg /Users/stachi/git-book.git. For instance, the following command clones a local repository.
> git clone /Users/stachi/git-book.git
However, if you are dealing with multiple repositories from different sources, the URL will look clearer if it is preceded by the protocol (in this case, file):
> git clone file:///Users/stachi/git-book.git
In addition to file, other protocols can also be used to access non-local repositories. The ssh protocol is probably the most frequently used because it enables secure authentication and the infrastructure to do so is often already in place when working with Linux or Unix servers.
> git clone ssh://[email protected]:git-book.git
On top of that, access is also made possible via the http, https, ftp, ftps, and rsync protocols or via a proprietary protocol called git.
Giving the Other Repository A Name
If you often access a repository, you may want to give it a name for easy access. You do this by using the remote add command followed by a nickname.
> git remote add myClone file:///tmp/git-book-clone.git
You can then use the short name, myClone in this example, instead of the repository URL with Git commands.
When cloning a repository, Git automatically stores the path to the original repository as its origin. If you call the remote command with the --verbose option, Git will list the links and show which paths were used for fetching or pushing the commits.
> git remote --verbose
origin ssh://[email protected]:git-book.git (fetch) origin [email protected]:rpreissel/git-workflows.git (push) klon file:///tmp/git-book-clone.git (fetch) klon file:///tmp/git-book-clone.git (push)
You can delete a nickname using the remote rm command.
> git remote rm myClone
Fetching Data
If work continues after cloning, the original repository and the clone will drift apart. New commits as well as branches may be created in either repository.
The fetch command fetches commits from another repository. For each branch in the other repository, fetch transfers all commits that are not yet available locally,
> git fetch myClone
Figure 9.1: Before and after a fetch
Figure 9.1 shows what is happening here:
Note that fetch is unidirectional. In the example, commits were transferred from the clone repository to the local repository. None of the new local commits was transferred to the clone.
You can specify a branch as a parameter to pick changes from that particular branch only. If you do not specify a parameter, fetch will fetch commits from all branches in the repository of origin, which is the repository from which the local repository has been cloned.
Remote-Tracking Branches: Monitoring Other Repositories
Figure 9.2: Remote-tracking branches
There are two types of branches, local and remote-tracking. You have learned about local branches, and now you will learn about remote-tracking branches too.
With a fetch, Git sets bookmarks that point to the locations of the branches in the other repository. These bookmarks are called remote tracking branches. A remote-tracking branch consists of a short name for the other repository and the name of the branch in the other repository. In Figure 9.2, clone/feature-a and clone/master are remote-tracking branches. Use the -r option with the branch command to show remote-tracking branches.
> git branch -r
clone/feature-a clone/master origin/HEAD -> origin/master origin/feature-a origin/master
You can compare your local branches with what other developers have done in the meantime. The diff command shows how the versions differ.
> git diff feature-a clone/feature-a
Use the log command to show which commits were added from the remote repository.
> git log --oneline feature-a..klon/feature-a
The next time you do a fetch, the remote-tracking branches will be updated again.
Note: Git treats remote-tracking branches differently from local branches. You can check out a remote-tracking branch just like a local one, but this puts you in a detached HEAD state (just like checking out an old commit). Instead, you should branch off a local branch from the remote- tracking branch, as described in the next section.
> git checkout -b feature-b clone/feature-b
Working with Local Branches from Other Repositories
You can also create a local branch with a fetch. To do this, use the colon (:). Specify the name of the branch from the other repository before the colon and the name of the local branch after the colon.
> git fetch clone feature-b:my-feature-b
This command fetches branch feature-b from repository clone and branches thereof. A local branch named my-feature-b will be created if none exists or updated if it already exists.
Pull = Fetch + Merge
A fetch often causes conflicts, because new commits have been added locally or to the other repository. In most cases, a merge is appropriate.
Figure 9.3: Double heads after a fetch. The commit that did not get picked up is highlighted
The pull command does exactly that: it imports commits from a remote repository and if necessary does a merge on the current branch (see Figure 9.4).
> git pull
Figure 9.4: After a pull. The commit that did not get picked up and the merge commit are highlighted
For Diamond Haters: --rebase
Those who prefer a linear history can use the pull command with the --rebase option. Then, a rebasing instead of a merge will be performed (see Figure 9.5).
> git pull --rebase
Figure 9.5: After a pull. Highlighted is the shifted commit
Push, the Opposite of Pull
You use the push command to transfer commits from the local repository to a remote repository. For example, the following command transfers new local commits on the feature-a branch to a remote repository pointed by clone and updates the branch pointer to feature-a there.
> git push clone feature-a
There are a few important differences between push and pull that you should consider:
Note that Git will refuse to push if fast-forward is not possible. You can, however, force it with the --force option. However, you should not do that, because this could result in a commit being lost in the other repository. It is always better to resolve conflicts locally, as described in the following step by step instructions.
Step by Step
Push denied. What next?
The reason why a push is denied is because there are already changes added to the same branch in the other repository. The conflicts must first be resolved locally before a push can pass.
1. Find the conflicts
The push command reports this through the following somewhat cumbersome message:
> git push clone feature-a
To /tmp/git-book-clone.git ! [rejected] feature-a -> feature-a (non-fast-forward) error: failed to push some refs to ’/Users/stachi/Book/’ To prevent you from losing history, non-fast-forward updates were rejected. Merge the remote changes (e.g. ’git pull’) before pushing again. See the ’Note about fast-forwards’ section of ’git push --help’ for details.
2. Change branch
> git checkout feature-a
3. Do a pull
> git pull
4. If necessary, clean up merge conflicts
> git mergetool
> git commit --all checkout feature-a
5. Push once again
> git push clone feature-a
If you called push with no parameters, you may have to run the above steps several times, once for each branch with conflicts.
Naming Branches
It is often advisable to agree on uniform branch names when working together in a software team.
Git gives the developer the freedom to name local branches. If you do this, you must use the colon in the parameters for fetch, pull or push. The word before the colon specifies the source branch and the word after the colon specifies the target branch. Consider the following example.
> git pull clone feature-a:favorite-feature
Here, pull imports the feature-a branch in repository clone and locally names the branch favorite-feature.
Deleting a branch in a remote repository is a special case. In this case, you use the push command with a colon and leave the left side of the colon empty, implying setting the branch in question to nothing.
Step by Step
Deleting a branch in a remote repository
You want to delete a branch in a remote repository. Warning: the following steps may cause commits to be lost.
1. Delete a branch in a remote repository
Pay attention to the colon.
> git push clone :feature-a
2. If necessary, delete the local branch
> git branch -d feature-a
Summary
18.225.55.193