RefWriter.java

  1. /*
  2.  * Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
  3.  * Copyright (C) 2009-2010, Google Inc.
  4.  * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com>
  5.  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
  6.  *
  7.  * This program and the accompanying materials are made available under the
  8.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  9.  * https://www.eclipse.org/org/documents/edl-v10.php.
  10.  *
  11.  * SPDX-License-Identifier: BSD-3-Clause
  12.  */

  13. package org.eclipse.jgit.lib;

  14. import java.io.IOException;
  15. import java.io.StringWriter;
  16. import java.util.Collection;
  17. import java.util.Map;

  18. import org.eclipse.jgit.internal.storage.file.RefDirectory;
  19. import org.eclipse.jgit.util.RefList;
  20. import org.eclipse.jgit.util.RefMap;

  21. /**
  22.  * Writes out refs to the {@link org.eclipse.jgit.lib.Constants#INFO_REFS} and
  23.  * {@link org.eclipse.jgit.lib.Constants#PACKED_REFS} files.
  24.  *
  25.  * This class is abstract as the writing of the files must be handled by the
  26.  * caller. This is because it is used by transport classes as well.
  27.  */
  28. public abstract class RefWriter {

  29.     private final Collection<Ref> refs;

  30.     /**
  31.      * <p>Constructor for RefWriter.</p>
  32.      *
  33.      * @param refs
  34.      *            the complete set of references. This should have been computed
  35.      *            by applying updates to the advertised refs already discovered.
  36.      */
  37.     public RefWriter(Collection<Ref> refs) {
  38.         this.refs = RefComparator.sort(refs);
  39.     }

  40.     /**
  41.      * <p>Constructor for RefWriter.</p>
  42.      *
  43.      * @param refs
  44.      *            the complete set of references. This should have been computed
  45.      *            by applying updates to the advertised refs already discovered.
  46.      */
  47.     public RefWriter(Map<String, Ref> refs) {
  48.         if (refs instanceof RefMap)
  49.             this.refs = refs.values();
  50.         else
  51.             this.refs = RefComparator.sort(refs.values());
  52.     }

  53.     /**
  54.      * <p>Constructor for RefWriter.</p>
  55.      *
  56.      * @param refs
  57.      *            the complete set of references. This should have been computed
  58.      *            by applying updates to the advertised refs already discovered.
  59.      */
  60.     public RefWriter(RefList<Ref> refs) {
  61.         this.refs = refs.asList();
  62.     }

  63.     /**
  64.      * Rebuild the {@link org.eclipse.jgit.lib.Constants#INFO_REFS}.
  65.      * <p>
  66.      * This method rebuilds the contents of the
  67.      * {@link org.eclipse.jgit.lib.Constants#INFO_REFS} file to match the passed
  68.      * list of references.
  69.      *
  70.      * @throws java.io.IOException
  71.      *             writing is not supported, or attempting to write the file
  72.      *             failed, possibly due to permissions or remote disk full, etc.
  73.      */
  74.     public void writeInfoRefs() throws IOException {
  75.         final StringWriter w = new StringWriter();
  76.         final char[] tmp = new char[Constants.OBJECT_ID_STRING_LENGTH];
  77.         for (Ref r : refs) {
  78.             if (Constants.HEAD.equals(r.getName())) {
  79.                 // Historically HEAD has never been published through
  80.                 // the INFO_REFS file. This is a mistake, but its the
  81.                 // way things are.
  82.                 //
  83.                 continue;
  84.             }

  85.             ObjectId objectId = r.getObjectId();
  86.             if (objectId == null) {
  87.                 // Symrefs to unborn branches aren't advertised in the info/refs
  88.                 // file.
  89.                 continue;
  90.             }
  91.             objectId.copyTo(tmp, w);
  92.             w.write('\t');
  93.             w.write(r.getName());
  94.             w.write('\n');

  95.             ObjectId peeledObjectId = r.getPeeledObjectId();
  96.             if (peeledObjectId != null) {
  97.                 peeledObjectId.copyTo(tmp, w);
  98.                 w.write('\t');
  99.                 w.write(r.getName());
  100.                 w.write("^{}\n"); //$NON-NLS-1$
  101.             }
  102.         }
  103.         writeFile(Constants.INFO_REFS, Constants.encode(w.toString()));
  104.     }

  105.     /**
  106.      * Rebuild the {@link org.eclipse.jgit.lib.Constants#PACKED_REFS} file.
  107.      * <p>
  108.      * This method rebuilds the contents of the
  109.      * {@link org.eclipse.jgit.lib.Constants#PACKED_REFS} file to match the
  110.      * passed list of references, including only those refs that have a storage
  111.      * type of {@link org.eclipse.jgit.lib.Ref.Storage#PACKED}.
  112.      *
  113.      * @throws java.io.IOException
  114.      *             writing is not supported, or attempting to write the file
  115.      *             failed, possibly due to permissions or remote disk full, etc.
  116.      */
  117.     public void writePackedRefs() throws IOException {
  118.         boolean peeled = false;
  119.         for (Ref r : refs) {
  120.             if (r.getStorage().isPacked() && r.isPeeled()) {
  121.                 peeled = true;
  122.                 break;
  123.             }
  124.         }

  125.         final StringWriter w = new StringWriter();
  126.         if (peeled) {
  127.             w.write(RefDirectory.PACKED_REFS_HEADER);
  128.             if (peeled)
  129.                 w.write(RefDirectory.PACKED_REFS_PEELED);
  130.             w.write('\n');
  131.         }

  132.         final char[] tmp = new char[Constants.OBJECT_ID_STRING_LENGTH];
  133.         for (Ref r : refs) {
  134.             if (r.getStorage() != Ref.Storage.PACKED)
  135.                 continue;

  136.             ObjectId objectId = r.getObjectId();
  137.             if (objectId == null) {
  138.                 // A packed ref cannot be a symref, let alone a symref
  139.                 // to an unborn branch.
  140.                 throw new NullPointerException();
  141.             }
  142.             objectId.copyTo(tmp, w);
  143.             w.write(' ');
  144.             w.write(r.getName());
  145.             w.write('\n');

  146.             ObjectId peeledObjectId = r.getPeeledObjectId();
  147.             if (peeledObjectId != null) {
  148.                 w.write('^');
  149.                 peeledObjectId.copyTo(tmp, w);
  150.                 w.write('\n');
  151.             }
  152.         }
  153.         writeFile(Constants.PACKED_REFS, Constants.encode(w.toString()));
  154.     }

  155.     /**
  156.      * Handles actual writing of ref files to the git repository, which may
  157.      * differ slightly depending on the destination and transport.
  158.      *
  159.      * @param file
  160.      *            path to ref file.
  161.      * @param content
  162.      *            byte content of file to be written.
  163.      * @throws java.io.IOException
  164.      */
  165.     protected abstract void writeFile(String file, byte[] content)
  166.             throws IOException;
  167. }