)]}'
{
  "log": [
    {
      "commit": "23d386651175ca83d214793749852339684d3c2c",
      "tree": "c831ff95425edbd430af39c4a23c5536725d54ca",
      "parents": [
        "e1ceedc7a754b0aecd3fc473d1b4fa11d7cd1d52"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Mon Sep 28 09:29:04 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Mon Sep 28 10:39:19 2026 -0700"
      },
      "message": "Clarify the message when replication start schedules nothing\n\n\"Nothing to replicate\" suggested the destinations were up to date, but\nit actually means no push task was scheduled because no destination\nmatched the given options or the authGroup cannot read the requested\nprojects. Reword it as a warning that says so.\n\nChange-Id: I3339df6bb622dff193626fa8fa13e23649a67829\n"
    },
    {
      "commit": "e1ceedc7a754b0aecd3fc473d1b4fa11d7cd1d52",
      "tree": "9c674e215a11001d2ee3658ffcd35393990fdf13",
      "parents": [
        "bb7b63785a7c9288bb0167fa79aa6f8d67c64a8e",
        "d820c3780693fccd9192dd1839c31df9c72f33af"
      ],
      "author": {
        "name": "Daniele Sassoli",
        "email": "danielesassoli@gmail.com",
        "time": "Wed Sep 23 17:07:00 2026 +0100"
      },
      "committer": {
        "name": "Daniele Sassoli",
        "email": "danielesassoli@gmail.com",
        "time": "Wed Sep 23 17:32:51 2026 +0100"
      },
      "message": "Merge branch \u0027stable-3.14\u0027\n\n* stable-3.14:\n  Retry failovers by applying url distribution strategy\n  Add projectSharded URL selection per remote\n\nChange-Id: Id760dc515503ea27f6720ddf9edec00b0e159e19\n"
    },
    {
      "commit": "d820c3780693fccd9192dd1839c31df9c72f33af",
      "tree": "2dd3b96e6b53157bddda504b4eb5bb7e7d0ba6d9",
      "parents": [
        "3e623c75033c815b97b5aa9b112dcc75c17dc018",
        "d5426652cb610adb62acb1392eb9e0e4434fce90"
      ],
      "author": {
        "name": "Daniele Sassoli",
        "email": "danielesassoli@gmail.com",
        "time": "Wed Sep 23 17:03:55 2026 +0100"
      },
      "committer": {
        "name": "Daniele Sassoli",
        "email": "danielesassoli@gmail.com",
        "time": "Wed Sep 23 17:03:55 2026 +0100"
      },
      "message": "Merge branch \u0027stable-3.13\u0027 into stable-3.14\n\n* stable-3.13:\n  Retry failovers by applying url distribution strategy\n  Add projectSharded URL selection per remote\n\nChange-Id: I8923c1620b4323411825d99bc5b1c0c35a1a34bb\n"
    },
    {
      "commit": "bb7b63785a7c9288bb0167fa79aa6f8d67c64a8e",
      "tree": "66b836c5642e8977cd65b70bb7ccac6f8e7527fc",
      "parents": [
        "36581c07b5fc46b6ab7811b14af0aa308ad398d3"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Fri Sep 18 17:26:49 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Mon Sep 21 15:02:26 2026 -0700"
      },
      "message": "ProjectRepairer: Sweep stale snapshots before repairing\n\nDelete snapshots older than a day at the start of each repair. A\nsnapshot directory is deleted in a finally block once its transfer\nfinishes, which covers every normal path, including failures and\ninterrupts. It cannot cover an abnormal JVM exit, such as a kill or an\nout of memory kill, between creating the hardlinks and deleting them.\nThe links that survive keep the objects they point at alive, so gc\ncannot reclaim the space until someone removes the directory by hand.\n\nChange-Id: Ia49e874ab39cc1eb86d0ca3fa898f2e7b31fa91d\n"
    },
    {
      "commit": "d5426652cb610adb62acb1392eb9e0e4434fce90",
      "tree": "bbc75d54649f81afa70ec0f69b2b2c0e3ab69b0e",
      "parents": [
        "33fc916b0425bdbfbba6fc96047de5b5a0bb8777",
        "7dbef8af7e73dd66322fe6c63fc10c2347396baa"
      ],
      "author": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Fri Sep 11 13:37:31 2026 -0500"
      },
      "committer": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Fri Sep 11 13:37:31 2026 -0500"
      },
      "message": "Merge branch \u0027stable-3.12\u0027 into stable-3.13\n\n* stable-3.12:\n  Retry failovers by applying url distribution strategy\n  Add projectSharded URL selection per remote\n\nChange-Id: If76798ff2d7deecb940dc8ea616b7230234da7f7\n"
    },
    {
      "commit": "7dbef8af7e73dd66322fe6c63fc10c2347396baa",
      "tree": "5387169b84533bd3570da19fce1cda8f53eac514",
      "parents": [
        "7a8f3429ed555d3576ddc598cf6d6c6ecb453cbd",
        "441ece925c523ecccdcb57a2bf5e874336ea595a"
      ],
      "author": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Fri Sep 11 13:36:25 2026 -0500"
      },
      "committer": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Fri Sep 11 13:36:25 2026 -0500"
      },
      "message": "Merge commit \u0027stable-3.11\u0027 into stable-3.12\n\n* commit \u0027441ece9\u0027:\n  Retry failovers by applying url distribution strategy\n  Add projectSharded URL selection per remote\n\nChange-Id: I6fb9d2c8a348ba9629e6dd25baeb3d043d71946d\n"
    },
    {
      "commit": "441ece925c523ecccdcb57a2bf5e874336ea595a",
      "tree": "e774e191e5c2653c46024192855d5f5e946133a9",
      "parents": [
        "f0d19065d6810d82d239d8b6805d5aeeda603e0a"
      ],
      "author": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Mon Jun 01 23:33:58 2026 -0500"
      },
      "committer": {
        "name": "Adithya C",
        "email": "achakilam@nvidia.com",
        "time": "Fri Sep 11 11:34:39 2026 -0700"
      },
      "message": "Retry failovers by applying url distribution strategy\n\nPreviously, a push with urlDistributionStrategy\u003droundRobin retried the\nsame URL after a transport error - a single unreachable replica could\nblock replication for any push the rotation happened to land on it. Fix\nit to apply the distribution strategy on retries.\n\nThis cherry-pick is modified to also introduce the failover strategy\nfor projectSharded distribution strategy.\n\nChange-Id: Id8ff42afdcd40a315fbab1ecb272b5e87bd0880d\n(cherry picked from commit 28ee207f72e3acb7910ab39d0a033c9da38b699c)\n"
    },
    {
      "commit": "36581c07b5fc46b6ab7811b14af0aa308ad398d3",
      "tree": "33ef3bd174cc052b94dfb2b338a39012d902718d",
      "parents": [
        "21bab3c517e8630d606ff2157479680f38c48c1e"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Tue Aug 18 13:00:22 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Fri Sep 04 11:08:33 2026 -0700"
      },
      "message": "ProjectRepairer: Rsync from a hardlink snapshot\n\nrsync fails when a file it is transferring is unlinked underneath it,\nwhich can happen on repositories that repack often. Link the files to\ncopy into a snapshot dir next to the repository\u0027s objects dir, then\nrsync from there. A hardlink keeps the inode alive after the original\nname is gone, so the transfer always sees a stable set of files. The\nsnapshot is deleted once the transfer for that action finishes.\n\nChange-Id: I47ddf03c63180542dbfe47582941284d3f691f9e\n"
    },
    {
      "commit": "f0d19065d6810d82d239d8b6805d5aeeda603e0a",
      "tree": "1bf24cf8a09a5efb8f1b78fe1dddfa72dcc26cfe",
      "parents": [
        "e2c75745275cff5560921917769176431d9a2433",
        "dcf17052dbd56cd5a9d8a1e0b3b5e759f855f5bf"
      ],
      "author": {
        "name": "Martin Fick US",
        "email": "mfick@nvidia.com",
        "time": "Sat Aug 29 08:20:13 2026 -0700"
      },
      "committer": {
        "name": "Gerrit Code Review",
        "email": "noreply-gerritcodereview@google.com",
        "time": "Sat Aug 29 08:20:13 2026 -0700"
      },
      "message": "Merge \"Add projectSharded URL selection per remote\" into stable-3.11"
    },
    {
      "commit": "dcf17052dbd56cd5a9d8a1e0b3b5e759f855f5bf",
      "tree": "6ef3434b511294df9fa949ec5fdca491a34018fd",
      "parents": [
        "d8ae6cc531c634c5e594470fef5fb4902cb0f048"
      ],
      "author": {
        "name": "Bhoomika Srinivas",
        "email": "bhoomikas@nvidia.com",
        "time": "Fri Aug 28 15:15:07 2026 +0530"
      },
      "committer": {
        "name": "Bhoomika Srinivas",
        "email": "bhoomikas@nvidia.com",
        "time": "Sat Aug 29 13:07:57 2026 +0530"
      },
      "message": "Add projectSharded URL selection per remote\n\nExtend `remote.NAME.urlDistributionStrategy` with a `projectSharded`\nvalue. Like `roundRobin` it picks a single URL per push, but derives it\nfrom the project name, so a project always replicates to the same URL\ninstead of rotating on every event.\n\nReplication tasks are coalesced per (project, URI), so rotating URLs\nleaves successive updates to one project as separate tasks racing\nagainst the shared NFS backend. Pinning collapses them into one task.\nThe URL is chosen by hashing the project name over the sorted candidate\nlist, so every host agrees regardless of config order.\n\nChange-Id: I91118a47e0e7da64995a9a444d4baf56ec8b8386\n"
    },
    {
      "commit": "21bab3c517e8630d606ff2157479680f38c48c1e",
      "tree": "68ed937f48ed0ff9abd5558ce5689f3c9151dc8c",
      "parents": [
        "72ac579351c886002890ee6f9ea3468277ba98ab"
      ],
      "author": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Wed Aug 26 15:57:01 2026 +0200"
      },
      "committer": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Thu Aug 27 19:08:49 2026 +0200"
      },
      "message": "Allow replicationRetry to be configured with a time-unit suffix\n\nremote.NAME.replicationRetry was parsed as a bare integer and scheduled\nin TimeUnit.MINUTES, so the smallest non-zero offline-retry backoff an\nadmin could configure was a full minute. That minutes-only quantum also\nprevents ReplicationIT from driving a sub-minute retry, which keeps its\ntwo new-project cases slow.\n\nParse replicationRetry with ConfigUtil.getTimeUnit and schedule the\nretry in seconds. A value without a unit keeps its historical meaning of\nminutes, so existing configurations are unchanged, while a value with a\ntime-unit suffix -- e.g. \"30 s\", \"90 s\" or \"2 m\" -- is honoured as\nwritten. getRetryDelay() now returns seconds, matching the\nRemoteConfiguration javadoc that already documented it as such, and both\nreschedule sites in Destination (the direct retry and the failover\nreplacement) schedule in TimeUnit.SECONDS instead of TimeUnit.MINUTES.\nThis follows the httpd.maxwait precedent in gerrit-core, which likewise\ndefaults a bare number to minutes and accepts an optional unit suffix.\n\nUse the new option to speed up the two new-project cases\n(shouldReplicateNewProjectWithoutRefLog and\nshouldCreateNewProjectWithRefLog): their first ref-push finds the\nreplica repository missing, PushOne#createRepository creates it, and\nthe push is rescheduled as REPOSITORY_MISSING, which waited the\none-minute replicationRetry. They now set replicationRetry to\nTEST_REPLICATION_RETRY_SECONDS (1s) so the missing-repository\nretry fires in a second, with TEST_NEW_PROJECT_TIMEOUT tightened\nto match. The shared TEST_REPLICATION_RETRY_MINUTES is left\nuntouched so tests that rely on the real one-minute retry (e.g.\nReplicationStorageIT) keep their timing. The two cases drop from\n~60s to ~4s; ReplicationIT drops from ~173s to ~61s, all 26 green.\n\nRelease-Notes: remote.NAME.replicationRetry now accepts an optional time-unit suffix (e.g. \"30 s\"); a bare number still means minutes.\nChange-Id: I54f8e3d48d20568b2858abde1e1b06f35bf27841\n"
    },
    {
      "commit": "72ac579351c886002890ee6f9ea3468277ba98ab",
      "tree": "be0ba6cf413c9c9791c1e3287da01d184da9eb11",
      "parents": [
        "050558e06029249ed1ac3493c026d0b9c8cdbcae"
      ],
      "author": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Thu Aug 27 15:22:12 2026 +0200"
      },
      "committer": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Thu Aug 27 15:31:24 2026 +0200"
      },
      "message": "ReplicationIT: don\u0027t wait a full retry window to prove non-replication\n\nReplicationIT takes ~343s (5m40s) while the rest of the build finishes\nin seconds. Five of its 26 cases each burn ~62s; the rest run in \u003c\u003d2s.\n\nAll five bottom out on the replication retry quantum: a failed push is\nrescheduled in TimeUnit.MINUTES (Destination#reschedule), so the\nsmallest non-zero retry is one minute and the tests set\nreplicationRetry\u003d1. The three negative tests --\nshouldNotDrainTheQueueWhenReloading,\nshouldNotReplicateToNonMatchingRemote and\nshouldNotReplicateProjectListedInProjectsAndExcludeProjects -- assert a\nref is never replicated by waiting for it and expecting the wait to\nexpire. They reuse TEST_TIMEOUT, which is sized to span a retry cycle\n((delay + retry*60)+1 \u003d 62s), so each burns the full minute only to\nprove a negative.\n\nGive those waits their own TEST_NOT_REPLICATED_TIMEOUT (replication\ndelay + push time + a few seconds cushion, ~7s), applied via a private\nwaitUntil(Supplier, Duration) overload in ReplicationIT.\n\nThis does not weaken the tests: waitUntil still throws on timeout, so\nassertThrows still verifies non-replication -- only the wait shortens.\nReplication is structurally suppressed in these cases (destination down,\nproject excluded, remote non-matching), so the ref can never appear;\nwere a regression to leak a push it would replicate within the ~1s\ndelay, well inside the window, and the assertion would then fail. The\nshared TEST_REPLICATION_RETRY_MINUTES is left untouched so tests that\nrely on the real one-minute retry (e.g. ReplicationStorageIT) keep their\ntiming.\n\nThe two remaining ~60s cases (new-project replication) genuinely wait a\nretry: the first push fails as REPOSITORY_MISSING while the replica repo\nis still being created, and that reason is retried on the minutes-based\ndelay. That is a separate, arguably production-side latency issue and is\nhandled in a follow-up.\n\nNet, the three negatives drop from ~62s to ~7s each while the two\nnew-project cases are untouched here, so ReplicationIT drops from ~343s\nto ~173s (~2x60s new-project waits + ~24 fast tests), all 26 green.\n\nChange-Id: I2d561950e51761a1d1b0fe09dc50763b3e5421af\n"
    },
    {
      "commit": "050558e06029249ed1ac3493c026d0b9c8cdbcae",
      "tree": "b0f77bf46898538a83e8039f8e9e26cce6450625",
      "parents": [
        "186d15ad334e6da0cf40cdf59f7c3ebe4ef59a14"
      ],
      "author": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Thu Aug 27 15:22:12 2026 +0200"
      },
      "committer": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Thu Aug 27 15:31:24 2026 +0200"
      },
      "message": "ReplicationIT: Reformat with google-java-format 1.35.0\n\nSeveral scheduleFullSync() invocations exceed the 100-column limit and\nwere left unwrapped, so google-java-format 1.35.0 (the tree\u0027s default,\ntools/gjf.sh) flags them.\n\nChange-Id: I63da43e4db4a29095736eb228a63851d51087cc1\n"
    },
    {
      "commit": "186d15ad334e6da0cf40cdf59f7c3ebe4ef59a14",
      "tree": "5565d096a120feff9cead4f738cf0de67bf16e18",
      "parents": [
        "c66ceb500a2f6ee14baa41acba0da32da3722c22"
      ],
      "author": {
        "name": "Milutin Kristofic",
        "email": "milutin@google.com",
        "time": "Tue Aug 18 10:09:39 2026 +0000"
      },
      "committer": {
        "name": "Milutin Kristofic",
        "email": "milutin@google.com",
        "time": "Tue Aug 18 10:09:39 2026 +0000"
      },
      "message": "Catch RuntimeException instead of Exception in PushAll and ReplicationQueue\n\nPrepare for enabling Error Prone\u0027s CatchingUnchecked rule.\n\nRelease-Notes: skip\nChange-Id: I625d2253aba2af823cff6be03a221104b9126143\n"
    },
    {
      "commit": "c66ceb500a2f6ee14baa41acba0da32da3722c22",
      "tree": "b02dd2c9b3710f028e9fec643c283fbc2aff4a4d",
      "parents": [
        "5c3414d99c36c293e2119191ae9bd0aca5b46380"
      ],
      "author": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Sun Aug 16 18:41:48 2026 +0200"
      },
      "committer": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Sun Aug 16 19:47:31 2026 +0200"
      },
      "message": "Name the unused Mockito answer parameter for Java 21 compatibility\n\nReplicationQueueTest used an unnamed variable (`_`) as the thenAnswer\nlambda parameter. Unnamed variables are a standard feature only in\nJava 22+; under a Java 21 language level they are a disabled preview\nfeature and fail to compile. Give the parameter a name so the test\nbuilds on both Java 21 and Java 25.\n\nChange-Id: Ib121547711a022a1ec1304289b905cb6c5f54428\n"
    },
    {
      "commit": "5c3414d99c36c293e2119191ae9bd0aca5b46380",
      "tree": "629e63799bed1d331524d75108ac200e9d4c24c5",
      "parents": [
        "54ed7e2c412308c73f9ae536c176731bc0d54e40"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Thu Aug 06 17:30:03 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Fri Aug 14 10:49:30 2026 -0700"
      },
      "message": "ProjectRepairer: Add a --copy-loose-objects repair action\n\nAdd a COPY_LOOSE_OBJECTS action, exposed as \u0027--copy-loose-objects\u0027,\nrsyncing the two hex character fanout directories under objects/. The\ninclude patterns keep rsync out of pack/, info/ and their siblings.\n\nThe action is declared ahead of COPY_PACKS, so when both are selected\nloose objects are copied first. \u0027--full\u0027 now covers both.\n\nChange-Id: I9c3d12182bdc5fa9da662269d8681db174057f21\n"
    },
    {
      "commit": "54ed7e2c412308c73f9ae536c176731bc0d54e40",
      "tree": "715943c03f17f9215f113c6bac88cc86f252421b",
      "parents": [
        "09be89ae6ffd64fb52b592040815c253268280da"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Thu Aug 06 17:42:32 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Fri Aug 14 10:49:30 2026 -0700"
      },
      "message": "ProjectRepairer: Refactor to select actions through an enum\n\nrepair() takes a \u0027copyPacks\u0027 boolean, which can only describe the one\nthing it knows how to do. A second kind of repair would mean a second\nboolean at every call site, with the order the two run in implicit in\nhow repair() tests them.\n\nTake a collection of Action values instead. Individual flags are kept in\nflag order. \u0027--full\u0027 and the no-flag default use Action.all() so actions\nrun in enum declaration order. Action has only COPY_PACKS, so behaviour\nis unchanged.\n\nChange-Id: Ie7c0d9af40cab893a01a754bc984b8593ff6a5bc\n"
    },
    {
      "commit": "09be89ae6ffd64fb52b592040815c253268280da",
      "tree": "793bca3c6d937714d9358baa1c2b740c900517b9",
      "parents": [
        "fa61cad9591888b6ccfd7bbb1901932bbe1e05a3"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Thu Aug 06 17:41:25 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Fri Aug 14 10:49:30 2026 -0700"
      },
      "message": "ProjectRepairer: Refactor to extract the repository lookup\n\ncopyPackTo() validates the destination URI and opens the repository to\nlocate it on disk before copying anything, neither of which has to do\nwith packs.\n\nMove both into objectsDir(), which reports the repository\u0027s objects\ndirectory, or nothing if the project cannot be repaired at that\ndestination. What is left is copyPacksTo(), taking the directory to\ncopy from. The now trivial copyInOrder() folds into it.\n\nChange-Id: I2d1ba0a23671d6db115f18a35420f4c04a24c0ec\n"
    },
    {
      "commit": "fa61cad9591888b6ccfd7bbb1901932bbe1e05a3",
      "tree": "777187f6e53a3ce8a40d005f4bb73e15759a61ee",
      "parents": [
        "3c2e8cc8c76a6024341f14f3a5afc6ba2de7d392"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Thu Aug 06 17:40:33 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Fri Aug 14 10:49:30 2026 -0700"
      },
      "message": "ProjectRepairer: Refactor copy() to take a destination dir\n\nbuildCopyDestination() appends objects/pack/ to the remote path\nitself, so copy() can only ever populate a destination\u0027s pack\ndirectory.\n\nPass the remote subdirectory in instead, with the pack callers naming\nPACK_DIR. The rsync commands produced are unchanged.\n\nChange-Id: I7bf05966dd95b7827864f2ae5a0a39fe9ca050eb\n"
    },
    {
      "commit": "3c2e8cc8c76a6024341f14f3a5afc6ba2de7d392",
      "tree": "faed9c6929803eb3ee914f6bd4b403240d8f639d",
      "parents": [
        "e928e4fcc0f929cbe97ad497124094698183756d"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Thu Aug 06 17:39:33 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Fri Aug 14 10:49:30 2026 -0700"
      },
      "message": "ProjectRepairer: Report an interrupted copy as InterruptedIOException\n\ncopy() declares InterruptedException, but the only one that escapes\ncomes from the halt() call in its own catch block, leaving copyInOrder()\nto catch an interrupt it takes no part in. The interrupt copy() does\ntake part in becomes a -1 exit code, and as throwing\nInterruptedException clears the interrupt status, nothing above can tell\nan interrupted transfer from a failed one, or that the repair was\ncancelled at all.\n\nAbsorb the halt() interrupt where it happens, and report the copy\u0027s own\ninterrupt as InterruptedIOException, which is what java.io raises when\nan interrupt terminates a transfer. It unwinds the repair, so the copies\nand destinations left after a cancel are skipped instead of running to\ncompletion.\n\nThe SSH command lets it escape, which BaseCommand already treats as the\nclient having dropped off, exiting 127 without logging. Auto-repair logs\nthe interrupt and skips its follow-up replication.\n\nChange-Id: Ie61e0329aaa3131c8ac1f8b4932e57e48c5e5e0b\n"
    },
    {
      "commit": "e928e4fcc0f929cbe97ad497124094698183756d",
      "tree": "58ab4cafbcbad3790318ba153717e4753f427e0a",
      "parents": [
        "c8eb9426b9f497f1f00ee6198b53a00edab20442"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Wed Jul 29 09:49:47 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Tue Aug 11 12:38:06 2026 -0700"
      },
      "message": "Filter excluded refs before scheduling tasks\n\nexcludedRefsPattern was only applied when building the push,\nso matching refs were still queued and written to task storage\nas no-op work. Check the pattern in Destination.wouldPushRef()\nso those refs are skipped before a task is scheduled or persisted.\n\nChange-Id: Iebfca8a5d025bef57894d67578668422f05a56ad\n"
    },
    {
      "commit": "c8eb9426b9f497f1f00ee6198b53a00edab20442",
      "tree": "b3765ee7b09af19097dc34cfcc03cb44fd3812c1",
      "parents": [
        "f0b8c225e060c49072c5a000d5462cb46f5c5b53",
        "720c47d8810fe548623f88092002f598d01c8326"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Fri Aug 07 16:52:34 2026 -0700"
      },
      "committer": {
        "name": "Gerrit Code Review",
        "email": "noreply-gerritcodereview@google.com",
        "time": "Fri Aug 07 16:52:34 2026 -0700"
      },
      "message": "Merge \"ProjectRepairer: Use rsync --progress instead of -P\""
    },
    {
      "commit": "720c47d8810fe548623f88092002f598d01c8326",
      "tree": "3ffe6069fa161cbf0ea56c3fbff03a26979151da",
      "parents": [
        "c89dc44d4bddfc7d2d34051c7d5aebf7755c6f2e"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Thu Aug 06 17:20:44 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Fri Aug 07 08:49:30 2026 -0700"
      },
      "message": "ProjectRepairer: Use rsync --progress instead of -P\n\n\u0027-P\u0027 is \u0027--partial --progress\u0027. When a transfer is aborted, \u0027--partial\u0027\nrenames the temporary file rsync was streaming into onto the final\ndestination name, leaving a truncated file where a complete one is\nexpected.\n\nKeep \u0027--progress\u0027 for the transfer output and let rsync discard its\ntemporary file on failure.\n\nChange-Id: Icb4c37c5dfc950e1472447597a9cfade3bd97765\n"
    },
    {
      "commit": "f0b8c225e060c49072c5a000d5462cb46f5c5b53",
      "tree": "cae18665a6f9b1a67dc9c1f56c67674a43d1ebb2",
      "parents": [
        "796f1f48e9cb8743f4bad4d9779cf57069b92fdc"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Thu Aug 06 08:11:21 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Thu Aug 06 15:47:03 2026 -0700"
      },
      "message": "Distributor: Skip re-fire of already pending tasks\n\nOn every cycle the distributor streams all tasks under waiting/ and\ncalls fireFromStorage() for each one, which allocates a fresh\nReplicationState and consolidates it into the pending PushOne for that\nURI. PushOne deduplicates refs, since refBatchesToPush is a Set, but\nits stateMap is a ListMultimap that appends unconditionally. A task\nthat stays in waiting/ across cycles therefore grows the pending\ntask\u0027s notification list by one entry per ref per cycle while adding\nno work at all.\n\nThe distributor already snapshots the ref updates carried by pending\npush tasks, in order to prune queue entries whose stored counterpart\nis gone. Each streamed update is removed from that snapshot, leaving\nonly the prunable ones behind by the end. Reuse that removal as the\ntest, i.e a non-null result means a pending task on this node is already\nholding the update, so re-firing it would append a redundant state. Skip\nit, and let only updates with no pending task reach fireFromStorage().\nTasks written by another primary are absent from the snapshot and\ncontinue to be fired, so cluster distribution is unaffected.\n\nThis change removes the repeat re-fires when Prune.TRUE rather than\nevery possible source of stateMap growth. The startup replay passes\nPrune.FALSE and is deliberately left uncovered. It walks each waiting/\nfile exactly once, so no task is fired twice into the same pending push\nand the growth is not reachable there today.\n\nChange-Id: Ib9e77f9cf99cab3d5a6f110f0578ae02eafe9198\n"
    },
    {
      "commit": "796f1f48e9cb8743f4bad4d9779cf57069b92fdc",
      "tree": "8771b89a158f924e4c0eeaf3de2254b9b22807ad",
      "parents": [
        "c89dc44d4bddfc7d2d34051c7d5aebf7755c6f2e"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Thu Aug 06 15:35:18 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Thu Aug 06 15:44:52 2026 -0700"
      },
      "message": "Distributor: Add tests for firing of tasks from storage\n\nAdd test coverage to ensure the distributor fires an update which no\npending task on this node is holding, and the startup replay fires\nunconditionally, without looking at the pending push tasks.\n\nChange-Id: If7fb97cbe9e64dc21dbfb5593541ed2367dad1f5\n"
    },
    {
      "commit": "c89dc44d4bddfc7d2d34051c7d5aebf7755c6f2e",
      "tree": "6c6b7f92c5147dbbacecf96a6fcabf15b175178e",
      "parents": [
        "07782a0f6bee9e78546d250abd59dd099849a0a8"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Mon Jul 27 14:26:53 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Mon Jul 27 14:39:15 2026 -0700"
      },
      "message": "Show batch progress in PushOne queue tasks\n\nUpdate the running task display during batched pushes to append the\ncurrent batch progress, so users can track long-running replication\nwork from the queue.\n\nChange-Id: I29dae9ee62452bbb3bd3d945ea04c6c7a8cd5261\n"
    },
    {
      "commit": "07782a0f6bee9e78546d250abd59dd099849a0a8",
      "tree": "d5f7a6937a46026d5bd5de4180be6a34314bf62f",
      "parents": [
        "7e7c2417c7a9cccd6e408955acf271c68c359bb3"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Mon Jul 27 11:14:12 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Mon Jul 27 11:28:03 2026 -0700"
      },
      "message": "Update docs to clarify \u0027excludedRefsPattern\u0027 uses Java regex\n\nDocument that remote.NAME.excludedRefsPattern accepts only Java\nregular expressions and that the expression must fully match the\nref name.\n\nChange-Id: I95ba705a1c5ef0768359817299abcdb1dfdb9e24\n"
    },
    {
      "commit": "7e7c2417c7a9cccd6e408955acf271c68c359bb3",
      "tree": "6d8f7cef36ebbbabbbe360f28cded82a522d72dd",
      "parents": [
        "54898d71ffa30bcdaa0f37f968efb0985d9bca64"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Wed Jul 22 12:37:16 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Thu Jul 23 13:27:43 2026 -0700"
      },
      "message": "Allow setting remote.NAME.threads\u003d0 for persist-only mode\n\nSetting \u0027remote.NAME.threads\u0027 to 0 now puts a remote into a\npersist-only mode on the current node. No thread pool is created and\nthe node performs no pushes, project deletions or HEAD updates for\nthat remote. Replication tasks are still written to the task storage.\n\nThis supports multi-primary deployments where a user-facing primary\nonly records replication work, while a separate non user-facing\nprimary (configured with threads \u003e 0 and distributor enabled) picks\nthe tasks up from the shared storage and performs the actual pushes.\nAlso, when a node has no push-enabled remotes, both recovery and the\nstartup replay are skipped entirely.\n\nA caveat to setting threads to \u00270\u0027 is that project deletions and HEAD\nupdates originating on a persist-only node are not replicated by other\nnodes, as they are not persisted to the task storage. If needed, support\nfor persisting them can be added in future changes.\n\nChange-Id: I022865cbd7105563b5760ca05ae85847e0d7b97b\n"
    },
    {
      "commit": "54898d71ffa30bcdaa0f37f968efb0985d9bca64",
      "tree": "545c5cdf78ef694c6c392a484c5c4cd4c5ae3da0",
      "parents": [
        "0f4eec70496e7589dba4d7e432deb427c22b6069",
        "a418c12074e796ab8e01484c715804a071370e4a"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Mon Jul 20 09:08:35 2026 -0700"
      },
      "committer": {
        "name": "Gerrit Code Review",
        "email": "noreply-gerritcodereview@google.com",
        "time": "Mon Jul 20 09:08:35 2026 -0700"
      },
      "message": "Merge \"Auto-repair replication on missing necessary objects error\""
    },
    {
      "commit": "0f4eec70496e7589dba4d7e432deb427c22b6069",
      "tree": "e09f1e19c483de8dfc1af30953cef95c345ed05a",
      "parents": [
        "1b609b02f0b60df09c43baf1edca452a375924ca"
      ],
      "author": {
        "name": "Thomas Dräbing",
        "email": "thomas.draebing@sap.com",
        "time": "Mon Jul 20 11:23:59 2026 +0200"
      },
      "committer": {
        "name": "Thomas Draebing",
        "email": "thomas.draebing@sap.com",
        "time": "Mon Jul 20 03:55:28 2026 -0700"
      },
      "message": "Log exception data in JSON log\n\nWhen logging in JSON format, the exception data, e.g. stacktrace, which\nthe replication plugin logs into its own log are not logged to file.\n\nThe implementation was copied from\n`com.google.gerrit.pgm.util.ErrorJsonLogEntry` in Gerrit core.\n\nChange-Id: I690b9c21584d98dc69eabaf41fb2d4365f734ce0\n"
    },
    {
      "commit": "a418c12074e796ab8e01484c715804a071370e4a",
      "tree": "75ee12ee5add5a48a50abed3007253ab000c79b0",
      "parents": [
        "4296f7364429ffe5e537756199cd14daab2d3353"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Mon May 18 16:32:53 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Wed Jul 15 09:39:27 2026 -0700"
      },
      "message": "Auto-repair replication on missing necessary objects error\n\nWhen a replication push fails because the remote git-receive-pack\nreports \u0027missing necessary objects\u0027, the destination is likely corrupt\nand requires manual intervention. Detect that error and, when enabled,\nschedule an asynchronous repair.\n\nRepair only runs for plain SSH destinations (not gerrit+ssh, http, or\ngit protocols) since rsync over SSH is required. Attempts are tracked\nper project+destination and gated by three new config options:\n\n  * autoRepairMaxAttempts (default 0, disabled): maximum number of\n    auto-repair attempts per project on each destination URL\n  * autoRepairInterval (default 3days): minimum spacing between\n    attempts for the same (project, destination)\n  * autoRepairConcurrencyLimit (default 2): maximum number of\n    auto-repair tasks allowed to run concurrently\n\nTracker state is in-memory and resets on plugin reload or Gerrit\nrestart, so the cap is best-effort rather than persistent. Enabling\nauto-repair in a clustered deployment can lead to redundant repairs as\nstate is not shared across nodes. This could be improved later if\ndesired, perhaps by persisting the state using the replication task\nstorage.\n\nAuto-repair deliberately uses the failing destination URI instead of\nround-robin failover, since repairs target corruption errors and are\nmore likely to succeed on the URL that just reported the problem.\n\nRelease-Notes: Auto-repair replication on missing objects errors\nChange-Id: I815f620af93b484fa801d0c953d56b6a6b00be66\n"
    },
    {
      "commit": "1b609b02f0b60df09c43baf1edca452a375924ca",
      "tree": "88042b28a6209ff8c117afff06f54fec7834c1bf",
      "parents": [
        "4296f7364429ffe5e537756199cd14daab2d3353",
        "28ee207f72e3acb7910ab39d0a033c9da38b699c"
      ],
      "author": {
        "name": "Martin Fick US",
        "email": "mfick@nvidia.com",
        "time": "Tue Jul 14 09:26:42 2026 -0700"
      },
      "committer": {
        "name": "Gerrit Code Review",
        "email": "noreply-gerritcodereview@google.com",
        "time": "Tue Jul 14 09:26:42 2026 -0700"
      },
      "message": "Merge \"Retry failovers by applying url distribution strategy\""
    },
    {
      "commit": "28ee207f72e3acb7910ab39d0a033c9da38b699c",
      "tree": "9d7b763dc9f00bd26c88555e2ee17c00ac75d92f",
      "parents": [
        "681d9ab03db4d2ac3b7bfc81f64e490ddaceebfb"
      ],
      "author": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Mon Jun 01 23:33:58 2026 -0500"
      },
      "committer": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Mon Jul 13 18:32:13 2026 -0500"
      },
      "message": "Retry failovers by applying url distribution strategy\n\nPreviously, a push with urlDistributionStrategy\u003droundRobin retried the\nsame URL after a transport error - a single unreachable replica could\nblock replication for any push the rotation happened to land on it. Fix\nit to apply the distribution strategy on retries.\n\nChange-Id: Id8ff42afdcd40a315fbab1ecb272b5e87bd0880d\n"
    },
    {
      "commit": "4296f7364429ffe5e537756199cd14daab2d3353",
      "tree": "8e44b2a2e83dc049ea07452b0be17bf496b967c1",
      "parents": [
        "681d9ab03db4d2ac3b7bfc81f64e490ddaceebfb"
      ],
      "author": {
        "name": "Bhoomika Srinivas",
        "email": "bhoomikas@nvidia.com",
        "time": "Tue May 05 14:48:01 2026 -0700"
      },
      "committer": {
        "name": "Bhoomika Srinivas",
        "email": "bhoomikas@nvidia.com",
        "time": "Wed Jul 08 11:37:32 2026 +0530"
      },
      "message": "Add --ref option to start command\n\nUsers can now trigger replication for a specific reference using the\n--ref \u003cRefName\u003e option. This allows for targeted replication of\nindividual branches or tags rather than forcing a full project sync.\n\nExample usage:\n  ssh -p 29418 host replication start --ref refs/heads/master my-project\n\nChange-Id: If6d208411e2a1645fff4f7a6d2e6c5428b87e3f3\nRelease-Notes: Add --ref \u003cRefName\u003e option to start command\n"
    },
    {
      "commit": "681d9ab03db4d2ac3b7bfc81f64e490ddaceebfb",
      "tree": "93a5ab5b50562296b269a607ee7128b3ff0e24a2",
      "parents": [
        "7ee2a87d66715cca69a75c69670c5c16a416c011",
        "7c9a80f4b0b1f64439a0a658d3d62b3c1afe7417"
      ],
      "author": {
        "name": "Adithya C",
        "email": "achakilam@nvidia.com",
        "time": "Tue Jun 09 14:20:15 2026 -0700"
      },
      "committer": {
        "name": "Gerrit Code Review",
        "email": "noreply-gerritcodereview@google.com",
        "time": "Tue Jun 09 14:20:15 2026 -0700"
      },
      "message": "Merge \"Destination: remove unused method\""
    },
    {
      "commit": "7c9a80f4b0b1f64439a0a658d3d62b3c1afe7417",
      "tree": "d9cba729719c3cc24656752fbd87db70fae7523f",
      "parents": [
        "57636c9a0273f0671209b9bb8051c42504df1a90"
      ],
      "author": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Tue Jun 09 15:45:18 2026 -0500"
      },
      "committer": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Tue Jun 09 15:45:18 2026 -0500"
      },
      "message": "Destination: remove unused method\n\nChange-Id: I97fb13b03694572c1925f65abdc941cffd9fbe8b\n"
    },
    {
      "commit": "7ee2a87d66715cca69a75c69670c5c16a416c011",
      "tree": "cc2bdd583e277edb285db95f2bca465476df5425",
      "parents": [
        "3e623c75033c815b97b5aa9b112dcc75c17dc018"
      ],
      "author": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Fri May 29 12:14:23 2026 -0500"
      },
      "committer": {
        "name": "Adithya C",
        "email": "achakilam@nvidia.com",
        "time": "Mon Jun 01 15:21:52 2026 -0700"
      },
      "message": "Add config option to disable replication event emission\n\nThe plugin posts a stream event at every point of the replication\nlifecycle (ref-replicated, replication-scheduled, replication-done,\nproject-deletion-*). When no listener is configured these events are\npure overhead. The cost is especially visible in multi-primary\ndeployments running an events-sharing plugin that fans every\nreplication event out to all the other primaries, even though nothing\non either end consumes it.\n\nIntroduce \u0027replication.emitEvents\u0027 (default true, so existing behavior\nis preserved) to suppress emission at the source.\n\nChange-Id: I32972d371ec83856e6783164bad9a229d1c8f934\n"
    },
    {
      "commit": "3e623c75033c815b97b5aa9b112dcc75c17dc018",
      "tree": "21f42cce401c5211ae465a61fe026dc09a1a8ee5",
      "parents": [
        "57636c9a0273f0671209b9bb8051c42504df1a90",
        "33fc916b0425bdbfbba6fc96047de5b5a0bb8777"
      ],
      "author": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Thu May 28 18:56:52 2026 -0500"
      },
      "committer": {
        "name": "Adithya C",
        "email": "achakilam@nvidia.com",
        "time": "Thu May 28 17:42:05 2026 -0700"
      },
      "message": "Merge branch \u0027stable-3.13\u0027\n\n* origin/stable-3.13:\n  Fix --remote option to replication start\n  Add RoundRobin URL selection per remote\n  Improve the wording to explain url matching\n\nRelease-Notes: skip\nChange-Id: I7939ec71984a80496f7baf0bb9d86fb749316d7a\n"
    },
    {
      "commit": "33fc916b0425bdbfbba6fc96047de5b5a0bb8777",
      "tree": "9a0d315bfa884b8f7f79ff59eaa71f6d450c92c1",
      "parents": [
        "163612b4daba5f3966d9dc646cb755a654a7bb66",
        "7a8f3429ed555d3576ddc598cf6d6c6ecb453cbd"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Thu May 28 21:03:23 2026 +0200"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Thu May 28 22:16:33 2026 +0200"
      },
      "message": "Merge branch \u0027stable-3.12\u0027 into stable-3.13\n\n* stable-3.12:\n  Fix --remote option to replication start\n  Add RoundRobin URL selection per remote\n  Improve the wording to explain url matching\n\nRelease-Notes: skip\nChange-Id: I98294fefeca033b53b0d3bc7c12372656e1c9ec9\n"
    },
    {
      "commit": "7a8f3429ed555d3576ddc598cf6d6c6ecb453cbd",
      "tree": "228cbebd87d0df6b0335e51d30f0af568ec29c03",
      "parents": [
        "4698ef08af7776834fbe14aa859654ac16c45dba",
        "e2c75745275cff5560921917769176431d9a2433"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Thu May 28 21:00:58 2026 +0200"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Thu May 28 21:00:58 2026 +0200"
      },
      "message": "Merge branch \u0027stable-3.11\u0027 into stable-3.12\n\n* stable-3.11:\n  Fix --remote option to replication start\n  Add RoundRobin URL selection per remote\n  Improve the wording to explain url matching\n\nRelease-Notes: skip\nChange-Id: Ice84b2af7a2c5d794538fdb2c909942223f8f5c4\n"
    },
    {
      "commit": "57636c9a0273f0671209b9bb8051c42504df1a90",
      "tree": "3b3c511040559764854a2eff4f3d255d87b6dbd8",
      "parents": [
        "fbf435c148d0edded8d37dcb7588e2c28195208d"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Wed May 20 12:32:14 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Wed May 20 12:37:40 2026 -0700"
      },
      "message": "Extract pack-copy logic into ProjectRepairer\n\nMove the pack-copy logic from RepairCommand into a new shared\nProjectRepairer class so it can be reused by other callers (for\nexample, an automated repair flow). RepairCommand now delegates the\nper-destination copy to ProjectRepairer.\n\nChange-Id: I86166730f02bf66963ce603cc929c49fbd895fff\n"
    },
    {
      "commit": "fbf435c148d0edded8d37dcb7588e2c28195208d",
      "tree": "f744a113ce9b374bee3064ae34446b82ddb6a8b7",
      "parents": [
        "19b159df1ccfdd0916d0f5701aa0cacf58507d9f"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Wed May 20 12:31:52 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Wed May 20 12:35:05 2026 -0700"
      },
      "message": "Repair destinations end-to-end in repair command\n\nPreviously, the SSH \u0027repair\u0027 command copied packs to all destinations\nfirst and then issued a single replication start for the project. A\nfailure during the copy phase still left a replication attempt queued\nfor the affected destination, which is not desired. Rework the command\nto drive one destination at a time so that the follow-up replication\nis only triggered when its preceding pack copy succeeds.\n\nChange-Id: Ibcc8cd5ee8a005b0c29d4fe9f4f8e1af96524d32\n"
    },
    {
      "commit": "19b159df1ccfdd0916d0f5701aa0cacf58507d9f",
      "tree": "8f198b89d054fec988c2609bfc1299fc4396881f",
      "parents": [
        "6c7ba5381d40017d64d309ce8babeb7fb1bdb058"
      ],
      "author": {
        "name": "Martin Fick",
        "email": "mfick@nvidia.com",
        "time": "Thu Jul 03 11:35:46 2025 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Tue May 19 12:38:04 2026 -0700"
      },
      "message": "Add explicit project exclusion support\n\nWhile it was previously possible to exclude projects with the \u0027projects\u0027\nkeyword by creating a negative match regular expression, this required\nall the excluded projects to be specified in a single ORed regex which\nis very ugly and error prone. Instead, introduce an explicit keyword,\n\u0027excludeProjects\u0027, which may be specified more than once to handle\nproject exclusions in a straight forward manner.\n\nAlso, bump ReplicationIT test target timeout to long. It defaulted to\nBazel\u0027s \u0027moderate\u0027 timeout bucket (300s). With new test additions in\nthis change, it will reach that ceiling and fail with a timeout. Set\nits \u0027timeout\u0027 to long (900s).\n\nRelease-Notes: Add explicit project exclusion support\nChange-Id: I4e50f923f734537bc32b6a350d3268dbc286d818\n"
    },
    {
      "commit": "6c7ba5381d40017d64d309ce8babeb7fb1bdb058",
      "tree": "46ae6c1059cf60610018254613aaa1c703c7384c",
      "parents": [
        "aa3f6318ff40d11566c3cf181ebeae0fea4b069a"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Tue Mar 24 23:42:25 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Mon May 18 12:39:38 2026 -0700"
      },
      "message": "Add SSH command to repair projects\n\nIntroduce a new \u0027replication repair PROJECT --copy-packs\u0027 SSH command\nthat copies the pack files for a single project to desired destinations\nand then triggers a \u0027replication start\u0027.\n\nChange-Id: Ic9f55e42f195a1261ae076c9fdc9a320b75b292a\n"
    },
    {
      "commit": "aa3f6318ff40d11566c3cf181ebeae0fea4b069a",
      "tree": "dfdd1f86d189b5a92c64627c0d47eeed1a8ffb45",
      "parents": [
        "bdf6d450db1a3524ed48d5d66801396300c43492"
      ],
      "author": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Wed May 13 12:47:11 2026 +0200"
      },
      "committer": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Fri May 15 12:30:48 2026 +0200"
      },
      "message": "Avoid string concatenation in Flogger log statements\n\nMove shared log message prefixes into log arguments instead of\nconcatenating them at logging sites.\n\nThis keeps format placeholders and arguments validated by Flogger while\npreserving the existing logging behavior for multi-primary setups.\n\nChange-Id: I09d97fa45a294b0f42fec43d935f3597f3cbcf7b\n"
    },
    {
      "commit": "bdf6d450db1a3524ed48d5d66801396300c43492",
      "tree": "7fe69aacc594af3b706742325efc60e443bf53e1",
      "parents": [
        "87a975dd6a62d383fc2d3914162365976123e1c2"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Thu May 07 11:48:30 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Tue May 12 09:04:23 2026 -0700"
      },
      "message": "Support configurable git binary path for SSH remotes\n\nSome SSH destinations may not expose \u0027git\u0027 on the non-interactive\nshell\u0027s PATH, causing \u0027create project\u0027 and \u0027update head\u0027 operations\nto fail. Add a new \u0027remote.NAME.gitPath\u0027 config option that lets\nadmins provide the absolute path to the \u0027git\u0027 binary on the remote\nhost.\n\nChange-Id: I01e32cff5240cb55cf9e0fc0539344b340361ac4\n"
    },
    {
      "commit": "e2c75745275cff5560921917769176431d9a2433",
      "tree": "c28091e20ec611130f1691c84bf1954aa34422c5",
      "parents": [
        "d8ae6cc531c634c5e594470fef5fb4902cb0f048"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Tue May 05 14:48:01 2026 -0700"
      },
      "committer": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@oss.qualcomm.com",
        "time": "Tue May 12 08:41:11 2026 -0700"
      },
      "message": "Fix --remote option to replication start\n\nDeclaring --remote as a \u0027Set\u003cString\u003e\u0027 field trips args4j\u0027s\nSetters.create(), which only treats List, Map, and Arrays as\nmulti-value containers. Use a method-based @Option that adds\neach --remote value into a Set\u003cString\u003e to work around this.\n\nChange-Id: Ic4b4242f3c9ac05b822f2b62141ba34270619f70\n(cherry picked from commit 87a975dd6a62d383fc2d3914162365976123e1c2)\n"
    },
    {
      "commit": "d8ae6cc531c634c5e594470fef5fb4902cb0f048",
      "tree": "c444e79f75d2d238f95cf2a2a9aed5f86e6bdfef",
      "parents": [
        "64ac69d2a6f5f66935ae20d6160f1624de4eb8d4",
        "2bac45ee6400abd7afd1782bb587bda19d7aac20"
      ],
      "author": {
        "name": "Martin Fick",
        "email": "mfick@nvidia.com",
        "time": "Tue May 05 16:02:25 2026 -0700"
      },
      "committer": {
        "name": "Martin Fick",
        "email": "mfick@nvidia.com",
        "time": "Tue May 05 16:16:29 2026 -0700"
      },
      "message": "Merge branch \u0027stable-3.10\u0027 into stable-3.11\n\n* stable-3.10:\n  Improve the wording to explain url matching\n\nRelease-Notes: skip\nChange-Id: Ifbb127d334c5a7c9cb74b5ffb6c64e9f1c7b00ca\n"
    },
    {
      "commit": "87a975dd6a62d383fc2d3914162365976123e1c2",
      "tree": "a35141346b3dc29eea03b7333ba268b69c64a7db",
      "parents": [
        "9b78af806be9cc7131a856f81051ee78d4f8c0e9"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Tue May 05 14:48:01 2026 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "klingarkar@nvidia.com",
        "time": "Tue May 05 14:50:27 2026 -0700"
      },
      "message": "Fix --remote option to replication start\n\nDeclaring --remote as a \u0027Set\u003cString\u003e\u0027 field trips args4j\u0027s\nSetters.create(), which only treats List, Map, and Arrays as\nmulti-value containers. Use a method-based @Option that adds\neach --remote value into a Set\u003cString\u003e to work around this.\n\nChange-Id: Ic4b4242f3c9ac05b822f2b62141ba34270619f70\n"
    },
    {
      "commit": "64ac69d2a6f5f66935ae20d6160f1624de4eb8d4",
      "tree": "89c77e0230bdf1d4cb4f811b8e13eca7cc85ad22",
      "parents": [
        "24b0f4b53a6cd48295a3aa78f57aa4d59531b639"
      ],
      "author": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Fri Apr 24 12:29:21 2026 -0500"
      },
      "committer": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Mon May 04 14:46:20 2026 -0500"
      },
      "message": "Add RoundRobin URL selection per remote\n\nIntroduce a `urlDistributionStrategy` config flag on remote sections.\nWhen set to `roundRobin`, each replication push selects one URL from the\nconfigured list in round-robin order rather than pushing to all URLs\nsimultaneously.\n\nThis is particularly useful in high-availability setups where multiple\nreplica hosts share a single NFS backend. Pushing to all URLs at once\nwould result in redundant writes to the same underlying storage.\nRound-robin distributes push load evenly across hosts while ensuring\neach push is executed exactly once.\n\nChange-Id: Id67cddcf9e7f7a6f47f259eb78b0f53f0977f536\n"
    },
    {
      "commit": "2bac45ee6400abd7afd1782bb587bda19d7aac20",
      "tree": "4f249968d5a9b8bb9aee1b8ca842c31a33c60d6d",
      "parents": [
        "5be3cc55304ebdef853fb43ec184a51f59fdf07f",
        "5aef8b8aa6ffdea9865f2f252b49324d2cff8ced"
      ],
      "author": {
        "name": "Martin Fick",
        "email": "mfick@nvidia.com",
        "time": "Tue Apr 28 14:24:34 2026 -0700"
      },
      "committer": {
        "name": "Martin Fick",
        "email": "mfick@nvidia.com",
        "time": "Tue Apr 28 14:34:05 2026 -0700"
      },
      "message": "Merge branch \u0027stable-3.9\u0027 into stable-3.10\n\n* stable-3.9:\n  Improve the wording to explain url matching\n\nRelease-Notes: skip\nChange-Id: Ie8ac890468c101ea9f4658ad7f9792ca838e7594\n"
    },
    {
      "commit": "9b78af806be9cc7131a856f81051ee78d4f8c0e9",
      "tree": "a074f7a6489c3ad1ff54df8f0b5c028e538978aa",
      "parents": [
        "1af23a265ad1fba98915e69b5bf928a747c5f5c4",
        "163612b4daba5f3966d9dc646cb755a654a7bb66"
      ],
      "author": {
        "name": "Antonio Barone",
        "email": "syntonyze@gmail.com",
        "time": "Mon Apr 27 17:49:23 2026 +0200"
      },
      "committer": {
        "name": "Antonio Barone",
        "email": "syntonyze@gmail.com",
        "time": "Mon Apr 27 17:49:23 2026 +0200"
      },
      "message": "Merge branch \u0027stable-3.13\u0027\n\n* stable-3.13:\n\nChange-Id: I6d695149a9d1120bcfbc8755cbdc89f6c19f6620\n"
    },
    {
      "commit": "163612b4daba5f3966d9dc646cb755a654a7bb66",
      "tree": "fe8cead676835039d341ce81169716cf967f0c63",
      "parents": [
        "8dafd933a883bdd15135a382753e1b9ea447daa0",
        "4698ef08af7776834fbe14aa859654ac16c45dba"
      ],
      "author": {
        "name": "Antonio Barone",
        "email": "syntonyze@gmail.com",
        "time": "Mon Apr 27 17:30:03 2026 +0200"
      },
      "committer": {
        "name": "Antonio Barone",
        "email": "syntonyze@gmail.com",
        "time": "Mon Apr 27 17:30:03 2026 +0200"
      },
      "message": "Merge branch \u0027stable-3.12\u0027 into stable-3.13\n\n* stable-3.12:\n  Add --remote switch to replication start command\n\nChange-Id: Ifa94395155956f48dae777e0e41222a79554afbd\n"
    },
    {
      "commit": "1af23a265ad1fba98915e69b5bf928a747c5f5c4",
      "tree": "a074f7a6489c3ad1ff54df8f0b5c028e538978aa",
      "parents": [
        "d638cebddd6ba09f7adbfe774147eba9f32dd747",
        "4698ef08af7776834fbe14aa859654ac16c45dba"
      ],
      "author": {
        "name": "Antonio Barone",
        "email": "syntonyze@gmail.com",
        "time": "Mon Apr 27 15:44:46 2026 +0200"
      },
      "committer": {
        "name": "Antonio Barone",
        "email": "syntonyze@gmail.com",
        "time": "Mon Apr 27 15:44:46 2026 +0200"
      },
      "message": "Merge branch \u0027stable-3.12\u0027\n\n* stable-3.12:\n  Add --remote switch to replication start command\n\nChange-Id: Ic61ad77a3bb4f4347b2e9488b0dba49af9666c15\n"
    },
    {
      "commit": "4698ef08af7776834fbe14aa859654ac16c45dba",
      "tree": "f2317ec05ba3f1b850ae7a5cf5a62ddbc58e59ad",
      "parents": [
        "b0e7aa63cf3e6ee2afb82d96aa117e38600b0015",
        "24b0f4b53a6cd48295a3aa78f57aa4d59531b639"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Mon Apr 27 14:05:03 2026 +0100"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Mon Apr 27 14:05:03 2026 +0100"
      },
      "message": "Merge branch \u0027stable-3.11\u0027 into stable-3.12\n\n* stable-3.11:\n  Add --remote switch to replication start command\n\nChange-Id: I414161edf66faf9de4d2b384b479b338bdcb9880\n"
    },
    {
      "commit": "24b0f4b53a6cd48295a3aa78f57aa4d59531b639",
      "tree": "b24036979653359450be691dfbe5731862fba830",
      "parents": [
        "83e15b020a4693bbc0daa24acf6e08bb9e8f8864"
      ],
      "author": {
        "name": "Adithya Chakilam",
        "email": "achakilam@nvidia.com",
        "time": "Thu Apr 23 15:38:44 2026 -0500"
      },
      "committer": {
        "name": "Adithya C",
        "email": "achakilam@nvidia.com",
        "time": "Thu Apr 23 19:11:23 2026 -0700"
      },
      "message": "Add --remote switch to replication start command\n\nAllow specifying one or more remote names to limit replication to\nthose destinations, without needing to know their URLs.\n\nUnlike --url which does substring matching on the URL, --remote\nmatches the exact remote name as configured in replication.config.\n\nChange-Id: I9db7a195d7a1ea8bef92d008a76386a01d0c9286\n"
    },
    {
      "commit": "d638cebddd6ba09f7adbfe774147eba9f32dd747",
      "tree": "4770ea8af868763b102144d0ace404050fc8c571",
      "parents": [
        "8dafd933a883bdd15135a382753e1b9ea447daa0"
      ],
      "author": {
        "name": "Thomas Dräbing",
        "email": "thomas.draebing@sap.com",
        "time": "Fri Dec 19 09:00:11 2025 +0100"
      },
      "committer": {
        "name": "Thomas Dräbing",
        "email": "thomas.draebing@sap.com",
        "time": "Tue Apr 07 14:10:08 2026 +0200"
      },
      "message": "Use of gerrit_plugin and gerrit_plugin_tests rules from bazlets\n\nChange-Id: If0c4d82b3f77cedccf94b6b74f775176a9d0bf6d\n"
    },
    {
      "commit": "5aef8b8aa6ffdea9865f2f252b49324d2cff8ced",
      "tree": "71ccc70105e8ac43ea155dd6402e3c6e06f7570b",
      "parents": [
        "5cfb07aa0e1e133a066ae81951b8e156d7dfd4e4"
      ],
      "author": {
        "name": "Martin Fick",
        "email": "mfick@nvidia.com",
        "time": "Fri Dec 05 11:47:28 2025 -0800"
      },
      "committer": {
        "name": "Martin Fick US",
        "email": "mfick@nvidia.com",
        "time": "Fri Feb 27 11:43:24 2026 -0800"
      },
      "message": "Improve the wording to explain url matching\n\nEverytime I use this switch I struggle and end up having to look a the\ncode to figure out how it works. Help clarify how the match works.\n\nChange-Id: I1001e83d677483bf8372febc4cc6f3eea15b8341\n"
    },
    {
      "commit": "8dafd933a883bdd15135a382753e1b9ea447daa0",
      "tree": "c50542badd3bff28ac87c32032cda7030104f1dc",
      "parents": [
        "5fc8c9e190b9427a864227c55bb4e7cb1ba93c8b"
      ],
      "author": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Mon Jan 05 14:29:04 2026 +0000"
      },
      "committer": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Mon Jan 05 14:31:00 2026 +0000"
      },
      "message": "Replace legacy non-Javadoc comment with {@inheritDoc}\n\nChange-Id: I3cf468b60ba4bfb8e948a17681cd6ab91df62f2a\n"
    },
    {
      "commit": "5fc8c9e190b9427a864227c55bb4e7cb1ba93c8b",
      "tree": "fa2e02d4ca90b21e32a27b17b8cb4b7459aa31b4",
      "parents": [
        "b859e22bbcfbafb2237daf5302b9118917e4747e"
      ],
      "author": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Mon Jan 05 14:24:07 2026 +0000"
      },
      "committer": {
        "name": "David Ostrovsky",
        "email": "david@ostrovsky.org",
        "time": "Mon Jan 05 14:30:37 2026 +0000"
      },
      "message": "Suppress UnnecessaryAssignment in withBaseOnly factory method\n\nwithBaseOnly() intentionally assigns an @Inject field to support an\nalternate construction path. Suppress Error Prone\u0027s\nUnnecessaryAssignment warning for this method.\n\nChange-Id: I30cc92f830d562d87bd0437d8ac374ed0a2f7295\n"
    },
    {
      "commit": "b859e22bbcfbafb2237daf5302b9118917e4747e",
      "tree": "5a710e69d283ea54b8412aba77566b062b2a9d3d",
      "parents": [
        "fbd8be05f18a18aba10661be97b07435167c1788"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Thu Dec 11 08:36:14 2025 +0000"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Thu Dec 11 01:00:16 2025 -0800"
      },
      "message": "Introduce an educated guess comment on why reflog is disabled on replicas\n\nWhen repositories are replicated, their reflog is turned off on the\nreplica side. Add a comment explaining why that choice was possibly made\nover a decade ago.\n\nThe author of the change is Shawn who passed years ago. The change did\nnot go through reviews at that time so there is no track record of the\nchoice.\n\nChange-Id: I04de480df0d6012e3a4539fda945d15fc3f4eaa3\n"
    },
    {
      "commit": "fbd8be05f18a18aba10661be97b07435167c1788",
      "tree": "21e8b7e07180467408ed6ab52bf9e513369ec249",
      "parents": [
        "ead0da3314f71a334400e3bbb140998c15554fec"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Fri Nov 28 15:31:46 2025 +0000"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Thu Dec 11 01:00:04 2025 -0800"
      },
      "message": "Allow to specify whether to store reflog for new projects\n\nIntroduce a new remote.NAME.storeRefLog for configuring the\ncore.logallrefupdates in the new repository\u0027s config file when it is\ncreated by replication.\n\nRelease-Notes: Allow configuring the reflog for newly created repositories with remote.NAME.storeRefLog config.\nBug: Issue 464210139\nChange-Id: Ia14a4b5829cceb226a537c0651d08f2057fce8aa\n"
    },
    {
      "commit": "ead0da3314f71a334400e3bbb140998c15554fec",
      "tree": "d173d3aa93797faee8b8bb9b48c8398529176147",
      "parents": [
        "071e64c36ef8028e59947c570f9bc71c8092b86f"
      ],
      "author": {
        "name": "Thomas Dräbing",
        "email": "thomas.draebing@sap.com",
        "time": "Tue Nov 25 11:37:28 2025 +0100"
      },
      "committer": {
        "name": "Thomas Dräbing",
        "email": "thomas.draebing@sap.com",
        "time": "Mon Dec 01 15:09:31 2025 +0100"
      },
      "message": "Fix error_prone pattern AssertEqualsArgumentOrderChecker\n\nChange-Id: I98d9a8bff87189e2c2349dbcd27fc8ce05263442\n"
    },
    {
      "commit": "071e64c36ef8028e59947c570f9bc71c8092b86f",
      "tree": "9010b33325dcd521bcdab92d9fa3c5e92ba4ab75",
      "parents": [
        "00228d21334cd67fd029eb5322eea66e6c555665"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Fri Nov 28 15:20:33 2025 +0000"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Fri Nov 28 09:54:33 2025 -0800"
      },
      "message": "Remove redundant public modifier in AdminApi interface\n\nChange-Id: I5296d802a857466d5267e9b77879760efbe1abf8\n"
    },
    {
      "commit": "00228d21334cd67fd029eb5322eea66e6c555665",
      "tree": "e849b9388370f5536f508083ec8675e0f3fe111a",
      "parents": [
        "b62454c285f78f23996864c9f920473f8f3d0f6a"
      ],
      "author": {
        "name": "Jacek Centkowski",
        "email": "geminica.programs@gmail.com",
        "time": "Fri Oct 31 07:30:07 2025 +0100"
      },
      "committer": {
        "name": "Jacek Centkowski",
        "email": "geminica.programs@gmail.com",
        "time": "Sun Nov 02 15:21:06 2025 +0100"
      },
      "message": "Catch `URISyntaxException` in TestUriUpdates constructor\n\nAnd retrow as RuntimeException so that it doesn\u0027t have to be caught in\ntests (sometimes with empty catch block that is being flagged as ERROR\nafter migration to newer errorprone).\n\nBug: Issue 303819949\nChange-Id: Ia7a9427f0472bb464104ed7cc24b850e08869118\n"
    },
    {
      "commit": "b62454c285f78f23996864c9f920473f8f3d0f6a",
      "tree": "a4f652e8a8f4782caf460a6476821a6ec7f6582b",
      "parents": [
        "721b87a89e3a1bd2c368e051dec12445697680c5",
        "79b50b0020bfc69d863bbd5811baadec3c8a19d2"
      ],
      "author": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@oss.qualcomm.com",
        "time": "Mon Jul 07 12:35:04 2025 -0700"
      },
      "committer": {
        "name": "Gerrit Code Review",
        "email": "noreply-gerritcodereview@google.com",
        "time": "Mon Jul 07 12:35:04 2025 -0700"
      },
      "message": "Merge \"docs/config: document inverted matches cancel each others\""
    },
    {
      "commit": "79b50b0020bfc69d863bbd5811baadec3c8a19d2",
      "tree": "ee09096d290885fcfeefd619ceaa4b4783113a00",
      "parents": [
        "ede3010e0455b7ce68da62695a5eeb76e7cff420"
      ],
      "author": {
        "name": "Antoine Musso",
        "email": "hashar@free.fr",
        "time": "Thu Apr 17 12:37:00 2025 +0200"
      },
      "committer": {
        "name": "Antoine Musso (hashar)",
        "email": "hashar@free.fr",
        "time": "Wed Jul 02 01:02:55 2025 -0700"
      },
      "message": "docs/config: document inverted matches cancel each others\n\nOur config had two inverted matches to exclude projects:\n\n  [remote \"github\"]\n    projects \u003d ^(?:(?!apps\\\\/android\\\\/).)*$\n    projects \u003d ^(?:(?!apps\\\\/ios\\\\/).)*$\n\nI have been wondering why `apps/android/wikipedia` is excluded given it\nmatches the first regex. The reason is ReplicationFilter applies each\npattern and when one matches it accepts the project. In this case, the\nproject is matched by the second and is thus accepted.\n\nRef: https://phabricator.wikimedia.org/T392198\nChange-Id: I2846a40d2aabe80c0db305dc4f64dd1aa885b07d\n"
    },
    {
      "commit": "721b87a89e3a1bd2c368e051dec12445697680c5",
      "tree": "ae9ede5809abcf82468d573f1b2c6403b1998d70",
      "parents": [
        "38a79f76b4432679b69736dd7cea4ed254a6f09d",
        "b0e7aa63cf3e6ee2afb82d96aa117e38600b0015"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Tue Jul 01 10:54:12 2025 +0100"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Tue Jul 01 10:54:12 2025 +0100"
      },
      "message": "Merge branch \u0027stable-3.12\u0027\n\n* stable-3.12:\n  Fix concurrent pushes to the same target URI\n\nChange-Id: I04170e0108bca0d17be8b75a1198fc565cb1fccc\n"
    },
    {
      "commit": "b0e7aa63cf3e6ee2afb82d96aa117e38600b0015",
      "tree": "5e546deba4d82594a02f7efd9f4d002e25fbec6f",
      "parents": [
        "e94dfc98f0d68f2699daf43dc3ee56b18a4c9fc7",
        "83e15b020a4693bbc0daa24acf6e08bb9e8f8864"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Tue Jul 01 10:53:46 2025 +0100"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Tue Jul 01 10:53:46 2025 +0100"
      },
      "message": "Merge branch \u0027stable-3.11\u0027 into stable-3.12\n\n* stable-3.11:\n  Fix concurrent pushes to the same target URI\n\nChange-Id: I7b5778de0a757023180e5d02f934bbf57e354936\n"
    },
    {
      "commit": "83e15b020a4693bbc0daa24acf6e08bb9e8f8864",
      "tree": "c34bcfa51d05a50120c903c6aad5b970e2503cd3",
      "parents": [
        "d29aa0d4cf02690e224ca3c896716f5d4f433b38"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Mon Jun 30 19:12:10 2025 +0100"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Tue Jul 01 00:59:11 2025 -0700"
      },
      "message": "Fix concurrent pushes to the same target URI\n\nWhen performing the refactoring of stripe locks, the runway request was\noverlooked and completely ignored, leading to all tasks being allowed to\nbe executed concurrently with existing pushes on the runway.\n\nThe issue has created significant overload on the replication plugin for\nhigh-load repositories, where the rate of replication events scheduled\nis higher than the push time.\n\nBug: Issue 428670066\nRelease-Notes: Fix the prevention of concurrent PushOps the same target URI when using multiple threads\nChange-Id: I6863944e997e3541186dfc13cab0d7add29b7805\n"
    },
    {
      "commit": "38a79f76b4432679b69736dd7cea4ed254a6f09d",
      "tree": "b99c70121c438f59ca80419648c5cbda7e5f8db0",
      "parents": [
        "9522db215bbdd9cb23220da7d4402ffe027c008b",
        "e94dfc98f0d68f2699daf43dc3ee56b18a4c9fc7"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Fri Jun 20 15:32:46 2025 +0100"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Fri Jun 20 15:32:46 2025 +0100"
      },
      "message": "Merge branch \u0027stable-3.12\u0027\n\n* stable-3.12:\n  Make excludedRefsPattern() to return an empty list by default\n  Do not push deletes for refs configured to be skipped\n  Add a remote config to exclude replicating desired refs\n  Auto-format source code using gjf\n\nChange-Id: I57e2f34aa6b3012d2aaca69ac13899ab2bc392c2\n"
    },
    {
      "commit": "e94dfc98f0d68f2699daf43dc3ee56b18a4c9fc7",
      "tree": "d4ed1e0433e1c713a6d2e8e8bb2bd1a6a8db447f",
      "parents": [
        "ede3010e0455b7ce68da62695a5eeb76e7cff420",
        "d29aa0d4cf02690e224ca3c896716f5d4f433b38"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Fri Jun 20 15:19:32 2025 +0100"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Fri Jun 20 15:24:08 2025 +0100"
      },
      "message": "Merge branch \u0027stable-3.11\u0027 into stable-3.12\n\n* stable-3.11:\n  Make excludedRefsPattern() to return an empty list by default\n  Do not push deletes for refs configured to be skipped\n  Add a remote config to exclude replicating desired refs\n  Auto-format source code using gjf\n\nChange-Id: I0b5d42ee5e0f6cc342ef2947d209f89edbaf2c76\n"
    },
    {
      "commit": "d29aa0d4cf02690e224ca3c896716f5d4f433b38",
      "tree": "09148f8970785eae8b4728910c2e05880eec77b7",
      "parents": [
        "12f3a1abd0f112adac582c2c17a9beae217b5ffd",
        "5be3cc55304ebdef853fb43ec184a51f59fdf07f"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Fri Jun 20 15:16:18 2025 +0100"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Fri Jun 20 15:16:18 2025 +0100"
      },
      "message": "Merge branch \u0027stable-3.10\u0027 into stable-3.11\n\n* stable-3.10:\n  Make excludedRefsPattern() to return an empty list by default\n  Do not push deletes for refs configured to be skipped\n\nChange-Id: Icab5579c4d3f1d8c8d2fac06492d063f6cab03d9\n"
    },
    {
      "commit": "5be3cc55304ebdef853fb43ec184a51f59fdf07f",
      "tree": "c1d881a7293af34b166b35ae6a1546cdff51e6e0",
      "parents": [
        "f01716cb201ba38178d817701665ecf315ef926c"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Thu Jun 19 23:17:42 2025 +0100"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Thu Jun 19 23:22:18 2025 +0100"
      },
      "message": "Make excludedRefsPattern() to return an empty list by default\n\nThe extension of the replication endpoints configuration done\nwith I42366148cfb1f introduced a breaking change that made other\nplugins (e.g. pull-replication) to fail.\n\nRestore a backward compatible implementation of the new\nexcludedRefPattern configuration and amend the documentation to\nspecify that is empty by default.\n\nChange-Id: Iee92281ed41ca70819de783b3563832363c245fb\n"
    },
    {
      "commit": "f01716cb201ba38178d817701665ecf315ef926c",
      "tree": "731d6645129a00d86504c93bef4be72fbb39bfb3",
      "parents": [
        "4161f94990ee146fd78789b96a3a4729a584a5d4"
      ],
      "author": {
        "name": "Nasser Grainawi",
        "email": "nasserg@qti.qualcomm.com",
        "time": "Fri Jun 06 16:18:06 2025 -0700"
      },
      "committer": {
        "name": "Nasser Grainawi",
        "email": "nasserg@qti.qualcomm.com",
        "time": "Fri Jun 06 16:33:56 2025 -0700"
      },
      "message": "Do not push deletes for refs configured to be skipped\n\nWhen pushing a delta set of refs, instead of treating non-existent and\n!canPush() refs the same, use the same logic as when pushing all refs\nand log and skip any !canPush() refs.\n\nRelease-Notes: refs configured to be skipped are not pushed as deletes\nChange-Id: I3491c907580b16ea23493a5eaa325d2ef7d6d108\n"
    },
    {
      "commit": "12f3a1abd0f112adac582c2c17a9beae217b5ffd",
      "tree": "9958aee6f37e8485783fc00681d29c81dcfcb7b1",
      "parents": [
        "0af31d2a5df62329162a750beec2e9dc0adf8e72",
        "4161f94990ee146fd78789b96a3a4729a584a5d4"
      ],
      "author": {
        "name": "Nasser Grainawi",
        "email": "nasserg@qti.qualcomm.com",
        "time": "Thu Jun 05 13:01:22 2025 -0700"
      },
      "committer": {
        "name": "Nasser Grainawi",
        "email": "nasserg@qti.qualcomm.com",
        "time": "Thu Jun 05 13:01:22 2025 -0700"
      },
      "message": "Merge branch \u0027stable-3.10\u0027 into stable-3.11\n\n* stable-3.10:\n  Add a remote config to exclude replicating desired refs\n  Auto-format source code using gjf\n\nChange-Id: I0ba471d5649b5852c57e40244353210dbc330ecc\n"
    },
    {
      "commit": "4161f94990ee146fd78789b96a3a4729a584a5d4",
      "tree": "f6cf2ef887b6c9a8aa1729039d738ccaf2a4032e",
      "parents": [
        "28ea10317799d065fa511036220fb33d20fc7d5e"
      ],
      "author": {
        "name": "Kaushik Lingarkar",
        "email": "kaushikl@qti.qualcomm.com",
        "time": "Fri May 30 14:56:25 2025 -0700"
      },
      "committer": {
        "name": "Kaushik Lingarkar",
        "email": "kaushikl@qti.qualcomm.com",
        "time": "Mon Jun 02 11:08:09 2025 -0700"
      },
      "message": "Add a remote config to exclude replicating desired refs\n\nAllow admins to exclude specific refs from replication when they are\nnot needed on replicas. For instance, refs created by certain plugins\ncan now be skipped. This config helps improve replication performance\nand stability, especially on busy servers that create a large number\nof such unwanted refs.\n\nChange-Id: I42366148cfb1f9da35904ad8e07d3fc103966a53\n"
    },
    {
      "commit": "28ea10317799d065fa511036220fb33d20fc7d5e",
      "tree": "3cacab670de3dc773be5a56cc0c9d97620049a53",
      "parents": [
        "9a750e2384ccb8975560b4ca76b9b92c535362af"
      ],
      "author": {
        "name": "Matthias Sohn",
        "email": "matthias.sohn@sap.com",
        "time": "Fri Mar 07 02:10:16 2025 +0100"
      },
      "committer": {
        "name": "Nasser Grainawi",
        "email": "nasserg@qti.qualcomm.com",
        "time": "Fri May 30 15:54:09 2025 -0700"
      },
      "message": "Auto-format source code using gjf\n\nChange-Id: I73004740248ec95eaa02a7dbe1a483c5f2abc528\n(cherry picked from commit 729e562946ae7357774c9e8572ef9052c15a6e9e)\n"
    },
    {
      "commit": "9522db215bbdd9cb23220da7d4402ffe027c008b",
      "tree": "c15afbaa8524f46ffbc7de3b50c37c5dbbcf1edd",
      "parents": [
        "ede3010e0455b7ce68da62695a5eeb76e7cff420"
      ],
      "author": {
        "name": "Thomas Dräbing",
        "email": "thomas.draebing@sap.com",
        "time": "Fri Feb 07 15:55:07 2025 +0100"
      },
      "committer": {
        "name": "Matthias Sohn",
        "email": "matthias.sohn@sap.com",
        "time": "Sat Mar 22 23:05:23 2025 +0100"
      },
      "message": "Add support for JSON-formatted replication_log\n\nChange-Id: Ifaa2c4b331b2898ae780eec041d34dc4f44d34b1\n"
    },
    {
      "commit": "ede3010e0455b7ce68da62695a5eeb76e7cff420",
      "tree": "2b1b58ecc466b20366dadf18c576f3331c9ed77c",
      "parents": [
        "729e562946ae7357774c9e8572ef9052c15a6e9e"
      ],
      "author": {
        "name": "Matthias Sohn",
        "email": "matthias.sohn@sap.com",
        "time": "Wed Mar 12 14:18:38 2025 +0100"
      },
      "committer": {
        "name": "Matthias Sohn",
        "email": "matthias.sohn@sap.com",
        "time": "Wed Mar 12 14:20:22 2025 +0100"
      },
      "message": "Ensure ExecutorService used in test is shutdown\n\nChange-Id: Ic757b4cc1edb5c02b4be8c9ef7abfaaaf63ac410\n"
    },
    {
      "commit": "729e562946ae7357774c9e8572ef9052c15a6e9e",
      "tree": "7a480134f658c3901c76423e35ecff3401a79173",
      "parents": [
        "a9f44a14c64efaa44afdf8f553a3b3211ef4342b"
      ],
      "author": {
        "name": "Matthias Sohn",
        "email": "matthias.sohn@sap.com",
        "time": "Fri Mar 07 02:10:16 2025 +0100"
      },
      "committer": {
        "name": "Matthias Sohn",
        "email": "matthias.sohn@sap.com",
        "time": "Fri Mar 07 02:10:16 2025 +0100"
      },
      "message": "Auto-format source code using gjf\n\nChange-Id: I73004740248ec95eaa02a7dbe1a483c5f2abc528\n"
    },
    {
      "commit": "a9f44a14c64efaa44afdf8f553a3b3211ef4342b",
      "tree": "6b9f41e09b8769c3415c63ff7d652cd39db27767",
      "parents": [
        "62218e2cd25b7c0c2e4086109770765eb7d093bf"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Mon Feb 10 21:42:47 2025 +0000"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Mon Feb 10 23:25:59 2025 +0000"
      },
      "message": "Use try-with-resource to close Repository in PushOne\n\nPushOne used an instance variable to store and share\nthe Repository object across the intermediate invocations\nof its internal push phases.\n\nUsing an instance variable for Repository is causing\nflakiness as it may not be properly cleaned up as also\nshown in the tests executions with reference counting\nenforced checks introduced with I303706438.\n\nChange-Id: Ibb2fe90ae714e0be3a4f272fa992a555e2bf145f\n"
    },
    {
      "commit": "62218e2cd25b7c0c2e4086109770765eb7d093bf",
      "tree": "42a036271f9527090cbbef36c6aa39306e45d65b",
      "parents": [
        "1138cb1645f68aa04cc4c067e204616a416fb650"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Mon Feb 03 20:46:03 2025 +0000"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Mon Feb 03 20:46:30 2025 +0000"
      },
      "message": "Remove duplicate sitePaths already injected in AbstractDaemonTest\n\nRelease-Notes: skip\nChange-Id: I027dc29e1836aca39b5808c7dc3234b4f4ce1a78\n"
    },
    {
      "commit": "1138cb1645f68aa04cc4c067e204616a416fb650",
      "tree": "4039f6fd03674e17e712826a416da0e863b9b0c0",
      "parents": [
        "0af31d2a5df62329162a750beec2e9dc0adf8e72"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Sun Feb 02 15:11:25 2025 +0000"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Sun Feb 02 15:13:44 2025 +0000"
      },
      "message": "Avoid casting RepositoryManager to LocalDiskRepositoryManager\n\nInject directly the expected class of LocalDiskRepositoryManager\nwithout assuming that the one provided by the AbstractDaemonTest.\n\nThe Change I8d288fa3 will replace the repository manager\nwith a wrapping repository tracking system, therefore the casting\nmay not be satisfied anymore and it may fail at runtime.\n\nRelease-Notes: skip\nChange-Id: I8e410ae6a489b1127ef09d4c203ab9aa1d711d8d\n"
    },
    {
      "commit": "0af31d2a5df62329162a750beec2e9dc0adf8e72",
      "tree": "306c36bf6a5e45b115762fcefbac32c293df799f",
      "parents": [
        "90c64204e1e27ec245f41b3d08b10f431dd72faf"
      ],
      "author": {
        "name": "Alvaro Vilaplana Garcia",
        "email": "alvaro.vilaplana@gmail.com",
        "time": "Wed Nov 13 15:35:29 2024 +0000"
      },
      "committer": {
        "name": "Alvaro Vilaplana Garcia",
        "email": "alvaro.vilaplana@gmail.com",
        "time": "Thu Nov 14 16:23:36 2024 +0000"
      },
      "message": "Fix PushOne toString() to show the right refs and hidden number\n\nInclude all refs without applying any limit when `maxRefsToShow`\nis set to 0 and limit the displayed refs to the specified maximum\nwhen `maxRefsToShow` is greater than 0. Previously, the method\nalways limited the number of refs to `maxRefsToShow`, and\n`maxRefsToShow` was updated to the batch count when initially\nset to 0.\n\nRemove the calculation of hidden refs to avoid performance\nissues with millions of refs.\n\nBug: Issue 375798125\nRelease-Notes: skip\nChange-Id: I30aae0cebf4fe28309542808baeeb316e1e7fb52\n"
    },
    {
      "commit": "90c64204e1e27ec245f41b3d08b10f431dd72faf",
      "tree": "a4176bace562960fa83155feb324f97cc2a05e12",
      "parents": [
        "60693efae661105fb6278c16cae9502d29bfa7f3",
        "9a750e2384ccb8975560b4ca76b9b92c535362af"
      ],
      "author": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Fri Oct 11 23:06:55 2024 -0700"
      },
      "committer": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Fri Oct 11 23:10:11 2024 -0700"
      },
      "message": "Merge branch \u0027stable-3.10\u0027\n\n* stable-3.10:\n  synchronizePendingEvents: Fix stuck replaying flag\n\nRelease-Notes: skip\nChange-Id: If0a080b81639d072cf20828e4cd6c9cbce010a8d\n"
    },
    {
      "commit": "9a750e2384ccb8975560b4ca76b9b92c535362af",
      "tree": "443a68b5fd341efe98be0d22a95df555841d3d46",
      "parents": [
        "445cd037108a9ad478c187aeb31cb14a8e889a0f",
        "5cfb07aa0e1e133a066ae81951b8e156d7dfd4e4"
      ],
      "author": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Fri Oct 11 23:00:23 2024 -0700"
      },
      "committer": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Fri Oct 11 23:01:40 2024 -0700"
      },
      "message": "Merge branch \u0027stable-3.8\u0027 into stable-3.10\n\n* stable-3.8:\n  synchronizePendingEvents: Fix stuck replaying flag\n\nRelease-Notes: skip\nChange-Id: I277e4bbfefee4895615976d1b26440d55a1eef58\n"
    },
    {
      "commit": "5cfb07aa0e1e133a066ae81951b8e156d7dfd4e4",
      "tree": "8734ccd523c5d66708f33181cd07531fb676ed67",
      "parents": [
        "584aea1a8968a9162d78460422a285a87910f502"
      ],
      "author": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Wed Oct 09 14:31:15 2024 -0700"
      },
      "committer": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Wed Oct 09 14:31:15 2024 -0700"
      },
      "message": "synchronizePendingEvents: Fix stuck replaying flag\n\nSimilar to change I4628a574, the replaying flag has a chance to become\nstuck as true if an exception is thrown while accumulating prunable\nreplication tasks, which happens before the earlier fix is relevant.\nCatch and log the exception and continue since synchronizing with a\nsubset of prunable tasks isn\u0027t much of a problem.\n\nChange-Id: I935c3252e8c3da63ac248b051380558b749ea76e\nRelease-Notes: Fixed chance for exceptions to block future distributor runs\n"
    },
    {
      "commit": "60693efae661105fb6278c16cae9502d29bfa7f3",
      "tree": "e8c730ff6f8244fc7d23edeec9ba307b5019c99b",
      "parents": [
        "660fcd4caca607625728f7d2066478f7ce0c1468"
      ],
      "author": {
        "name": "Matthias Sohn",
        "email": "matthias.sohn@sap.com",
        "time": "Tue Apr 02 01:06:18 2024 +0200"
      },
      "committer": {
        "name": "Matthias Sohn",
        "email": "matthias.sohn@sap.com",
        "time": "Mon Sep 16 13:23:57 2024 +0200"
      },
      "message": "Destination: extract StateLock class to simplify lock handling\n\nChange-Id: If46d2b1ece73e32a7463c02988ccd1d4e2f769aa\n"
    },
    {
      "commit": "660fcd4caca607625728f7d2066478f7ce0c1468",
      "tree": "e9a9056cf9625d411d43c98874aac6bc34aaf908",
      "parents": [
        "cf0aea56d2efad79bcb84df37edc32c01bf8afb7"
      ],
      "author": {
        "name": "Matthias Sohn",
        "email": "matthias.sohn@sap.com",
        "time": "Sun Mar 31 00:41:53 2024 +0100"
      },
      "committer": {
        "name": "Matthias Sohn",
        "email": "matthias.sohn@sap.com",
        "time": "Mon Sep 16 13:18:50 2024 +0200"
      },
      "message": "Destination: use striped lock to reduce lock contention\n\nDestination used a single lock to serialize pushes to the same\ndestination. When many repositories are replicated via one\ndestination this may cause lock contention and unnecessarily\nthrottle replication throughput.\n\nHence use a striped lock with up to 16 stripes to reduce lock\ncontention but still serialize replication per repository.\nDon\u0027t use more stripes than the number of projects to be\nreplicated if the projects are configured explicitly.\n\nAvoid copying the pending and in-flight maps of PushOnes to be\nexecuted for reading them. Instead directly access the maps held\nby a destination and use thread-safe ConcurrentHashMaps for both.\n\nChange-Id: I0ad2f51249ffba429fc61b422056fa85d88b1de4\n"
    },
    {
      "commit": "cf0aea56d2efad79bcb84df37edc32c01bf8afb7",
      "tree": "2b6fc3a74a6614534f1f14c3525a4dbd59b47f45",
      "parents": [
        "3982574d28f254b525b093c2ecae8caa47825910",
        "445cd037108a9ad478c187aeb31cb14a8e889a0f"
      ],
      "author": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Mon Jul 29 17:25:15 2024 -0600"
      },
      "committer": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Mon Jul 29 17:25:15 2024 -0600"
      },
      "message": "Merge branch \u0027stable-3.10\u0027\n\n* stable-3.10:\n  PushOne: Remove unused addRef() method\n  TasksStorage: Remove synchronized from methods\n  Place the replaying flag clearing in a finally\n  Demote delete errors when distributor is enabled\n  distributor: Reduce log level for no-op consolidations\n\nChange-Id: I3ce1519c44ec6cbae7725527ea626e55401cc488\nRelease-Notes: skip\n"
    },
    {
      "commit": "445cd037108a9ad478c187aeb31cb14a8e889a0f",
      "tree": "abbfbbff20f2382c23ded30586e9a18fd3951290",
      "parents": [
        "56b8ffbab5bf619c0b6b5d44f0255fd41b9e1c89",
        "584aea1a8968a9162d78460422a285a87910f502"
      ],
      "author": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Mon Jul 29 16:58:06 2024 -0600"
      },
      "committer": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Mon Jul 29 16:58:06 2024 -0600"
      },
      "message": "Merge branch \u0027stable-3.8\u0027 into stable-3.10\n\n* stable-3.8:\n  PushOne: Remove unused addRef() method\n  TasksStorage: Remove synchronized from methods\n  Place the replaying flag clearing in a finally\n  Demote delete errors when distributor is enabled\n  distributor: Reduce log level for no-op consolidations\n\nChange-Id: I15eb65d0f379a6e562d3c86e1089cfb7d9e6752c\nRelease-Notes: skip\n"
    },
    {
      "commit": "584aea1a8968a9162d78460422a285a87910f502",
      "tree": "c29d833faa4fec69e9040218bde42f4e213381ca",
      "parents": [
        "0ef3c3437366b126c5e04f76a4fc47932bf4e8ca",
        "694b34084ca623756a9ec7e6a60631ab6027d5c2"
      ],
      "author": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Mon Jul 29 14:24:41 2024 -0600"
      },
      "committer": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Mon Jul 29 14:24:41 2024 -0600"
      },
      "message": "Merge branch \u0027stable-3.5\u0027 into stable-3.8\n\n* stable-3.5:\n  TasksStorage: Remove synchronized from methods\n  Place the replaying flag clearing in a finally\n  Demote delete errors when distributor is enabled\n  distributor: Reduce log level for no-op consolidations\n\nChange-Id: Iaa6d4a59d5cb26879500f2d36852eaa30efc5d7b\nRelease-Notes: skip\n"
    },
    {
      "commit": "0ef3c3437366b126c5e04f76a4fc47932bf4e8ca",
      "tree": "56372d895f4e220236d089f69918d5e199ca1aa3",
      "parents": [
        "8fd3c271ce0a21480e3d04da5ad2112efea3bedf"
      ],
      "author": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Mon Jul 29 13:54:55 2024 -0600"
      },
      "committer": {
        "name": "Nasser Grainawi",
        "email": "nasser.grainawi@linaro.org",
        "time": "Mon Jul 29 13:54:55 2024 -0600"
      },
      "message": "PushOne: Remove unused addRef() method\n\nThis is only used by tests and should have been removed when\naddRefBatch() superseded it.\n\nChange-Id: If9de1df29fd56a595fa4d4b2b4f5716264a67e11\nRelease-Notes: skip\n"
    },
    {
      "commit": "3982574d28f254b525b093c2ecae8caa47825910",
      "tree": "1f0e421b4045efc95c764d5eb2c9f3d39668bc8a",
      "parents": [
        "eac5d4e844c6103b37e8d35ee1ef3eb5ddf117ea"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Tue Jul 09 19:46:42 2024 +0100"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Tue Jul 09 19:52:12 2024 +0000"
      },
      "message": "Fix FanoutConfigurationResource update API\n\nWhen updating the FanoutConfigurationResource, the remote subsection\nneeds to be removed as the subsection name is part of the config\nfilename.\n\nBug: Issue 351843807\nChange-Id: Ib434a434a82de7e237387c7d7df8eb0e78cfa5a6\n"
    },
    {
      "commit": "eac5d4e844c6103b37e8d35ee1ef3eb5ddf117ea",
      "tree": "aea0930aa001d0b1cfe12fcc52901f8fef688b26",
      "parents": [
        "69c66c8919533635bb7ef36cdd660f48f6647c09"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Sat Jul 06 00:57:20 2024 +0100"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Tue Jul 09 19:51:55 2024 +0000"
      },
      "message": "Delay the fetching of credentials until push time\n\nThe replication plugin can be configured to reload dynamically\nthe credentials when changed on the filesystem or in the SecureStore\nbackend.\n\nThe overall reloading mechanism worked as long as a new PushOne\nobject was created per replication task; however, when a push was\nfailing because of credentials issues, the PushOne object was\nrequeued without being recreated, causing a chicken \u0026 egg situation\n\nUntil the PushOne was retried, the old credentials were stored\nin the instance and therefore any retry would have failed.\nEven though the replication.config was configured for reloading\ndynamically the credentials, any task stuck in retry would have\nnot been able to pass the correct credentials.\n\nDelay the creation of the credentials provider until push\ntime, so that even in case of retrying replication tasks\nthe credentials can be dynamically reloaded when changed\non the filesystem.\n\nChange-Id: I2f069a8433651a0db2681b6675f392ff894f27fc\n"
    },
    {
      "commit": "69c66c8919533635bb7ef36cdd660f48f6647c09",
      "tree": "dfc7de5dfa2228756d2740d186e7dc3d82369829",
      "parents": [
        "69c0c4dd67a942f42cb36ed3f89e4257d7de575e"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Fri Jul 05 22:08:04 2024 +0100"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Tue Jul 09 20:46:09 2024 +0100"
      },
      "message": "Improve FanoutConfigurationResource loading performance\n\nWhen using replication remotes as individual files under\n$GERRIT_SITE/etc/replication the time requested to load the remotes,\nparsing them and merging them together can be very significant,\nespecially when having a lot of remote configurations.\n\nOn GerritHub.io with 12k remotes configured, the time to load,\nparse and merge them in a unique FileBasedConfig object reaches\nseveral minutes.\n\nThe main issue found during profiling is the manual merging of\nConfig through one call per setting using Config.setStringList()\nrather than merging them first and then parsing them as a whole.\n\nRefactor the overall parsing logic by splitting the process in\nthree parts:\n- load of all replication remotes a list of String\n- expanding them as individual strings and adding the remote names\n- merging them together with the base config using the JGit\u0027s\n  default mechanism\n\nFrom an external perspective, the API work exactly as before, including\nthe config validation process; from a performance perspective, the\nloading of 12k remotes is reduced by 20x times, taking under 1 minute.\n\nChange-Id: I2880428b881034e6253d07ab0ba368c0a97eecc0\n"
    },
    {
      "commit": "69c0c4dd67a942f42cb36ed3f89e4257d7de575e",
      "tree": "f23ccc62a8be4d77e7fe11d54649d5a18d3a9fcf",
      "parents": [
        "05c096ed827467645776abcec6b39523b4e76843"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Fri Jul 05 22:01:34 2024 +0100"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Mon Jul 08 22:10:16 2024 +0100"
      },
      "message": "FanoutConfigResourceTest: assert configuration updates being visible\n\nWhen updating a remote configuration using the FanoutConfigResource,\nthe updated values should be also visible in-memory and not simply\nstored on the underlying filesystem.\n\nFix an issue where the updates to the remotes performed using\n$GERRIT_SITE/etc/replication/remote.config fan-out config scheme\nwas persisted on disk but left a stale in-memory copy.\n\nChange-Id: I2360ba447ffbc3695faad72e5674ca883f75081a\n"
    },
    {
      "commit": "05c096ed827467645776abcec6b39523b4e76843",
      "tree": "54ac17bdcfc08a6f48664f95042aa3573b3724e5",
      "parents": [
        "2e2a6415a979e74e454a11e6e1637cb0d9d7a20a"
      ],
      "author": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Thu Jun 27 23:35:36 2024 +0100"
      },
      "committer": {
        "name": "Luca Milanesio",
        "email": "luca.milanesio@gmail.com",
        "time": "Fri Jun 28 22:02:53 2024 +0000"
      },
      "message": "Move ReplicationQueue and associated bindings to ReplicationModule\n\nDuring the refactoring in I8003ec6c827f the ReplicationQueue and its\nassociated bindings were moved to the ReplicationConfigModule for\nallowing the users of the replication APIs to be able to have the\nfull bindings of all the internal replication plugin mechanisms.\n\nHowever, the change was reverted in I461cd1bef because of the\nbreakages generated and later a new approch has been introduced\nwith I349461e29d where the APIs are exposed via DynamicItem\u003c\u003e\nand the caller isn\u0027t required anymore to have the full Guice\nbindings to re-create the replication objects.\n\nAs a result, the bindings of the replication plugin internals\nremained in the ReplicationConfigModule with nasty consequences\nof other plugins (e.g. pull-replication) to inadvertently generate\njust-in-time bindings of the replication plugin classes.\n\nWhen using the pull-replication and replication plugins together\nthe just-in-time bindings would then eventually conflict\nwith the explicit bindings done in the replication plugin.\n\nExample:\n\n[2024-06-27T22:23:19.471+01:00] [main] WARN  com.google.gerrit.server.plugins.PluginLoader : Cannot load plugin replication\ncom.google.inject.CreationException: Unable to create injector, see the following errors:\n\n1) [Guice/JitBindingAlreadySet]: A just-in-time binding to DynamicSet\u003cReplicationStateListener\u003e was already configured on a parent injector.\n  at DynamicSet.setOf(DynamicSet.java:88)\n\n1 error\nDynamicSet:               \"com.google.gerrit.extensions.registration.DynamicSet\"\nReplicationStateListener: \"com.googlesource.gerrit.plugins.replication.ReplicationStateListener\"\n\nBy moving back the replication plugin internal classes\nbindings into its proper ReplicationModule the problem is fully\nresolved and there aren\u0027t just-in-time bindings created\nimplicitly anymore.\n\nChange-Id: I2bf9dab0d2555310b4d1b0c66528b2aaa2cc5106\n"
    },
    {
      "commit": "2e2a6415a979e74e454a11e6e1637cb0d9d7a20a",
      "tree": "ca531c7bd62fd3ae07c16789b6d1f98140dec70e",
      "parents": [
        "b4bf5d2057cb748a7e50acc6c83727b8d64105e1"
      ],
      "author": {
        "name": "Jacek Centkowski",
        "email": "geminica.programs@gmail.com",
        "time": "Tue Jun 25 13:40:10 2024 +0200"
      },
      "committer": {
        "name": "Jacek Centkowski",
        "email": "geminica.programs@gmail.com",
        "time": "Tue Jun 25 13:40:10 2024 +0200"
      },
      "message": "Follow-up to \u0027Revert \"Reduce visibility of *CredentialsFact...\u0027\n\nMark these public factories with `UsedAt` so there it is no longer\ntempting to package-protect them.\n\nChange-Id: Icd4bec980472851dfb769159c1abb528e353cbd9\n"
    }
  ],
  "next": "b4bf5d2057cb748a7e50acc6c83727b8d64105e1"
}
