eclipse-jgit / jgit

JGit, the Java implementation of git
https://www.eclipse.org/jgit/
Other
121 stars 34 forks source link

`RefLeaseSpec` configured with invalid object ID is silently ignored #74

Open Stephan202 opened 1 month ago

Stephan202 commented 1 month ago

Version

6.10.0

Operating System

Linux/Unix

Bug description

When PushCommand is configured with a RefLeaseSpec that contains an invalid (unparsable) expected object ID, then the RefLeaseSpec is silently ignored, and the push is performed unconditionally.

Actual behavior

The following parameterized JUnit 5 test fails for useInvalidRevision = true.

/**
 * Tests the difference between a {@link RefLeaseSpec} with a valid-but-unknown and an invalid
 * object reference.
 */
@ParameterizedTest
@ValueSource(booleans = {false, true})
void pushBranchDeletionWithLease(
    boolean useInvalidRevision, @TempDir Path upstream, @TempDir Path downstream)
    throws GitAPIException {
  /* Create a repository with branch `master` and a single commit. */
  Git gitUpstream = Git.init().setDirectory(upstream.toFile()).call();
  String firstCommit =
      gitUpstream
          .commit()
          .setAllowEmpty(true)
          .setAuthor("Author", "Email")
          .setMessage("Initial commit")
          .call()
          .toObjectId()
          .name();

  /* Create a second repository that has the first as its `origin` remote. */
  Git gitDownstream =
      Git.cloneRepository().setURI(upstream.toString()).setDirectory(downstream.toFile()).call();

  /* Add a second commit to `master` in the downstream repository. */
  String secondCommit =
      gitDownstream
          .commit()
          .setAllowEmpty(true)
          .setAuthor("Author", "Email")
          .setMessage("Second commit")
          .call()
          .toObjectId()
          .name();

  /*
   * Have the downstream repository attempt to push the second commit to the upstream repository,
   * with lease. The lease specifies either a valid-but-unknown revision or an invalid revision.
   */
  String target = Constants.R_HEADS + Constants.MASTER;
  String expectedRevision =
      useInvalidRevision ? "invalid-revision" : "0123456789abcdef0123456789abcdef01234567";
  Iterable<PushResult> results =
      gitDownstream
          .push()
          .setRefLeaseSpecs(new RefLeaseSpec(target, expectedRevision))
          .setRefSpecs(new RefSpec().setSourceDestination("HEAD", target))
          .call();

  /*
   * In case an invalid revision is specified, then the following assertion fails, as the response
   * is `OK` instead.
   */
  assertEquals(
      RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
      results.iterator().next().getRemoteUpdate(target).getStatus());

  /* In that case the assertions below also fail, as the push did happen. */
  String upstreamHead = gitUpstream.log().call().iterator().next().toObjectId().name();
  assertNotEquals(secondCommit, upstreamHead);
  assertEquals(firstCommit, upstreamHead);
}

Expected behavior

I expect either that the RefLeaseSpec rejects the invalid object ID (preferred), or that the push fails with a REJECTED_REMOTE_CHANGED error. Not sure the Git protocol even allows the latter. C Git seems to do something akin to the former (i.e., fail fast):

# Create a repository with branch `master` and a single commit.
mkdir upstream
git -C upstream init
git -C upstream commit --allow-empty -m 'Initial commit'

# Create a second repository that has the first as its `origin` remote.
git clone upstream downstream

# Add a second commit to `master` in the downstream repository.
git -C downstream commit --allow-empty -m 'Second commit'

# Have the downstream repository attempt to push te second commit to the
# upstream repository, with lease.
#
# When an unknown revision is specified, then the push is rejected:
#
#   To /path/to/upstream
#    ! [rejected]        master -> master (stale info)
#   error: failed to push some refs to '/path/to/upstream'
git -C downstream push --force-with-lease=master:0123456789abcdef0123456789abcdef01234567 origin master

# When an _invalid_ revision is specified, then the command isn't even
# executed:
#
#   error: cannot parse expected object name 'invalid-revision'
git -C downstream push --force-with-lease=master:invalid-revision origin master

Relevant log output

No response

Other information

No response