LCOV - code coverage report
Current view: top level - server/git - NotesBranchUtil.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 58 74 78.4 %
Date: 2022-11-19 15:00:39 Functions: 7 10 70.0 %

          Line data    Source code
       1             : // Copyright (C) 2012 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.git;
      16             : 
      17             : import static com.google.common.base.MoreObjects.firstNonNull;
      18             : 
      19             : import com.google.gerrit.entities.Project;
      20             : import com.google.gerrit.git.LockFailureException;
      21             : import com.google.gerrit.git.RefUpdateUtil;
      22             : import com.google.gerrit.server.GerritPersonIdent;
      23             : import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
      24             : import com.google.inject.Inject;
      25             : import com.google.inject.assistedinject.Assisted;
      26             : import java.io.IOException;
      27             : import org.eclipse.jgit.lib.BatchRefUpdate;
      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.lib.ObjectReader;
      32             : import org.eclipse.jgit.lib.PersonIdent;
      33             : import org.eclipse.jgit.lib.Ref;
      34             : import org.eclipse.jgit.lib.Repository;
      35             : import org.eclipse.jgit.notes.Note;
      36             : import org.eclipse.jgit.notes.NoteMap;
      37             : import org.eclipse.jgit.notes.NoteMerger;
      38             : import org.eclipse.jgit.revwalk.RevCommit;
      39             : import org.eclipse.jgit.revwalk.RevWalk;
      40             : import org.eclipse.jgit.transport.ReceiveCommand;
      41             : 
      42             : /** A utility class for updating a notes branch with automatic merge of note trees. */
      43             : public class NotesBranchUtil {
      44             :   public interface Factory {
      45             :     NotesBranchUtil create(Project.NameKey project, Repository db, ObjectInserter inserter);
      46             :   }
      47             : 
      48             :   private final PersonIdent gerritIdent;
      49             :   private final GitReferenceUpdated gitRefUpdated;
      50             :   private final Project.NameKey project;
      51             :   private final Repository db;
      52             :   private final ObjectInserter inserter;
      53             : 
      54             :   private RevCommit baseCommit;
      55             :   private NoteMap base;
      56             : 
      57             :   private RevCommit oursCommit;
      58             :   private NoteMap ours;
      59             : 
      60             :   private RevWalk revWalk;
      61             :   private ObjectReader reader;
      62             :   private boolean overwrite;
      63             : 
      64             :   private ReviewNoteMerger noteMerger;
      65             : 
      66             :   @Inject
      67             :   public NotesBranchUtil(
      68             :       @GerritPersonIdent PersonIdent gerritIdent,
      69             :       GitReferenceUpdated gitRefUpdated,
      70             :       @Assisted Project.NameKey project,
      71             :       @Assisted Repository db,
      72           2 :       @Assisted ObjectInserter inserter) {
      73           2 :     this.gerritIdent = gerritIdent;
      74           2 :     this.gitRefUpdated = gitRefUpdated;
      75           2 :     this.project = project;
      76           2 :     this.db = db;
      77           2 :     this.inserter = inserter;
      78           2 :   }
      79             : 
      80             :   /**
      81             :    * Create a new commit in the {@code notesBranch} by updating existing or creating new notes from
      82             :    * the {@code notes} map.
      83             :    *
      84             :    * <p>Does not retry in the case of lock failure; callers may use {@link
      85             :    * com.google.gerrit.server.update.RetryHelper}.
      86             :    *
      87             :    * @param notes map of notes
      88             :    * @param notesBranch notes branch to update
      89             :    * @param commitAuthor author of the commit in the notes branch
      90             :    * @param commitMessage for the commit in the notes branch
      91             :    * @throws LockFailureException if committing the notes failed due to a lock failure on the notes
      92             :    *     branch
      93             :    * @throws IOException if committing the notes failed for any other reason
      94             :    */
      95             :   public final void commitAllNotes(
      96             :       NoteMap notes, String notesBranch, PersonIdent commitAuthor, String commitMessage)
      97             :       throws IOException {
      98           0 :     this.overwrite = true;
      99           0 :     commitNotes(notes, notesBranch, commitAuthor, commitMessage);
     100           0 :   }
     101             : 
     102             :   /**
     103             :    * Create a new commit in the {@code notesBranch} by creating not yet existing notes from the
     104             :    * {@code notes} map. The notes from the {@code notes} map which already exist in the note-tree of
     105             :    * the tip of the {@code notesBranch} will not be updated.
     106             :    *
     107             :    * <p>Does not retry in the case of lock failure; callers may use {@link
     108             :    * com.google.gerrit.server.update.RetryHelper}.
     109             :    *
     110             :    * @param notes map of notes
     111             :    * @param notesBranch notes branch to update
     112             :    * @param commitAuthor author of the commit in the notes branch
     113             :    * @param commitMessage for the commit in the notes branch
     114             :    * @return map with those notes from the {@code notes} that were newly created
     115             :    * @throws LockFailureException if committing the notes failed due to a lock failure on the notes
     116             :    *     branch
     117             :    * @throws IOException if committing the notes failed for any other reason
     118             :    */
     119             :   public final NoteMap commitNewNotes(
     120             :       NoteMap notes, String notesBranch, PersonIdent commitAuthor, String commitMessage)
     121             :       throws IOException {
     122           2 :     this.overwrite = false;
     123           2 :     commitNotes(notes, notesBranch, commitAuthor, commitMessage);
     124           2 :     NoteMap newlyCreated = NoteMap.newEmptyMap();
     125           2 :     for (Note n : notes) {
     126           2 :       if (base == null || !base.contains(n)) {
     127           2 :         newlyCreated.set(n, n.getData());
     128             :       }
     129           2 :     }
     130           2 :     return newlyCreated;
     131             :   }
     132             : 
     133             :   private void commitNotes(
     134             :       NoteMap notes, String notesBranch, PersonIdent commitAuthor, String commitMessage)
     135             :       throws LockFailureException, IOException {
     136             :     try {
     137           2 :       revWalk = new RevWalk(db);
     138           2 :       reader = db.newObjectReader();
     139           2 :       loadBase(notesBranch);
     140           2 :       if (overwrite) {
     141           0 :         addAllNotes(notes);
     142             :       } else {
     143           2 :         addNewNotes(notes);
     144             :       }
     145           2 :       if (base != null) {
     146           1 :         oursCommit = createCommit(ours, commitAuthor, commitMessage, baseCommit);
     147             :       } else {
     148           2 :         oursCommit = createCommit(ours, commitAuthor, commitMessage);
     149             :       }
     150           2 :       updateRef(notesBranch);
     151             :     } finally {
     152           2 :       revWalk.close();
     153           2 :       reader.close();
     154             :     }
     155           2 :   }
     156             : 
     157             :   private void addNewNotes(NoteMap notes) throws IOException {
     158           2 :     for (Note n : notes) {
     159           2 :       if (!ours.contains(n)) {
     160           2 :         ours.set(n, n.getData());
     161             :       }
     162           2 :     }
     163           2 :   }
     164             : 
     165             :   private void addAllNotes(NoteMap notes) throws IOException {
     166           0 :     for (Note n : notes) {
     167           0 :       if (ours.contains(n)) {
     168             :         // Merge the existing and the new note as if they are both new,
     169             :         // means: base == null
     170             :         // There is no really a common ancestry for these two note revisions
     171           0 :         ObjectId noteContent =
     172           0 :             getNoteMerger().merge(null, n, ours.getNote(n), reader, inserter).getData();
     173           0 :         ours.set(n, noteContent);
     174           0 :       } else {
     175           0 :         ours.set(n, n.getData());
     176             :       }
     177           0 :     }
     178           0 :   }
     179             : 
     180             :   private NoteMerger getNoteMerger() {
     181           0 :     if (noteMerger == null) {
     182           0 :       noteMerger = new ReviewNoteMerger();
     183             :     }
     184           0 :     return noteMerger;
     185             :   }
     186             : 
     187             :   private void loadBase(String notesBranch) throws IOException {
     188           2 :     Ref branch = db.getRefDatabase().exactRef(notesBranch);
     189           2 :     if (branch != null) {
     190           1 :       baseCommit = revWalk.parseCommit(branch.getObjectId());
     191           1 :       base = NoteMap.read(revWalk.getObjectReader(), baseCommit);
     192             :     }
     193           2 :     if (baseCommit != null) {
     194           1 :       ours = NoteMap.read(revWalk.getObjectReader(), baseCommit);
     195             :     } else {
     196           2 :       ours = NoteMap.newEmptyMap();
     197             :     }
     198           2 :   }
     199             : 
     200             :   private RevCommit createCommit(
     201             :       NoteMap map, PersonIdent author, String message, RevCommit... parents) throws IOException {
     202           2 :     CommitBuilder b = new CommitBuilder();
     203           2 :     b.setTreeId(map.writeTree(inserter));
     204           2 :     b.setAuthor(author != null ? author : gerritIdent);
     205           2 :     b.setCommitter(gerritIdent);
     206           2 :     if (parents.length > 0) {
     207           1 :       b.setParentIds(parents);
     208             :     }
     209           2 :     b.setMessage(message);
     210           2 :     ObjectId commitId = inserter.insert(b);
     211           2 :     inserter.flush();
     212           2 :     return revWalk.parseCommit(commitId);
     213             :   }
     214             : 
     215             :   private void updateRef(String notesBranch) throws LockFailureException, IOException {
     216           2 :     if (baseCommit != null && oursCommit.getTree().equals(baseCommit.getTree())) {
     217             :       // If the trees are identical, there is no change in the notes.
     218             :       // Avoid saving this commit as it has no new information.
     219           1 :       return;
     220             :     }
     221           2 :     BatchRefUpdate bru = db.getRefDatabase().newBatchUpdate();
     222           2 :     bru.addCommand(
     223           2 :         new ReceiveCommand(firstNonNull(baseCommit, ObjectId.zeroId()), oursCommit, notesBranch));
     224           2 :     RefUpdateUtil.executeChecked(bru, revWalk);
     225           2 :     gitRefUpdated.fire(project, bru, null);
     226           2 :   }
     227             : }

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