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 <edwin.kempin@sap.com>
This commit is contained in:
		| @@ -20,6 +20,7 @@ import com.google.common.base.MoreObjects; | |||||||
| import com.google.gerrit.extensions.events.LifecycleListener; | import com.google.gerrit.extensions.events.LifecycleListener; | ||||||
| import com.google.gerrit.lifecycle.LifecycleModule; | import com.google.gerrit.lifecycle.LifecycleModule; | ||||||
| import com.google.gerrit.reviewdb.client.Project; | 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.GerritServerConfig; | ||||||
| import com.google.gerrit.server.config.SitePaths; | import com.google.gerrit.server.config.SitePaths; | ||||||
| import com.google.gerrit.server.notedb.NotesMigration; | import com.google.gerrit.server.notedb.NotesMigration; | ||||||
| @@ -246,6 +247,18 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager { | |||||||
|         null, ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true); |         null, ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true); | ||||||
|       config.save(); |       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); |       onCreateProject(name); | ||||||
|  |  | ||||||
|       return db; |       return db; | ||||||
|   | |||||||
| @@ -45,7 +45,9 @@ import org.eclipse.jgit.transport.ReceiveCommand; | |||||||
| import org.eclipse.jgit.treewalk.TreeWalk; | import org.eclipse.jgit.treewalk.TreeWalk; | ||||||
| import org.eclipse.jgit.util.RawParseUtils; | import org.eclipse.jgit.util.RawParseUtils; | ||||||
|  |  | ||||||
|  | import java.io.BufferedReader; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  | import java.io.StringReader; | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -342,7 +344,12 @@ public abstract class VersionedMetaData { | |||||||
|         RefUpdate ru = db.updateRef(refName); |         RefUpdate ru = db.updateRef(refName); | ||||||
|         ru.setExpectedOldObjectId(oldId); |         ru.setExpectedOldObjectId(oldId); | ||||||
|         ru.setNewObjectId(src); |         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(); |         inserter.flush(); | ||||||
|         RefUpdate.Result result = ru.update(); |         RefUpdate.Result result = ru.update(); | ||||||
|         switch (result) { |         switch (result) { | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ import java.util.List; | |||||||
| /** A version of the database schema. */ | /** A version of the database schema. */ | ||||||
| public abstract class SchemaVersion { | public abstract class SchemaVersion { | ||||||
|   /** The current schema version. */ |   /** The current schema version. */ | ||||||
|   public static final Class<Schema_105> C = Schema_105.class; |   public static final Class<Schema_106> C = Schema_106.class; | ||||||
|  |  | ||||||
|   public static int getBinaryVersion() { |   public static int getBinaryVersion() { | ||||||
|     return guessVersion(C); |     return guessVersion(C); | ||||||
|   | |||||||
| @@ -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<Schema_105> 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<Project.NameKey> 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"); | ||||||
|  |   } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 Edwin Kempin
					Edwin Kempin