Those coming from a Subversion or CSV background will find the Git learning curve is a steep one.
Luckily there is lots of great documentation around. Before continuing developers should take the
time to educate themselves about git. The following are good references:

Git is a distributed versioning system which means there is strictly no notion of a single
central repository, but many distributed ones. For GeoServer these are:

The canonical repository located on GitHub that serves as the official authoritative
copy of the source code for project

Developers’ forked repositories on GitHub. These repositories
generally contain everything in the canonical repository, as well any feature or
topic branches a developer is working on and wishes to back up or share.

Developers’ local repositories on their own systems. This is where development work is actually done.

Even though there are numerous copies of the repository they can all interoperate because
they share a common history. This is the magic of git!

In order to interoperate with other repositories hosted on GitHub,
a local repository must contain remote references to them.
A local repository typically contains the following remote references:

A remote called origin that points to the developers’ forked GitHub repository.

A remote called upstream that points to the canonical GitHub repository.

Optionally, some remotes that point to other developers’ forked repositories on GitHub.

Release branches are used to manage releases of stable branches. For each stable primary branch there is a
corresponding release branch. At present this includes:

rel_2.2.x - The stable release branch

rel_2.1.x - The previous stable release branch

Release branches are only used during a versioned release of the software. At any given time a release branch
corresponds to the exact state of the last release from that branch. During release these branches are tagged.

Feature branches are what developers use for day-to-day development. This can include small-scale bug fixes or
major new features. Feature branches serve as a staging area for work that allows a developer to freely commit to
them without affecting the primary branches. For this reason feature branches generally only live
in a developer’s local repository, and possibly their remote forked repository. Feature branches are never pushed
up into the canonical repository.

When a developer feels a particular feature is complete enough the feature branch is merged into a primary branch,
usually master. If the work is suitable for the current stable branch the changeset can be ported back to the
stable branch as well. This is explained in greater detail in the Development workflow section.

When a repository is shared across different platforms it is necessary to have a
strategy in place for dealing with file line endings. In general git is pretty good about
dealing this without explicit configuration but to be safe developers should set the
core.autocrlf setting to “input”:

%gitconfig--globalcore.autocrlfinput

The value “input” essentially tells git to respect whatever line ending form is present
in the git repository.

Note

It is also a good idea, especially for Windows users, to set the core.safecrlf
option to “true”:

%gitconfig--globalcore.safecrlftrue

This will basically prevent commits that may potentially modify file line endings.

This section contains examples of workflows a developer will typically use on a daily basis.
To follow these examples it is crucial to understand the phases that a changeset goes though in the git
workflow. The lifecycle of a single changeset is:

The change is made in a developer’s local repository.

The change is staged for commit.

The staged change is committed.

The committed changed is pushed up to a remote repository

There are many variations on this general workflow.
For instance, it is common to make many local commits and then push them all up in batch to a remote repository.
Also, for brevity multiple local commits may be squashed into a single final commit.

As mentioned above, git has a two-phase workflow in which changes are first staged and then committed
locally. For example, to change, stage and commit a single file:

%gitcheckoutmaster# do some work on file x%gitaddx%gitcommit-m"commit message"x

Again there are many
variations but generally the staging process involves using gitadd to stage files that have been added
or modified, and gitrm to stage files that have been deleted. gitmv is used to move files and
stage the changes in one step.

At any time you can run gitstatus to check what files have been changed in the working area
and what has been staged for commit. It also shows the current branch, which is useful when
switching frequently between branches.

Once a developer has made some local commits they generally will want to push them up to a remote repository.
For the primary branches these commits should always be pushed up to the canonical repository. If they are for
some reason not suitable to be pushed to the canonical repository then the work should not be done on a primary
branch, but on a feature branch.

For example, to push a local bug fix up to the canonical master branch:

%gitcheckoutmaster# make a change%gitadd/rm/mv...%gitcommit-m"making change x"%gitpullupstreammaster%gitpushupstreammaster

