private static Groups prepareGroups(
      @NotNull VcsLogDataPack dataPack,
      @Nullable Collection<VirtualFile> visibleRoots,
      @Nullable List<List<String>> recentItems) {
    Groups filteredGroups = new Groups();
    Collection<VcsRef> allRefs = dataPack.getRefs().getBranches();
    for (Map.Entry<VirtualFile, Set<VcsRef>> entry :
        VcsLogUtil.groupRefsByRoot(allRefs).entrySet()) {
      VirtualFile root = entry.getKey();
      if (visibleRoots != null && !visibleRoots.contains(root)) continue;
      Collection<VcsRef> refs = entry.getValue();
      VcsLogProvider provider = dataPack.getLogProviders().get(root);
      VcsLogRefManager refManager = provider.getReferenceManager();
      List<RefGroup> refGroups = refManager.group(refs);

      putActionsForReferences(refGroups, filteredGroups);
    }

    if (recentItems != null) {
      for (List<String> recentItem : recentItems) {
        if (recentItem.size() == 1) {
          final String item = ContainerUtil.getFirstItem(recentItem);
          if (filteredGroups.singletonGroups.contains(item)
              || ContainerUtil.find(
                      filteredGroups.expandedGroups.values(), strings -> strings.contains(item))
                  != null) {
            continue;
          }
        }
        filteredGroups.recentGroups.add(recentItem);
      }
    }

    return filteredGroups;
  }
  @Override
  public void customizePainter(
      @NotNull JComponent component,
      @NotNull Collection<VcsRef> references,
      @Nullable VcsLogRefManager manager,
      @NotNull Color background,
      @NotNull Color foreground) {
    FontMetrics metrics = component.getFontMetrics(getReferenceFont());
    myHeight =
        metrics.getHeight()
            + RectanglePainter.TOP_TEXT_PADDING
            + RectanglePainter.BOTTOM_TEXT_PADDING;
    myWidth = 2 * PaintParameters.LABEL_PADDING;

    myLabels = ContainerUtil.newArrayList();
    if (manager == null) return;

    List<VcsRef> sorted = ContainerUtil.sorted(references, manager.getLabelsOrderComparator());

    for (Map.Entry<VcsRefType, Collection<VcsRef>> entry :
        ContainerUtil.groupBy(sorted, VcsRef::getType).entrySet()) {
      VcsRef ref = ObjectUtils.assertNotNull(ContainerUtil.getFirstItem(entry.getValue()));
      String text = ref.getName() + (entry.getValue().size() > 1 ? " +" : "");
      myLabels.add(Pair.create(text, entry.getKey().getBackgroundColor()));

      myWidth +=
          myLabelPainter.calculateSize(text, metrics).getWidth() + PaintParameters.LABEL_PADDING;
    }
  }
  @Override
  public int compareHeads(int head1, int head2) {
    if (head1 == head2) {
      return 0;
    }

    Collection<VcsRef> refs1 = myRefsModel.refsToCommit(head1);
    Collection<VcsRef> refs2 = myRefsModel.refsToCommit(head2);
    boolean firstEmpty = isEmptyRefs(refs1, head1);
    boolean secondEmpty = isEmptyRefs(refs2, head2);
    if (firstEmpty && secondEmpty) {
      return 0;
    }
    if (firstEmpty) {
      return -1;
    }
    if (secondEmpty) {
      return 1;
    }

    VcsLogRefManager refManager1 = getRefManager(refs1);
    VcsLogRefManager refManager2 = getRefManager(refs2);
    if (!refManager1.equals(refManager2)) {
      return 0;
    }

    Map<VcsRef, Boolean> positions = ContainerUtil.newHashMap();
    for (VcsRef ref : refs1) {
      positions.put(ref, true);
    }
    for (VcsRef ref : refs2) {
      positions.put(ref, false);
    }

    VcsRef firstRef = refManager1.sort(positions.keySet()).get(0);
    return positions.get(firstRef) ? 1 : -1;
  }