From 5b5cad6522f848d481795be7145a6331a038bf10 Mon Sep 17 00:00:00 2001 From: Edwin Kempin Date: Tue, 13 Jan 2015 15:53:46 +0100 Subject: [PATCH] Write reflog for refs/meta/config changes A reflog is currently only written for branches under refs/heads/. The refs/meta/config branch has no reflog, but it contains important information such as access rights, and hence it should have a reflog as well. A problem with getting a reflog for refs/meta/config is that JGit (and also native Git) is not creating a reflog for this branch even if core.logAllRefUpdates is set (see section 'core.logAllRefUpdates' in [1]). To make JGit write the reflog for the refs/meta/config branch the reflog file for this branch must already exist. This is why the LocalDiskRepositoryManager now creates an empty reflog file for the refs/meta/config branch when a new repository is created. In addition this change contains a schema migration that creates the reflog file for the refs/meta/config branch in all existing repositories, so that further updates get a reflog entry. This migration is only possible if the repositories are stored on local disk and is not executed if a GitRepositoryManager other than LocalDiskRepositoryManager is used. [1] https://www.kernel.org/pub/software/scm/git/docs/git-config.html#_variables Change-Id: I9b6354cfa51263db10fb99e329b397b86f40156d Signed-off-by: Edwin Kempin --- .../git/LocalDiskRepositoryManager.java | 13 +++ .../gerrit/server/git/VersionedMetaData.java | 9 +- .../gerrit/server/schema/SchemaVersion.java | 2 +- .../gerrit/server/schema/Schema_106.java | 104 ++++++++++++++++++ 4 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_106.java diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java index f5ec96280f..633c3bb8d6 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java @@ -20,6 +20,7 @@ import com.google.common.base.MoreObjects; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.server.notedb.NotesMigration; @@ -246,6 +247,18 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager { null, ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true); config.save(); + // JGit only writes to the reflog for refs/meta/config if the log file + // already exists. + // + File metaConfigLog = + new File(db.getDirectory(), "logs/" + RefNames.REFS_CONFIG); + if (!metaConfigLog.getParentFile().mkdirs() + || !metaConfigLog.createNewFile()) { + log.error(String.format( + "Failed to create ref log for %s in repository %s", + RefNames.REFS_CONFIG, name)); + } + onCreateProject(name); return db; diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java index 09846d6305..7f07e232b2 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java @@ -45,7 +45,9 @@ import org.eclipse.jgit.transport.ReceiveCommand; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.util.RawParseUtils; +import java.io.BufferedReader; import java.io.IOException; +import java.io.StringReader; import java.util.Objects; /** @@ -342,7 +344,12 @@ public abstract class VersionedMetaData { RefUpdate ru = db.updateRef(refName); ru.setExpectedOldObjectId(oldId); ru.setNewObjectId(src); - ru.disableRefLog(); + ru.setRefLogIdent(update.getCommitBuilder().getAuthor()); + try (BufferedReader reader = new BufferedReader( + new StringReader(update.getCommitBuilder().getMessage()))) { + // read the subject line and use it as reflog message + ru.setRefLogMessage("commit: " + reader.readLine(), true); + } inserter.flush(); RefUpdate.Result result = ru.update(); switch (result) { diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java index ba2350fc48..50b3d88cc8 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java @@ -32,7 +32,7 @@ import java.util.List; /** A version of the database schema. */ public abstract class SchemaVersion { /** The current schema version. */ - public static final Class C = Schema_105.class; + public static final Class C = Schema_106.class; public static int getBinaryVersion() { return guessVersion(C); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_106.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_106.java new file mode 100644 index 0000000000..ef7e291b8d --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_106.java @@ -0,0 +1,104 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.schema; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.reviewdb.client.RefNames; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.GerritPersonIdent; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.LocalDiskRepositoryManager; +import com.google.gwtorm.server.OrmException; +import com.google.inject.Inject; +import com.google.inject.Provider; + +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Repository; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.SortedSet; + +public class Schema_106 extends SchemaVersion { + private final GitRepositoryManager repoManager; + private final PersonIdent serverUser; + + @Inject + Schema_106(Provider prior, + GitRepositoryManager repoManager, + @GerritPersonIdent PersonIdent serverUser) { + super(prior); + this.repoManager = repoManager; + this.serverUser = serverUser; + } + + @Override + protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException { + if (!(repoManager instanceof LocalDiskRepositoryManager)) { + return; + } + + ui.message("listing all repositories ..."); + SortedSet repoList = repoManager.list(); + ui.message("done"); + + ui.message(String.format("creating reflog files for %s branches ...", + RefNames.REFS_CONFIG)); + for (Project.NameKey project : repoList) { + try { + Repository repo = repoManager.openRepository(project); + try { + File metaConfigLog = + new File(repo.getDirectory(), "logs/" + RefNames.REFS_CONFIG); + if (metaConfigLog.exists()) { + continue; + } + + if (!metaConfigLog.getParentFile().mkdirs() + || !metaConfigLog.createNewFile()) { + throw new IOException(String.format( + "Failed to create reflog for %s in repository %s", + RefNames.REFS_CONFIG, project)); + } + + ObjectId metaConfigId = repo.resolve(RefNames.REFS_CONFIG); + if (metaConfigId != null) { + try (PrintWriter writer = + new PrintWriter(metaConfigLog, UTF_8.name())) { + writer.print(ObjectId.zeroId().name()); + writer.print(" "); + writer.print(metaConfigId.name()); + writer.print(" "); + writer.print(serverUser.toExternalString()); + writer.print("\t"); + writer.print("create reflog"); + writer.println(); + } + } + } finally { + repo.close(); + } + } catch (IOException e) { + ui.message(String.format("ERROR: Failed to create reflog file for the" + + " %s branch in repository %s", RefNames.REFS_CONFIG, project.get())); + } + } + ui.message("done"); + } +}