libgit2 / libgit2/rugged

Expose libgit2's git_reference_create_matching (atomic compare-and-swap ref update)

Open
#1,013 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C
Stars
2.3k
Forks
293
PR merge metrics
No merged PRs in 30d

Description

Motivation

I'd like to advance a reference atomically from in-process Rugged, the way
git update-ref <ref> <new-oid> <old-oid> does on the command line: update the
ref to a new OID only if it still points at the OID I expect. This is the one
operation that currently forces a service doing everything else in-process
(index build, Rugged::Commit.create, reads) to shell out to git.

libgit2 has the primitive -- git_reference_create_matching, which takes a
current_id and returns GIT_EMODIFIED if the ref has moved -- but Rugged
doesn't currently surface it. ReferenceCollection#create calls plain
git_reference_create, and #update uses git_reference_set_target; neither
lets the caller pass an expected current value.

Proposed API

Add a :current_id option to ReferenceCollection#create, dispatching to
git_reference_create_matching when present:

# advance refs/heads/main to new_oid only if it still points at base_oid
repo.references.create("refs/heads/main", new_oid,
                       force: true, current_id: base_oid)

Outcomes:

  • ref points at current_id -> updated, returns the new Rugged::Reference
  • ref exists but points elsewhere -> no change, returns nil (this mirrors how
    merge_trees returns nil on GIT_EMERGECONFLICT and rebase.commit on
    GIT_EAPPLIED -- a CAS miss is an expected outcome under concurrency, not an
    error)
  • ref does not exist -> raises Rugged::ReferenceError (libgit2's not-found)
  • :current_id with a symbolic target -> ArgumentError (CAS is OID-only)
Open question

Is create the right home for this, or would you prefer it on #update, or a
dedicated method? I put it on create because that mirrors libgit2's own
naming (..._create_matching) and handles both create-if-absent and
conditional-update in one call, but I'm happy to reshape it to whatever fits
Rugged's conventions best.

Status

I'm opening a PR alongside this issue (thin C wrapper + tests covering all the
outcomes above + CHANGELOG/rdoc). Happy to rework the API shape before it's
merged if you'd prefer a different form.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with ReferenceCollection#create and compare it with #update and the existing libgit2 bindings. Add coverage for the current_id outcomes described in the issue, including symbolic targets, then update the CHANGELOG and rdoc; done means the proposed API behavior is tested and documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, ruby
Domain
backend-api-design, tooling
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.