The example shows the practice of first pulling from canonical before pushing to it. Developers should always do
this. In fact, if there are commits in canonical that have not been pulled down, by default git will not allow
you to push the change until you have pulled those commits.

Note

A merge commit may occur when one branch is merged with another.
A merge commit occurs when two branches are merged and the merge is not a “fast-forward” merge.
This happens when the target branch has changed since the commits were created.
Fast-forward merges are worth reading about.

An easy way to avoid merge commits is to do a “rebase” when pulling down changes:

%gitpull--rebaseupstreammaster

The rebase makes local changes appear in git history after the changes that are pulled down.
This allows the following merge to be fast-forward. This is not a required practice since merge commits are fairly harmless,
but they should be avoided where possible since they clutter up the commit history and make the git log harder to read.

As mentioned before, it is always a good idea to work on a feature branch rather than directly on a primary branch.
A classic problem every developer who has used a version control system has run into is when they have
worked on a feature locally and made a ton of changes, but then need to switch context to work on some other feature or
bug fix. The developer tries to make the fix in the midst of the other changes
and ends up committing a file that should not have been changed.
Feature branches are the remedy for this problem.

To create a new feature branch off the master branch:

%gitcheckout-bmy_featuremaster%# make some changes%gitadd/rm,etc...%gitcommit-m"first part of my_feature"

Rinse, wash, repeat. The nice about thing about using a feature branch is that it is easy to switch context
to work on something else. Just gitcheckout whatever other branch you need to work on,
and then return to the feature branch when ready.

Note

When a branch is checked out, all the files in the working area are modified to reflect
the current state of the branch. When using development tools which cache the state of the
project (such as Eclipse) it may be necessary to refresh their state to match the file system.
If the branch is very different it may even be necessary to perform a rebuild so that
build artifacts match the modified source code.

Often a single change (such as a bug fix) has to be committed to multiple branches. Unfortunately primary
branches cannot be merged with the gitmerge command. Instead we use gitcherry-pick.

As an example consider making a change to master:

%gitcheckoutmaster%# make the change%gitadd/rm/etc...%gitcommit-m"fixing bug GEOS-XYZ"%gitpull--rebaseupstreammaster%gitpushupstreammaster

We want to backport the bug fix to the stable branch as well. To do so we have to note the commit
id of the change we just made on master. The gitlog command will provide this. Let’s assume the commit
id is “123”. Backporting to the stable branch then becomes:

Consider the following situation. A developer has been working on a feature branch and has gone back
and forth to and from it making commits here and there. The result is that the feature branch has accumulated
a number of commits on it. But all the commits are related, and what we want is really just one commit.

Rebasing allows us to rewrite the commits on a branch, deleting commits we don’t want, or merging commits that should
really be done. You can read more about interactive rebasing here.

Warning

Much care should be taken with rebasing. You should never rebase commits that are public (that is, commits that have
been copied outside your local repository). Rebasing public commits changes branch history and results in the inability to merge
with other repositories.

The following example shows an interactive rebase on a feature branch:

%gitcheckoutmy_feature%gitlog

The git log shows the current commit on the branch is commit “123”.
We make some changes and commit the result:

%gitcommit"fixing bug x"# results in commit 456

We realize we forgot to stage a change before committing, so we add the file and commit:

%gitcommit-m"oops, forgot to commit that file"# results in commit 678

Then we notice a small mistake, so we fix and commit again:

%gitcommit-m"darn, made a typo"# results in commit #910

At this point we have three commits when what we really want is one. So we rebase,
specifying the revision immediately prior to the first commit:

%gitrebase-i123

This invokes an editor that allows indicating which commits should be combined.
Git then squashes the commits into an equivalent single commit.
After this we can merge the cleaned-up feature branch into master as usual:

%gitcheckoutmaster%gitmergemy_feature

Again, be sure to read up on this feature before attempting to use it. And again, never rebase a public commit.

The gitmerge command takes an option --squash that performs the merge
against the working area but does not commit the result to the target branch.
This squashes all the commits from the feature branch into a single changeset that
is staged and ready to be committed: