You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ReflogWriter.java 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /*
  2. * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
  3. * Copyright (C) 2009-2010, Google Inc.
  4. * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
  5. * Copyright (C) 2006, 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.internal.storage.file;
  14. import static org.eclipse.jgit.lib.Constants.HEAD;
  15. import static org.eclipse.jgit.lib.Constants.LOCK_SUFFIX;
  16. import static org.eclipse.jgit.lib.Constants.R_HEADS;
  17. import static org.eclipse.jgit.lib.Constants.R_NOTES;
  18. import static org.eclipse.jgit.lib.Constants.R_REFS;
  19. import static org.eclipse.jgit.lib.Constants.R_REMOTES;
  20. import java.io.File;
  21. import java.io.FileNotFoundException;
  22. import java.io.FileOutputStream;
  23. import java.io.IOException;
  24. import java.nio.ByteBuffer;
  25. import java.nio.channels.FileChannel;
  26. import java.text.MessageFormat;
  27. import org.eclipse.jgit.internal.JGitText;
  28. import org.eclipse.jgit.lib.ConfigConstants;
  29. import org.eclipse.jgit.lib.Constants;
  30. import org.eclipse.jgit.lib.CoreConfig;
  31. import org.eclipse.jgit.lib.ObjectId;
  32. import org.eclipse.jgit.lib.PersonIdent;
  33. import org.eclipse.jgit.lib.Ref;
  34. import org.eclipse.jgit.lib.RefUpdate;
  35. import org.eclipse.jgit.lib.ReflogEntry;
  36. import org.eclipse.jgit.lib.Repository;
  37. import org.eclipse.jgit.util.FileUtils;
  38. /**
  39. * Utility for writing reflog entries using the traditional one-file-per-log
  40. * format.
  41. */
  42. public class ReflogWriter {
  43. /**
  44. * Get the ref name to be used for when locking a ref's log for rewriting.
  45. *
  46. * @param name
  47. * name of the ref, relative to the Git repository top level
  48. * directory (so typically starts with refs/).
  49. * @return the name of the ref's lock ref.
  50. */
  51. public static String refLockFor(String name) {
  52. return name + LOCK_SUFFIX;
  53. }
  54. private final RefDirectory refdb;
  55. private final boolean forceWrite;
  56. /**
  57. * Create writer for ref directory.
  58. *
  59. * @param refdb
  60. * a {@link org.eclipse.jgit.internal.storage.file.RefDirectory}
  61. * object.
  62. */
  63. public ReflogWriter(RefDirectory refdb) {
  64. this(refdb, false);
  65. }
  66. /**
  67. * Create writer for ref directory.
  68. *
  69. * @param refdb
  70. * a {@link org.eclipse.jgit.internal.storage.file.RefDirectory}
  71. * object.
  72. * @param forceWrite
  73. * true to write to disk all entries logged, false to respect the
  74. * repository's config and current log file status.
  75. */
  76. public ReflogWriter(RefDirectory refdb, boolean forceWrite) {
  77. this.refdb = refdb;
  78. this.forceWrite = forceWrite;
  79. }
  80. /**
  81. * Create the log directories.
  82. *
  83. * @throws java.io.IOException
  84. * @return this writer.
  85. */
  86. public ReflogWriter create() throws IOException {
  87. FileUtils.mkdir(refdb.logsDir);
  88. FileUtils.mkdir(refdb.logsRefsDir);
  89. FileUtils.mkdir(
  90. new File(refdb.logsRefsDir, R_HEADS.substring(R_REFS.length())));
  91. return this;
  92. }
  93. /**
  94. * Write the given entry to the ref's log.
  95. *
  96. * @param refName
  97. * a {@link java.lang.String} object.
  98. * @param entry
  99. * a {@link org.eclipse.jgit.lib.ReflogEntry} object.
  100. * @return this writer
  101. * @throws java.io.IOException
  102. */
  103. public ReflogWriter log(String refName, ReflogEntry entry)
  104. throws IOException {
  105. return log(refName, entry.getOldId(), entry.getNewId(), entry.getWho(),
  106. entry.getComment());
  107. }
  108. /**
  109. * Write the given entry information to the ref's log
  110. *
  111. * @param refName
  112. * ref name
  113. * @param oldId
  114. * old object id
  115. * @param newId
  116. * new object id
  117. * @param ident
  118. * a {@link org.eclipse.jgit.lib.PersonIdent}
  119. * @param message
  120. * reflog message
  121. * @return this writer
  122. * @throws java.io.IOException
  123. */
  124. public ReflogWriter log(String refName, ObjectId oldId,
  125. ObjectId newId, PersonIdent ident, String message) throws IOException {
  126. byte[] encoded = encode(oldId, newId, ident, message);
  127. return log(refName, encoded);
  128. }
  129. /**
  130. * Write the given ref update to the ref's log.
  131. *
  132. * @param update
  133. * a {@link org.eclipse.jgit.lib.RefUpdate}
  134. * @param msg
  135. * reflog message
  136. * @param deref
  137. * whether to dereference symbolic refs
  138. * @return this writer
  139. * @throws java.io.IOException
  140. */
  141. public ReflogWriter log(RefUpdate update, String msg,
  142. boolean deref) throws IOException {
  143. ObjectId oldId = update.getOldObjectId();
  144. ObjectId newId = update.getNewObjectId();
  145. Ref ref = update.getRef();
  146. PersonIdent ident = update.getRefLogIdent();
  147. if (ident == null)
  148. ident = new PersonIdent(refdb.getRepository());
  149. else
  150. ident = new PersonIdent(ident);
  151. byte[] rec = encode(oldId, newId, ident, msg);
  152. if (deref && ref.isSymbolic()) {
  153. log(ref.getName(), rec);
  154. log(ref.getLeaf().getName(), rec);
  155. } else
  156. log(ref.getName(), rec);
  157. return this;
  158. }
  159. private byte[] encode(ObjectId oldId, ObjectId newId, PersonIdent ident,
  160. String message) {
  161. StringBuilder r = new StringBuilder();
  162. r.append(ObjectId.toString(oldId));
  163. r.append(' ');
  164. r.append(ObjectId.toString(newId));
  165. r.append(' ');
  166. r.append(ident.toExternalString());
  167. r.append('\t');
  168. r.append(
  169. message.replace("\r\n", " ") //$NON-NLS-1$ //$NON-NLS-2$
  170. .replace("\n", " ")); //$NON-NLS-1$ //$NON-NLS-2$
  171. r.append('\n');
  172. return Constants.encode(r.toString());
  173. }
  174. private FileOutputStream getFileOutputStream(File log) throws IOException {
  175. try {
  176. return new FileOutputStream(log, true);
  177. } catch (FileNotFoundException err) {
  178. File dir = log.getParentFile();
  179. if (dir.exists()) {
  180. throw err;
  181. }
  182. if (!dir.mkdirs() && !dir.isDirectory()) {
  183. throw new IOException(MessageFormat
  184. .format(JGitText.get().cannotCreateDirectory, dir));
  185. }
  186. return new FileOutputStream(log, true);
  187. }
  188. }
  189. private ReflogWriter log(String refName, byte[] rec) throws IOException {
  190. File log = refdb.logFor(refName);
  191. boolean write = forceWrite
  192. || shouldAutoCreateLog(refName)
  193. || log.isFile();
  194. if (!write)
  195. return this;
  196. WriteConfig wc = refdb.getRepository().getConfig().get(WriteConfig.KEY);
  197. try (FileOutputStream out = getFileOutputStream(log)) {
  198. if (wc.getFSyncRefFiles()) {
  199. FileChannel fc = out.getChannel();
  200. ByteBuffer buf = ByteBuffer.wrap(rec);
  201. while (0 < buf.remaining()) {
  202. fc.write(buf);
  203. }
  204. fc.force(true);
  205. } else {
  206. out.write(rec);
  207. }
  208. }
  209. return this;
  210. }
  211. private boolean shouldAutoCreateLog(String refName) {
  212. Repository repo = refdb.getRepository();
  213. CoreConfig.LogRefUpdates value = repo.isBare()
  214. ? CoreConfig.LogRefUpdates.FALSE
  215. : CoreConfig.LogRefUpdates.TRUE;
  216. value = repo.getConfig().getEnum(ConfigConstants.CONFIG_CORE_SECTION,
  217. null, ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, value);
  218. if (value != null) {
  219. switch (value) {
  220. case FALSE:
  221. break;
  222. case TRUE:
  223. return refName.equals(HEAD) || refName.startsWith(R_HEADS)
  224. || refName.startsWith(R_REMOTES)
  225. || refName.startsWith(R_NOTES);
  226. case ALWAYS:
  227. return refName.equals(HEAD) || refName.startsWith(R_REFS);
  228. default:
  229. break;
  230. }
  231. }
  232. return false;
  233. }
  234. }