LCOV - code coverage report
Current view: top level - server/notedb - DeleteChangeMessageRewriter.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 41 41 100.0 %
Date: 2022-11-19 15:00:39 Functions: 5 5 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2018 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.server.notedb;
      16             : 
      17             : import static com.google.common.base.Preconditions.checkArgument;
      18             : import static com.google.common.base.Preconditions.checkState;
      19             : import static com.google.gerrit.server.notedb.ChangeNoteUtil.parseCommitMessageRange;
      20             : import static java.util.Objects.requireNonNull;
      21             : import static org.eclipse.jgit.util.RawParseUtils.decode;
      22             : 
      23             : import com.google.gerrit.entities.Change;
      24             : import com.google.gerrit.entities.RefNames;
      25             : import java.io.IOException;
      26             : import java.nio.charset.Charset;
      27             : import java.util.Optional;
      28             : import org.eclipse.jgit.lib.CommitBuilder;
      29             : import org.eclipse.jgit.lib.ObjectId;
      30             : import org.eclipse.jgit.lib.ObjectInserter;
      31             : import org.eclipse.jgit.revwalk.RevCommit;
      32             : import org.eclipse.jgit.revwalk.RevSort;
      33             : import org.eclipse.jgit.revwalk.RevWalk;
      34             : import org.eclipse.jgit.util.RawParseUtils;
      35             : 
      36             : /**
      37             :  * Deletes a change message from NoteDb by rewriting the commit history. After deletion, the whole
      38             :  * change message will be replaced by a new message indicating the original change message has been
      39             :  * deleted for the given reason.
      40             :  */
      41             : public class DeleteChangeMessageRewriter implements NoteDbRewriter {
      42             : 
      43             :   private final Change.Id changeId;
      44             :   private final String targetMessageId;
      45             :   private final String newChangeMessage;
      46             : 
      47           1 :   DeleteChangeMessageRewriter(Change.Id changeId, String targetMessageId, String newChangeMessage) {
      48           1 :     this.changeId = changeId;
      49           1 :     this.targetMessageId = requireNonNull(targetMessageId);
      50           1 :     this.newChangeMessage = newChangeMessage;
      51           1 :   }
      52             : 
      53             :   @Override
      54             :   public String getRefName() {
      55           1 :     return RefNames.changeMetaRef(changeId);
      56             :   }
      57             : 
      58             :   @Override
      59             :   public ObjectId rewriteCommitHistory(RevWalk revWalk, ObjectInserter inserter, ObjectId currTip)
      60             :       throws IOException {
      61           1 :     checkArgument(!currTip.equals(ObjectId.zeroId()));
      62             : 
      63             :     // Walk from the first commit of the branch.
      64           1 :     revWalk.reset();
      65           1 :     revWalk.markStart(revWalk.parseCommit(currTip));
      66           1 :     revWalk.sort(RevSort.TOPO);
      67           1 :     revWalk.sort(RevSort.REVERSE);
      68             : 
      69           1 :     ObjectId newTipId = null;
      70             :     RevCommit originalCommit;
      71           1 :     boolean startRewrite = false;
      72           1 :     while ((originalCommit = revWalk.next()) != null) {
      73           1 :       boolean isTargetCommit = originalCommit.getId().getName().equals(targetMessageId);
      74           1 :       if (!startRewrite && !isTargetCommit) {
      75           1 :         newTipId = originalCommit;
      76           1 :         continue;
      77             :       }
      78             : 
      79           1 :       startRewrite = true;
      80             :       String newCommitMessage =
      81           1 :           isTargetCommit ? createNewCommitMessage(originalCommit) : originalCommit.getFullMessage();
      82           1 :       newTipId = rewriteOneCommit(originalCommit, newTipId, newCommitMessage, inserter);
      83           1 :     }
      84           1 :     return newTipId;
      85             :   }
      86             : 
      87             :   private String createNewCommitMessage(RevCommit commit) {
      88           1 :     byte[] raw = commit.getRawBuffer();
      89             : 
      90           1 :     Optional<ChangeNoteUtil.CommitMessageRange> range = parseCommitMessageRange(commit);
      91           1 :     checkState(
      92           1 :         range.isPresent() && range.get().hasChangeMessage(), "failed to parse commit message");
      93             : 
      94             :     // Only replace the commit message body, which is the user-provided message. The subject and
      95             :     // footers are NoteDb metadata.
      96           1 :     Charset encoding = RawParseUtils.parseEncoding(raw);
      97           1 :     String prefix =
      98           1 :         decode(encoding, raw, range.get().subjectStart(), range.get().changeMessageStart());
      99           1 :     String postfix = decode(encoding, raw, range.get().changeMessageEnd() + 1, raw.length);
     100           1 :     return prefix + newChangeMessage + postfix;
     101             :   }
     102             : 
     103             :   /**
     104             :    * Rewrites one commit.
     105             :    *
     106             :    * @param originalCommit the original commit to be rewritten.
     107             :    * @param parentCommitId the parent of the new commit. For the first rewritten commit, it's the
     108             :    *     parent of 'originalCommit'. For the latter rewritten commits, it's the commit rewritten
     109             :    *     just before it.
     110             :    * @param commitMessage the full commit message of the new commit.
     111             :    * @param inserter the {@code ObjectInserter} for the rewrite process.
     112             :    * @return the {@code objectId} of the new commit.
     113             :    */
     114             :   private ObjectId rewriteOneCommit(
     115             :       RevCommit originalCommit,
     116             :       ObjectId parentCommitId,
     117             :       String commitMessage,
     118             :       ObjectInserter inserter)
     119             :       throws IOException {
     120           1 :     CommitBuilder cb = new CommitBuilder();
     121           1 :     if (parentCommitId != null) {
     122           1 :       cb.setParentId(parentCommitId);
     123             :     }
     124           1 :     cb.setTreeId(originalCommit.getTree());
     125           1 :     cb.setMessage(commitMessage);
     126           1 :     cb.setCommitter(originalCommit.getCommitterIdent());
     127           1 :     cb.setAuthor(originalCommit.getAuthorIdent());
     128           1 :     cb.setEncoding(originalCommit.getEncoding());
     129           1 :     return inserter.insert(cb);
     130             :   }
     131             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750