Codingdomain.com

Getting up speed with Git

Sharing your work

After mastering Git in a local repository, it's time to share the committed changes with others. One interesting fact comes up:

Some statistics

KMess required 130MB for the subversion database, 33MB for a checkout of a single revision.

Git needs just 27MB for the full history, and additionally 14MB for a checkout.

In other words, a complete Git clone with full history available offline, is just slightly larger then a single subversion checkout.

Every user has it's own repository in Git.

Each repository is a full clone, with the entire history, compacted in such way it's actually smaller then the actual checkout of one or two commits.

Hence, every developer can work offline with full access to the entire project history. When needed, you can also share your work with other peers before sending the changes the main repository.

Establishing a connection

New projects

Projects need a location to host the repository. This can be a Gitorious or GitHub project for example. A self-hosted location can created with:

Creating a shared repository at a server:
git init --bare projectname.git

Next, the local Git repository can be linked to it:

Linking the local Git repository to a remote:
git remote add origin remote-url
git push origin master      # push your local master to the origin

And you're set up!

Exising projects

To contribute to an existing project, clone the full repository:

Cloning a repository:
git clone some-url

This will create a local repository, holding the full history of the remote project. The remote location is setup as origin automatically.

Fetching changes

New remote changes can be downloaded with:

Downloading remote changes:
git fetch

The remote commits will actually live side-by-side in your repository, because they have different SHA1 identifiers. The commits also appear as just another branch in your repository, see:

Inspecting fetched commits:
git log --all --graph --decorate   # alias it as 'git log-graph'
git log --oneline origin/master    # display master branch of origin only
git branch -a                      # display local and remote branches

Integrating remote changes

Any local commits can be reaplied on top of the newly fetched changes:

Connecting the remote branch:
git rebase origin/master

You can also do git merge instead.

Pull shortcut

The above steps can be done at once by the git pull command. The pull command uses git merge by default, but it can be changed:

Letting git pull use rebase:
git pull --rebase

This can also be configured as default, in several ways:

Setting git pull --rebase by default:
git config branch.master.rebase true               # master branch only
git config --global  branch.autosetuprebase always  # always use rebase
git config --global  alias.up "pull --rebase"        # or separate alias

Working at remote branches

To continue working from a remote branch, you'll have to create a local branch first. This is called a "tracking branch". The git clone command already does this for the master branch.

For a second branch, use:

Shortcut

Note how this command is actually a shortcut for:

git branch -t branchname \
  origin/branchname

git checkout branchname

Start working on a remote branch:
git checkout -t origin/branch

Any commits at the tracking branch can eventually be pushed to the remote server.

Sending changes

Changes can be submitted with the push command. By default, all matching branches are pushed. To push one branch only, use:

Pushing the master branch:
git push origin master

Aditionally, some notable parameters are:

--dry-runShow what will be pushed
--allPush all branches
--tagsAlso push tags

Out of date

The push command complains it can't do a "non fast-forward update". This is complex speak for "your repository is not in sync with the remote one".

Likely, the local branch is out of date or it's not merged with the remote branch. First run git fetch so the local repository is fully updated. With the log command, both branches will appear as disconnected:

Displaying all branches:
git log --all --graph --decorate --oneline

This can be solved in several ways:

Fixing a non-fast-forward:
git merge origin/master     # merge the remote branch
git rebase origin/master    # base current commits on top of the remote
git reset origin/master     # throw local changes away

This also happens when you've rewritten history of shared commits.

Extra: sending patches

A user without push access, can extract all commits as patches:

Extracting patches:
git format-patch origin/master

The developer can apply these patches directly:

Applying a single patch:
git apply 0001-test.patch
git commit --author="Contributor <[email protected]>

Applying many patches:

Applying many patches:
git am *.patch

And finally, push the changes.

Internals: reference specifiers

The first argument of the push, pull and fetch commands indicates the remote alias or URL, the second is actually a "refspec". It has the notation localname:remotename.

This allows some weird syntax notations, you'll encounter sometimes:

Push command syntax:
git push origin master:master  # local master to remote master
git push origin master        # shortcut for master:master
git push origin :            # all matching branches (the default)

git push origin :branch       # nothing to remote branch -> deletes it

git fetch master:remotes/origin/master  # fetch remote master to local remotes/origin/master

Like commit identifiers, branches have short names, for example:

master= refs/heads/master
origin/master= refs/remotes/origin/master

All these references can be found with internal commands:

Internal commands to list references:
git show-ref
git ls-remote .
find .git/refs/ -type f

Coming up next

Sharing changes get nicer by rewriting history beforehand.

blog comments powered by Disqus