LCOV - code coverage report
Current view: top level - server/patch - DiffUtil.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 30 32 93.8 %
Date: 2022-11-19 15:00:39 Functions: 10 11 90.9 %

          Line data    Source code
       1             : // Copyright (C) 2020 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             : 
      16             : package com.google.gerrit.server.patch;
      17             : 
      18             : import com.google.common.collect.ArrayListMultimap;
      19             : import com.google.common.collect.ImmutableList;
      20             : import com.google.common.collect.ListMultimap;
      21             : import com.google.gerrit.entities.Patch.ChangeType;
      22             : import com.google.gerrit.server.patch.diff.ModifiedFilesCache;
      23             : import com.google.gerrit.server.patch.gitdiff.GitModifiedFilesCache;
      24             : import com.google.gerrit.server.patch.gitdiff.ModifiedFile;
      25             : import java.io.IOException;
      26             : import java.util.Comparator;
      27             : import java.util.List;
      28             : import org.eclipse.jgit.lib.ObjectId;
      29             : import org.eclipse.jgit.revwalk.RevCommit;
      30             : import org.eclipse.jgit.revwalk.RevWalk;
      31             : 
      32             : /**
      33             :  * A utility class used by the diff cache interfaces {@link GitModifiedFilesCache} and {@link
      34             :  * ModifiedFilesCache}.
      35             :  */
      36           0 : public class DiffUtil {
      37             : 
      38             :   /**
      39             :    * Return the {@code modifiedFiles} input list while merging rewritten entries.
      40             :    *
      41             :    * <p>Background: In some cases, JGit returns two diff entries (ADDED/DELETED, RENAMED/DELETED,
      42             :    * etc...) for the same file path. This happens e.g. when a file's mode is changed between
      43             :    * patchsets, for example converting a symlink file to a regular file. We identify this case and
      44             :    * return a single modified file with changeType = {@link ChangeType#REWRITE}.
      45             :    */
      46             :   public static ImmutableList<ModifiedFile> mergeRewrittenModifiedFiles(
      47             :       List<ModifiedFile> modifiedFiles) {
      48         104 :     ImmutableList.Builder<ModifiedFile> result = ImmutableList.builder();
      49         104 :     ListMultimap<String, ModifiedFile> byPath = ArrayListMultimap.create();
      50         104 :     modifiedFiles.stream()
      51         104 :         .forEach(
      52             :             f -> {
      53          93 :               if (f.changeType() == ChangeType.DELETED) {
      54          25 :                 byPath.get(f.oldPath().get()).add(f);
      55             :               } else {
      56          93 :                 byPath.get(f.newPath().get()).add(f);
      57             :               }
      58          93 :             });
      59         104 :     for (String path : byPath.keySet()) {
      60          93 :       List<ModifiedFile> entries = byPath.get(path);
      61          93 :       if (entries.size() == 1) {
      62          93 :         result.add(entries.get(0));
      63             :       } else {
      64             :         // More than one. Return a single REWRITE entry.
      65             :         // Convert the first entry (prioritized according to change type enum order) to REWRITE
      66           3 :         entries.sort(Comparator.comparingInt(o -> o.changeType().ordinal()));
      67           3 :         result.add(entries.get(0).toBuilder().changeType(ChangeType.REWRITE).build());
      68             :       }
      69          93 :     }
      70         104 :     return result.build();
      71             :   }
      72             : 
      73             :   /**
      74             :    * Returns the Git tree object ID pointed to by the commitId parameter.
      75             :    *
      76             :    * @param rw a {@link RevWalk} of an opened repository that is used to walk the commit graph.
      77             :    * @param commitId 20 bytes commitId SHA-1 hash.
      78             :    * @return Git tree object ID pointed to by the commitId.
      79             :    */
      80             :   public static ObjectId getTreeId(RevWalk rw, ObjectId commitId) throws IOException {
      81         104 :     RevCommit current = rw.parseCommit(commitId);
      82         104 :     return current.getTree().getId();
      83             :   }
      84             : 
      85             :   /**
      86             :    * Returns the RevCommit object given the 20 bytes commitId SHA-1 hash.
      87             :    *
      88             :    * @param rw a {@link RevWalk} of an opened repository that is used to walk the commit graph.
      89             :    * @param commitId 20 bytes commitId SHA-1 hash
      90             :    * @return The RevCommit representing the commit in Git
      91             :    * @throws IOException a pack file or loose object could not be read while parsing the commits.
      92             :    */
      93             :   public static RevCommit getRevCommit(RevWalk rw, ObjectId commitId) throws IOException {
      94         104 :     return rw.parseCommit(commitId);
      95             :   }
      96             : 
      97             :   /**
      98             :    * Returns true if the commitA and commitB parameters are parent/child, if they have a common
      99             :    * parent, or if any of them is a root or merge commit.
     100             :    */
     101             :   public static boolean areRelated(RevCommit commitA, RevCommit commitB) {
     102         100 :     return commitA == null
     103         100 :         || isRootOrMergeCommit(commitA)
     104          67 :         || isRootOrMergeCommit(commitB)
     105          66 :         || areParentAndChild(commitA, commitB)
     106         100 :         || haveCommonParent(commitA, commitB);
     107             :   }
     108             : 
     109             :   public static int stringSize(String str) {
     110         104 :     if (str != null) {
     111             :       // each character in the string occupies 2 bytes. Ignoring the fixed overhead for the string
     112             :       // (length, offset and hash code) since they are negligible and do not affect the comparison
     113             :       // of 2 strings.
     114         104 :       return str.length() * 2;
     115             :     }
     116           0 :     return 0;
     117             :   }
     118             : 
     119             :   private static boolean isRootOrMergeCommit(RevCommit commit) {
     120         100 :     return commit.getParentCount() != 1;
     121             :   }
     122             : 
     123             :   private static boolean areParentAndChild(RevCommit commitA, RevCommit commitB) {
     124          66 :     return ObjectId.isEqual(commitA.getParent(0), commitB)
     125          66 :         || ObjectId.isEqual(commitB.getParent(0), commitA);
     126             :   }
     127             : 
     128             :   private static boolean haveCommonParent(RevCommit commitA, RevCommit commitB) {
     129          11 :     return ObjectId.isEqual(commitA.getParent(0), commitB.getParent(0));
     130             :   }
     131             : }

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