Yes... but no. Its intentionally not exported from JGit right now to
prevent client applications from making a mistake and getting a branch
stuck.
*IF* you do this, you will need to take on the entire responsibility
for handling the update of the branch. Otherwise it won't be atomic.
:-)
Remember that a branch like "master" is actually the reference
"refs/heads/master". References are stored in $GIT_DIR/refs. So the
current value for "master" is actually in the file
"$GIT_DIR/refs/heads/master". The way a reference gets updated is...
atomically create the file "$GIT_DIR/refs/heads/master.lock". (And
before you ask, the suffix ".lock" is forbidden from being on a
reference for this reason, you cannot name a branch "foo.lock". Git /
JGit won't let you.) If your process successfully created the file,
nobody else has the lock on the reference, so now you can do what you
need.
When you are done, you write the *new* SHA-1 (in ASCII) to the
"$GIT_DIR/refs/heads/master.lock" file you created, then you perform
an atomic rename of this file onto the normal name,
"$GIT_DIR/refs/heads/master". If that rename is successful, you have
updated the reference. If it fails, you delete the
"$GIT_DIR/refs/heads/master.lock" file to rollback your update
attempt.
This is harder than it sounds cross-platform, thanks to thinks like
Windows. JGit has abstracted all of the logic with the LockFile class.
Please use it. You can lock the file, then keep that locked for a
while, and unlock it later. You'll obviously need to keep track of a
whole collection of these LockFile objects, one for each branch you
are holding onto. You can write out the new SHA-1 into the
"master.lock" file using LockFile as soon as it is ready, but defer
the commit() step until you are sure everyone has written out their
updated SHA-1s. This reduces the risk that a disk full error would
cause you to partially update branches. :-)
As for updating a reference, the RefUpdate class in JGit uses LockFile
internally to manage the update. But it also does extra work, like
re-reading the "$GIT_DIR/refs/heads/master" file *after* the lock was
acquired to validate that the current SHA-1 of the branch is what the
update step expects it to be at. If its not, it means another
thread/process had updated the branch and your thread needs to take
that update into account... otherwise work may be lost.
RefUpdate also usually checks that the current SHA-1 of the branch is
merged into the new SHA-1... a fast-forward update. This is an
additional safety check to prevent already merged commits from
suddenly getting unmerged during an update of the branch.
This is all really, really ugly. I don't like the idea of having
Gerrit Code Review poking down this deep into JGit internals... even
though it could. I wonder if a better strategy isn't to have Gerrit
Code Review manage its own "lock table" in memory of references that
are involved in an atomic group and block updates from elsewhere. This
way you can still use the JGit RefUpdate class to actually modify the
references, but MergeOp and ReceiveCommits would refuse updates while
the Gerrit Code Review "lock table" says the reference is busy as part
of a group submit.
That is a good point, if Gerrit uses a lock table in memory it doesn't
protect from C Git concurrently changing the repository, while holding
multiple lock files does. Do we need to protect the site administrator
from breaking a multi-project submit?