Flavio Poletti

What if you started developing a tool using git, and after 400+
commits someone tells you to import it into a Gerrit-based
central repository where you barely have a bit for pushing
proposed changes? Well… it’s possible!

Assumptions

Let’s give things names:

git-repo is the original repo you used so far. It might even be your
local copy of the code in your computer, this does not affect the test;

gerrit-origin is the Fort-Knox central repository where you can push
proposed changes

gerrit-local is your humble local copy of gerrit-origin

There are few assumptions that will make you decide whether it’s
worth reading on or not:

you have git-repo with a branch source containing the commits you
want to push into gerrit-origin;

you are provided with means to access gerrit-origin (i.e. clone it
and eventually push to it) and a branch destination to push your
commits;

you want to preserve history, so each commit in the branch of git-repo
will have to end up in a commit in gerrit-origin;

you only have a commit bit in gerrit-origin, i.e. you have the right
to do a git push origin HEAD:refs/for/master (see
Gerrit documentation) and hope that someone will
be so kind as to accept those changes, but nothing fancier.

this will be your first import of some consistent history, from that
point in time on you will use gerrit-local and forget about
git-repo (i.e. you will definitely jump on the Gerrit carriage for
this development).

We’ll see later what you can do if some of the above do not apply… you
lucky!

Assumptions Are Right!

So the assumptions are right… let’s proceed in order. This is what we
are going to do (you’ll notice that Gerrit is somehow very fussy and
you will have to do a lot of work to make it happy):

clone the gerrit-origin into a local copy gerrit-local;

get the commits from branch source in git-repo into branch
intermediate in gerrit-local;

adjust the imported commits in gerrit-local to match the rules
set in gerrit-origin. E.g. you might want to modify the committer’s
name or email to match what is set in gerrit-origin in case you
saved your commits with an email address and you want to push
commits with another email;

add a ‘Commit-Id` to each commit message (or gerrit-origin will
complain);

rebase intermediate to destination, so that gerrit-origin will
see them as acceptable;

push to gerrit-origin and cross fingers.

Cloning the repository

First thing to do is to create gerrit-local cloning gerrit-origin.
We will assume that gerrit-origin is at:

ssh://gerrithost:29418/GerritRepo.git

and that you want to clone it into /path/to/GerritRepo so the clone
will be:

This will provide you a fresh copy of the repository, but it’s still
not sufficient for setting up your Gerrit clone properly. Every time
you have to do a push, in fact, you will have to include a Commit-Id
inside the commit message, and doing this manually is cumbersome. So
most probably you will do something like this:

scp -p -P 29418 john.doe@gerrithost:hooks/commit-msg .git/hooks/

This will install a hook that will be called every time a commit
message is created, including the Commit-Id inside the last paragraph
of the message itself and making Gerrit happy.

Instructions for cloning and getting the hook script should be also
available in the Gerrit GUI - if you have access to it. See
cloning and commit-msg creation
for details and possible variants that might apply to your case.

Last thing, we ensure that the destination branch is available as
a branch in gerrit-local too:

git checkout -b development origin/development

Getting your commits in gerrit-local

We will do most of the work in gerrit-local so we want to acquire the
relevant commits there. We will assume that git-repo is at:

ssh://githost/GitRepo.git

so we move into gerrit-local’s directory and do this:

git remote add git-repo ssh://githost/GitRepo.git
git fetch git-repo

and the checkout its source branch into the local intermediate
for doing transformations:

git checkout -b intermediate git-repo/source

Transforming the commits

Time for some commits mangling now. Your friend is filter-branch, so
you might want to see some additional documentation
if you want to do different changes.

The changes in this section will change the SHA1 identifiers for all
the commits. This should not be a problem because you are probably
doing a transition towards Gerrit and will use gerrit-origin
after importing all commits as described in this article.

One problem that I had was about the commiter’s email address. This might
differ between git-repo (e.g. it might be your personal email address)
and gerrit-origin (e.g. for your work address, or another address that
you are using to contribute to the project in Gerrit). If this is
yours too, the following command (found here) can be
useful:

Then, you will surely need to ensure that each commit has a Commit-Id
inside. The hook you installed will be useful to do this, but it expects
to take its input from a file and not from the standard input so we will
use a pivot file /tmp/mymessage for exchanging data:

Rebasing and pushing

We are now ready to do the rebase. This is needed because there is
currently no link between your new commits and whatever was in
gerrit-origin, so you have to make sure this link is there.

Rebase will be very simple - although the results will vary depending
on the contents of branch destination in gerrit-origin:

git checkout intermediate
git rebase destination

At this point it should be easy:

git push origin HEAD:refs/for/destination

If you get errors, you might want to push only part of the commits and
then repeat - whatever is fine for you!

What if…

You might be in the situation in which some of the assumptions do not
really apply… the following hints might help.

Assumption 1 or 2 do not apply?

Well, this article is probably not for you at all! Did you read it up
to here? Wow, you’re really curious!

Assumption 3 does not apply?

If you’re not interested into preserving all intermediate commits, you
can just squash the whole thing into one single commit and then
push it. At this point you will not need to do any transformation,
because the commit hook will take care of setting the Change-Id and
you will surely have updated your email at this point - right?!?

Assumption 4 does not apply?

If you have wider powers on the Gerrit side, and this is an initial
import, then you probably can just work behind the scenes and set a
copy of git-repo to what’s behind gerrit-origin.

Another alternative is to temporarily disable Change-Ids in the
Gerrit repo to simplify the import.