View Javadoc
1   /*
2    * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3    * Copyright (C) 2008-2010, Google Inc.
4    * Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
5    * Copyright (C) 2006-2012, Shawn O. Pearce <spearce@spearce.org>
6    * Copyright (C) 2012, Daniel Megert <daniel_megert@ch.ibm.com>
7    * and other copyright owners as documented in the project's IP log.
8    *
9    * This program and the accompanying materials are made available
10   * under the terms of the Eclipse Distribution License v1.0 which
11   * accompanies this distribution, is reproduced below, and is
12   * available at http://www.eclipse.org/org/documents/edl-v10.php
13   *
14   * All rights reserved.
15   *
16   * Redistribution and use in source and binary forms, with or
17   * without modification, are permitted provided that the following
18   * conditions are met:
19   *
20   * - Redistributions of source code must retain the above copyright
21   *   notice, this list of conditions and the following disclaimer.
22   *
23   * - Redistributions in binary form must reproduce the above
24   *   copyright notice, this list of conditions and the following
25   *   disclaimer in the documentation and/or other materials provided
26   *   with the distribution.
27   *
28   * - Neither the name of the Eclipse Foundation, Inc. nor the
29   *   names of its contributors may be used to endorse or promote
30   *   products derived from this software without specific prior
31   *   written permission.
32   *
33   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
34   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
35   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
38   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
39   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
40   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
41   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
42   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
43   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
45   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46   */
47  
48  package org.eclipse.jgit.lib;
49  
50  import java.io.BufferedOutputStream;
51  import java.io.File;
52  import java.io.FileNotFoundException;
53  import java.io.FileOutputStream;
54  import java.io.IOException;
55  import java.net.URISyntaxException;
56  import java.text.MessageFormat;
57  import java.util.Collection;
58  import java.util.Collections;
59  import java.util.HashMap;
60  import java.util.HashSet;
61  import java.util.LinkedList;
62  import java.util.List;
63  import java.util.Map;
64  import java.util.Set;
65  import java.util.concurrent.atomic.AtomicInteger;
66  
67  import org.eclipse.jgit.dircache.DirCache;
68  import org.eclipse.jgit.errors.AmbiguousObjectException;
69  import org.eclipse.jgit.errors.CorruptObjectException;
70  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
71  import org.eclipse.jgit.errors.MissingObjectException;
72  import org.eclipse.jgit.errors.NoWorkTreeException;
73  import org.eclipse.jgit.errors.RevisionSyntaxException;
74  import org.eclipse.jgit.events.IndexChangedEvent;
75  import org.eclipse.jgit.events.IndexChangedListener;
76  import org.eclipse.jgit.events.ListenerList;
77  import org.eclipse.jgit.events.RepositoryEvent;
78  import org.eclipse.jgit.internal.JGitText;
79  import org.eclipse.jgit.revwalk.RevBlob;
80  import org.eclipse.jgit.revwalk.RevCommit;
81  import org.eclipse.jgit.revwalk.RevObject;
82  import org.eclipse.jgit.revwalk.RevTree;
83  import org.eclipse.jgit.revwalk.RevWalk;
84  import org.eclipse.jgit.transport.RefSpec;
85  import org.eclipse.jgit.transport.RemoteConfig;
86  import org.eclipse.jgit.treewalk.TreeWalk;
87  import org.eclipse.jgit.util.FS;
88  import org.eclipse.jgit.util.FileUtils;
89  import org.eclipse.jgit.util.IO;
90  import org.eclipse.jgit.util.RawParseUtils;
91  import org.eclipse.jgit.util.SystemReader;
92  import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
93  
94  /**
95   * Represents a Git repository.
96   * <p>
97   * A repository holds all objects and refs used for managing source code (could
98   * be any type of file, but source code is what SCM's are typically used for).
99   * <p>
100  * This class is thread-safe.
101  */
102 public abstract class Repository implements AutoCloseable {
103 	private static final ListenerList globalListeners = new ListenerList();
104 
105 	/** @return the global listener list observing all events in this JVM. */
106 	public static ListenerList getGlobalListenerList() {
107 		return globalListeners;
108 	}
109 
110 	private final AtomicInteger useCnt = new AtomicInteger(1);
111 
112 	/** Metadata directory holding the repository's critical files. */
113 	private final File gitDir;
114 
115 	/** File abstraction used to resolve paths. */
116 	private final FS fs;
117 
118 	private final ListenerList myListeners = new ListenerList();
119 
120 	/** If not bare, the top level directory of the working files. */
121 	private final File workTree;
122 
123 	/** If not bare, the index file caching the working file states. */
124 	private final File indexFile;
125 
126 	/**
127 	 * Initialize a new repository instance.
128 	 *
129 	 * @param options
130 	 *            options to configure the repository.
131 	 */
132 	protected Repository(final BaseRepositoryBuilder options) {
133 		gitDir = options.getGitDir();
134 		fs = options.getFS();
135 		workTree = options.getWorkTree();
136 		indexFile = options.getIndexFile();
137 	}
138 
139 	/** @return listeners observing only events on this repository. */
140 	public ListenerList getListenerList() {
141 		return myListeners;
142 	}
143 
144 	/**
145 	 * Fire an event to all registered listeners.
146 	 * <p>
147 	 * The source repository of the event is automatically set to this
148 	 * repository, before the event is delivered to any listeners.
149 	 *
150 	 * @param event
151 	 *            the event to deliver.
152 	 */
153 	public void fireEvent(RepositoryEvent<?> event) {
154 		event.setRepository(this);
155 		myListeners.dispatch(event);
156 		globalListeners.dispatch(event);
157 	}
158 
159 	/**
160 	 * Create a new Git repository.
161 	 * <p>
162 	 * Repository with working tree is created using this method. This method is
163 	 * the same as {@code create(false)}.
164 	 *
165 	 * @throws IOException
166 	 * @see #create(boolean)
167 	 */
168 	public void create() throws IOException {
169 		create(false);
170 	}
171 
172 	/**
173 	 * Create a new Git repository initializing the necessary files and
174 	 * directories.
175 	 *
176 	 * @param bare
177 	 *            if true, a bare repository (a repository without a working
178 	 *            directory) is created.
179 	 * @throws IOException
180 	 *             in case of IO problem
181 	 */
182 	public abstract void create(boolean bare) throws IOException;
183 
184 	/** @return local metadata directory; null if repository isn't local. */
185 	public File getDirectory() {
186 		return gitDir;
187 	}
188 
189 	/**
190 	 * @return the object database which stores this repository's data.
191 	 */
192 	public abstract ObjectDatabase getObjectDatabase();
193 
194 	/** @return a new inserter to create objects in {@link #getObjectDatabase()} */
195 	public ObjectInserter newObjectInserter() {
196 		return getObjectDatabase().newInserter();
197 	}
198 
199 	/** @return a new reader to read objects from {@link #getObjectDatabase()} */
200 	public ObjectReader newObjectReader() {
201 		return getObjectDatabase().newReader();
202 	}
203 
204 	/** @return the reference database which stores the reference namespace. */
205 	public abstract RefDatabase getRefDatabase();
206 
207 	/**
208 	 * @return the configuration of this repository
209 	 */
210 	public abstract StoredConfig getConfig();
211 
212 	/**
213 	 * @return the used file system abstraction
214 	 */
215 	public FS getFS() {
216 		return fs;
217 	}
218 
219 	/**
220 	 * @param objectId
221 	 * @return true if the specified object is stored in this repo or any of the
222 	 *         known shared repositories.
223 	 */
224 	public boolean hasObject(AnyObjectId objectId) {
225 		try {
226 			return getObjectDatabase().has(objectId);
227 		} catch (IOException e) {
228 			// Legacy API, assume error means "no"
229 			return false;
230 		}
231 	}
232 
233 	/**
234 	 * Open an object from this repository.
235 	 * <p>
236 	 * This is a one-shot call interface which may be faster than allocating a
237 	 * {@link #newObjectReader()} to perform the lookup.
238 	 *
239 	 * @param objectId
240 	 *            identity of the object to open.
241 	 * @return a {@link ObjectLoader} for accessing the object.
242 	 * @throws MissingObjectException
243 	 *             the object does not exist.
244 	 * @throws IOException
245 	 *             the object store cannot be accessed.
246 	 */
247 	public ObjectLoader open(final AnyObjectId objectId)
248 			throws MissingObjectException, IOException {
249 		return getObjectDatabase().open(objectId);
250 	}
251 
252 	/**
253 	 * Open an object from this repository.
254 	 * <p>
255 	 * This is a one-shot call interface which may be faster than allocating a
256 	 * {@link #newObjectReader()} to perform the lookup.
257 	 *
258 	 * @param objectId
259 	 *            identity of the object to open.
260 	 * @param typeHint
261 	 *            hint about the type of object being requested, e.g.
262 	 *            {@link Constants#OBJ_BLOB}; {@link ObjectReader#OBJ_ANY} if
263 	 *            the object type is not known, or does not matter to the
264 	 *            caller.
265 	 * @return a {@link ObjectLoader} for accessing the object.
266 	 * @throws MissingObjectException
267 	 *             the object does not exist.
268 	 * @throws IncorrectObjectTypeException
269 	 *             typeHint was not OBJ_ANY, and the object's actual type does
270 	 *             not match typeHint.
271 	 * @throws IOException
272 	 *             the object store cannot be accessed.
273 	 */
274 	public ObjectLoader open(AnyObjectId objectId, int typeHint)
275 			throws MissingObjectException, IncorrectObjectTypeException,
276 			IOException {
277 		return getObjectDatabase().open(objectId, typeHint);
278 	}
279 
280 	/**
281 	 * Create a command to update, create or delete a ref in this repository.
282 	 *
283 	 * @param ref
284 	 *            name of the ref the caller wants to modify.
285 	 * @return an update command. The caller must finish populating this command
286 	 *         and then invoke one of the update methods to actually make a
287 	 *         change.
288 	 * @throws IOException
289 	 *             a symbolic ref was passed in and could not be resolved back
290 	 *             to the base ref, as the symbolic ref could not be read.
291 	 */
292 	public RefUpdate updateRef(final String ref) throws IOException {
293 		return updateRef(ref, false);
294 	}
295 
296 	/**
297 	 * Create a command to update, create or delete a ref in this repository.
298 	 *
299 	 * @param ref
300 	 *            name of the ref the caller wants to modify.
301 	 * @param detach
302 	 *            true to create a detached head
303 	 * @return an update command. The caller must finish populating this command
304 	 *         and then invoke one of the update methods to actually make a
305 	 *         change.
306 	 * @throws IOException
307 	 *             a symbolic ref was passed in and could not be resolved back
308 	 *             to the base ref, as the symbolic ref could not be read.
309 	 */
310 	public RefUpdate updateRef(final String ref, final boolean detach) throws IOException {
311 		return getRefDatabase().newUpdate(ref, detach);
312 	}
313 
314 	/**
315 	 * Create a command to rename a ref in this repository
316 	 *
317 	 * @param fromRef
318 	 *            name of ref to rename from
319 	 * @param toRef
320 	 *            name of ref to rename to
321 	 * @return an update command that knows how to rename a branch to another.
322 	 * @throws IOException
323 	 *             the rename could not be performed.
324 	 *
325 	 */
326 	public RefRename renameRef(final String fromRef, final String toRef) throws IOException {
327 		return getRefDatabase().newRename(fromRef, toRef);
328 	}
329 
330 	/**
331 	 * Parse a git revision string and return an object id.
332 	 *
333 	 * Combinations of these operators are supported:
334 	 * <ul>
335 	 * <li><b>HEAD</b>, <b>MERGE_HEAD</b>, <b>FETCH_HEAD</b></li>
336 	 * <li><b>SHA-1</b>: a complete or abbreviated SHA-1</li>
337 	 * <li><b>refs/...</b>: a complete reference name</li>
338 	 * <li><b>short-name</b>: a short reference name under {@code refs/heads},
339 	 * {@code refs/tags}, or {@code refs/remotes} namespace</li>
340 	 * <li><b>tag-NN-gABBREV</b>: output from describe, parsed by treating
341 	 * {@code ABBREV} as an abbreviated SHA-1.</li>
342 	 * <li><i>id</i><b>^</b>: first parent of commit <i>id</i>, this is the same
343 	 * as {@code id^1}</li>
344 	 * <li><i>id</i><b>^0</b>: ensure <i>id</i> is a commit</li>
345 	 * <li><i>id</i><b>^n</b>: n-th parent of commit <i>id</i></li>
346 	 * <li><i>id</i><b>~n</b>: n-th historical ancestor of <i>id</i>, by first
347 	 * parent. {@code id~3} is equivalent to {@code id^1^1^1} or {@code id^^^}.</li>
348 	 * <li><i>id</i><b>:path</b>: Lookup path under tree named by <i>id</i></li>
349 	 * <li><i>id</i><b>^{commit}</b>: ensure <i>id</i> is a commit</li>
350 	 * <li><i>id</i><b>^{tree}</b>: ensure <i>id</i> is a tree</li>
351 	 * <li><i>id</i><b>^{tag}</b>: ensure <i>id</i> is a tag</li>
352 	 * <li><i>id</i><b>^{blob}</b>: ensure <i>id</i> is a blob</li>
353 	 * </ul>
354 	 *
355 	 * <p>
356 	 * The following operators are specified by Git conventions, but are not
357 	 * supported by this method:
358 	 * <ul>
359 	 * <li><b>ref@{n}</b>: n-th version of ref as given by its reflog</li>
360 	 * <li><b>ref@{time}</b>: value of ref at the designated time</li>
361 	 * </ul>
362 	 *
363 	 * @param revstr
364 	 *            A git object references expression
365 	 * @return an ObjectId or null if revstr can't be resolved to any ObjectId
366 	 * @throws AmbiguousObjectException
367 	 *             {@code revstr} contains an abbreviated ObjectId and this
368 	 *             repository contains more than one object which match to the
369 	 *             input abbreviation.
370 	 * @throws IncorrectObjectTypeException
371 	 *             the id parsed does not meet the type required to finish
372 	 *             applying the operators in the expression.
373 	 * @throws RevisionSyntaxException
374 	 *             the expression is not supported by this implementation, or
375 	 *             does not meet the standard syntax.
376 	 * @throws IOException
377 	 *             on serious errors
378 	 */
379 	public ObjectId resolve(final String revstr)
380 			throws AmbiguousObjectException, IncorrectObjectTypeException,
381 			RevisionSyntaxException, IOException {
382 		try (RevWalk rw = new RevWalk(this)) {
383 			Object resolved = resolve(rw, revstr);
384 			if (resolved instanceof String) {
385 				final Ref ref = getRef((String)resolved);
386 				return ref != null ? ref.getLeaf().getObjectId() : null;
387 			} else {
388 				return (ObjectId) resolved;
389 			}
390 		}
391 	}
392 
393 	/**
394 	 * Simplify an expression, but unlike {@link #resolve(String)} it will not
395 	 * resolve a branch passed or resulting from the expression, such as @{-}.
396 	 * Thus this method can be used to process an expression to a method that
397 	 * expects a branch or revision id.
398 	 *
399 	 * @param revstr
400 	 * @return object id or ref name from resolved expression
401 	 * @throws AmbiguousObjectException
402 	 * @throws IOException
403 	 */
404 	public String simplify(final String revstr)
405 			throws AmbiguousObjectException, IOException {
406 		try (RevWalk rw = new RevWalk(this)) {
407 			Object resolved = resolve(rw, revstr);
408 			if (resolved != null)
409 				if (resolved instanceof String)
410 					return (String) resolved;
411 				else
412 					return ((AnyObjectId) resolved).getName();
413 			return null;
414 		}
415 	}
416 
417 	private Object resolve(final RevWalk rw, final String revstr)
418 			throws IOException {
419 		char[] revChars = revstr.toCharArray();
420 		RevObject rev = null;
421 		String name = null;
422 		int done = 0;
423 		for (int i = 0; i < revChars.length; ++i) {
424 			switch (revChars[i]) {
425 			case '^':
426 				if (rev == null) {
427 					if (name == null)
428 						if (done == 0)
429 							name = new String(revChars, done, i);
430 						else {
431 							done = i + 1;
432 							break;
433 						}
434 					rev = parseSimple(rw, name);
435 					name = null;
436 					if (rev == null)
437 						return null;
438 				}
439 				if (i + 1 < revChars.length) {
440 					switch (revChars[i + 1]) {
441 					case '0':
442 					case '1':
443 					case '2':
444 					case '3':
445 					case '4':
446 					case '5':
447 					case '6':
448 					case '7':
449 					case '8':
450 					case '9':
451 						int j;
452 						rev = rw.parseCommit(rev);
453 						for (j = i + 1; j < revChars.length; ++j) {
454 							if (!Character.isDigit(revChars[j]))
455 								break;
456 						}
457 						String parentnum = new String(revChars, i + 1, j - i
458 								- 1);
459 						int pnum;
460 						try {
461 							pnum = Integer.parseInt(parentnum);
462 						} catch (NumberFormatException e) {
463 							throw new RevisionSyntaxException(
464 									JGitText.get().invalidCommitParentNumber,
465 									revstr);
466 						}
467 						if (pnum != 0) {
468 							RevCommit commit = (RevCommit) rev;
469 							if (pnum > commit.getParentCount())
470 								rev = null;
471 							else
472 								rev = commit.getParent(pnum - 1);
473 						}
474 						i = j - 1;
475 						done = j;
476 						break;
477 					case '{':
478 						int k;
479 						String item = null;
480 						for (k = i + 2; k < revChars.length; ++k) {
481 							if (revChars[k] == '}') {
482 								item = new String(revChars, i + 2, k - i - 2);
483 								break;
484 							}
485 						}
486 						i = k;
487 						if (item != null)
488 							if (item.equals("tree")) { //$NON-NLS-1$
489 								rev = rw.parseTree(rev);
490 							} else if (item.equals("commit")) { //$NON-NLS-1$
491 								rev = rw.parseCommit(rev);
492 							} else if (item.equals("blob")) { //$NON-NLS-1$
493 								rev = rw.peel(rev);
494 								if (!(rev instanceof RevBlob))
495 									throw new IncorrectObjectTypeException(rev,
496 											Constants.TYPE_BLOB);
497 							} else if (item.equals("")) { //$NON-NLS-1$
498 								rev = rw.peel(rev);
499 							} else
500 								throw new RevisionSyntaxException(revstr);
501 						else
502 							throw new RevisionSyntaxException(revstr);
503 						done = k;
504 						break;
505 					default:
506 						rev = rw.peel(rev);
507 						if (rev instanceof RevCommit) {
508 							RevCommit commit = ((RevCommit) rev);
509 							if (commit.getParentCount() == 0)
510 								rev = null;
511 							else
512 								rev = commit.getParent(0);
513 						} else
514 							throw new IncorrectObjectTypeException(rev,
515 									Constants.TYPE_COMMIT);
516 					}
517 				} else {
518 					rev = rw.peel(rev);
519 					if (rev instanceof RevCommit) {
520 						RevCommit commit = ((RevCommit) rev);
521 						if (commit.getParentCount() == 0)
522 							rev = null;
523 						else
524 							rev = commit.getParent(0);
525 					} else
526 						throw new IncorrectObjectTypeException(rev,
527 								Constants.TYPE_COMMIT);
528 				}
529 				done = i + 1;
530 				break;
531 			case '~':
532 				if (rev == null) {
533 					if (name == null)
534 						if (done == 0)
535 							name = new String(revChars, done, i);
536 						else {
537 							done = i + 1;
538 							break;
539 						}
540 					rev = parseSimple(rw, name);
541 					name = null;
542 					if (rev == null)
543 						return null;
544 				}
545 				rev = rw.peel(rev);
546 				if (!(rev instanceof RevCommit))
547 					throw new IncorrectObjectTypeException(rev,
548 							Constants.TYPE_COMMIT);
549 				int l;
550 				for (l = i + 1; l < revChars.length; ++l) {
551 					if (!Character.isDigit(revChars[l]))
552 						break;
553 				}
554 				int dist;
555 				if (l - i > 1) {
556 					String distnum = new String(revChars, i + 1, l - i - 1);
557 					try {
558 						dist = Integer.parseInt(distnum);
559 					} catch (NumberFormatException e) {
560 						throw new RevisionSyntaxException(
561 								JGitText.get().invalidAncestryLength, revstr);
562 					}
563 				} else
564 					dist = 1;
565 				while (dist > 0) {
566 					RevCommit commit = (RevCommit) rev;
567 					if (commit.getParentCount() == 0) {
568 						rev = null;
569 						break;
570 					}
571 					commit = commit.getParent(0);
572 					rw.parseHeaders(commit);
573 					rev = commit;
574 					--dist;
575 				}
576 				i = l - 1;
577 				done = l;
578 				break;
579 			case '@':
580 				if (rev != null)
581 					throw new RevisionSyntaxException(revstr);
582 				if (i + 1 < revChars.length && revChars[i + 1] != '{')
583 					continue;
584 				int m;
585 				String time = null;
586 				for (m = i + 2; m < revChars.length; ++m) {
587 					if (revChars[m] == '}') {
588 						time = new String(revChars, i + 2, m - i - 2);
589 						break;
590 					}
591 				}
592 				if (time != null) {
593 					if (time.equals("upstream")) { //$NON-NLS-1$
594 						if (name == null)
595 							name = new String(revChars, done, i);
596 						if (name.equals("")) //$NON-NLS-1$
597 							// Currently checked out branch, HEAD if
598 							// detached
599 							name = Constants.HEAD;
600 						if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
601 							throw new RevisionSyntaxException(revstr);
602 						Ref ref = getRef(name);
603 						name = null;
604 						if (ref == null)
605 							return null;
606 						if (ref.isSymbolic())
607 							ref = ref.getLeaf();
608 						name = ref.getName();
609 
610 						RemoteConfig remoteConfig;
611 						try {
612 							remoteConfig = new RemoteConfig(getConfig(),
613 									"origin"); //$NON-NLS-1$
614 						} catch (URISyntaxException e) {
615 							throw new RevisionSyntaxException(revstr);
616 						}
617 						String remoteBranchName = getConfig()
618 								.getString(
619 										ConfigConstants.CONFIG_BRANCH_SECTION,
620 								Repository.shortenRefName(ref.getName()),
621 										ConfigConstants.CONFIG_KEY_MERGE);
622 						List<RefSpec> fetchRefSpecs = remoteConfig
623 								.getFetchRefSpecs();
624 						for (RefSpec refSpec : fetchRefSpecs) {
625 							if (refSpec.matchSource(remoteBranchName)) {
626 								RefSpec expandFromSource = refSpec
627 										.expandFromSource(remoteBranchName);
628 								name = expandFromSource.getDestination();
629 								break;
630 							}
631 						}
632 						if (name == null)
633 							throw new RevisionSyntaxException(revstr);
634 					} else if (time.matches("^-\\d+$")) { //$NON-NLS-1$
635 						if (name != null)
636 							throw new RevisionSyntaxException(revstr);
637 						else {
638 							String previousCheckout = resolveReflogCheckout(-Integer
639 									.parseInt(time));
640 							if (ObjectId.isId(previousCheckout))
641 								rev = parseSimple(rw, previousCheckout);
642 							else
643 								name = previousCheckout;
644 						}
645 					} else {
646 						if (name == null)
647 							name = new String(revChars, done, i);
648 						if (name.equals("")) //$NON-NLS-1$
649 							name = Constants.HEAD;
650 						if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
651 							throw new RevisionSyntaxException(revstr);
652 						Ref ref = getRef(name);
653 						name = null;
654 						if (ref == null)
655 							return null;
656 						// @{n} means current branch, not HEAD@{1} unless
657 						// detached
658 						if (ref.isSymbolic())
659 							ref = ref.getLeaf();
660 						rev = resolveReflog(rw, ref, time);
661 					}
662 					i = m;
663 				} else
664 					throw new RevisionSyntaxException(revstr);
665 				break;
666 			case ':': {
667 				RevTree tree;
668 				if (rev == null) {
669 					if (name == null)
670 						name = new String(revChars, done, i);
671 					if (name.equals("")) //$NON-NLS-1$
672 						name = Constants.HEAD;
673 					rev = parseSimple(rw, name);
674 					name = null;
675 				}
676 				if (rev == null)
677 					return null;
678 				tree = rw.parseTree(rev);
679 				if (i == revChars.length - 1)
680 					return tree.copy();
681 
682 				TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(),
683 						new String(revChars, i + 1, revChars.length - i - 1),
684 						tree);
685 				return tw != null ? tw.getObjectId(0) : null;
686 			}
687 			default:
688 				if (rev != null)
689 					throw new RevisionSyntaxException(revstr);
690 			}
691 		}
692 		if (rev != null)
693 			return rev.copy();
694 		if (name != null)
695 			return name;
696 		if (done == revstr.length())
697 			return null;
698 		name = revstr.substring(done);
699 		if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
700 			throw new RevisionSyntaxException(revstr);
701 		if (getRef(name) != null)
702 			return name;
703 		return resolveSimple(name);
704 	}
705 
706 	private static boolean isHex(char c) {
707 		return ('0' <= c && c <= '9') //
708 				|| ('a' <= c && c <= 'f') //
709 				|| ('A' <= c && c <= 'F');
710 	}
711 
712 	private static boolean isAllHex(String str, int ptr) {
713 		while (ptr < str.length()) {
714 			if (!isHex(str.charAt(ptr++)))
715 				return false;
716 		}
717 		return true;
718 	}
719 
720 	private RevObject parseSimple(RevWalk rw, String revstr) throws IOException {
721 		ObjectId id = resolveSimple(revstr);
722 		return id != null ? rw.parseAny(id) : null;
723 	}
724 
725 	private ObjectId resolveSimple(final String revstr) throws IOException {
726 		if (ObjectId.isId(revstr))
727 			return ObjectId.fromString(revstr);
728 
729 		if (Repository.isValidRefName("x/" + revstr)) { //$NON-NLS-1$
730 			Ref r = getRefDatabase().getRef(revstr);
731 			if (r != null)
732 				return r.getObjectId();
733 		}
734 
735 		if (AbbreviatedObjectId.isId(revstr))
736 			return resolveAbbreviation(revstr);
737 
738 		int dashg = revstr.indexOf("-g"); //$NON-NLS-1$
739 		if ((dashg + 5) < revstr.length() && 0 <= dashg
740 				&& isHex(revstr.charAt(dashg + 2))
741 				&& isHex(revstr.charAt(dashg + 3))
742 				&& isAllHex(revstr, dashg + 4)) {
743 			// Possibly output from git describe?
744 			String s = revstr.substring(dashg + 2);
745 			if (AbbreviatedObjectId.isId(s))
746 				return resolveAbbreviation(s);
747 		}
748 
749 		return null;
750 	}
751 
752 	private String resolveReflogCheckout(int checkoutNo)
753 			throws IOException {
754 		ReflogReader reader = getReflogReader(Constants.HEAD);
755 		if (reader == null) {
756 			return null;
757 		}
758 		List<ReflogEntry> reflogEntries = reader.getReverseEntries();
759 		for (ReflogEntry entry : reflogEntries) {
760 			CheckoutEntry checkout = entry.parseCheckout();
761 			if (checkout != null)
762 				if (checkoutNo-- == 1)
763 					return checkout.getFromBranch();
764 		}
765 		return null;
766 	}
767 
768 	private RevCommit resolveReflog(RevWalk rw, Ref ref, String time)
769 			throws IOException {
770 		int number;
771 		try {
772 			number = Integer.parseInt(time);
773 		} catch (NumberFormatException nfe) {
774 			throw new RevisionSyntaxException(MessageFormat.format(
775 					JGitText.get().invalidReflogRevision, time));
776 		}
777 		assert number >= 0;
778 		ReflogReader reader = getReflogReader(ref.getName());
779 		if (reader == null) {
780 			throw new RevisionSyntaxException(
781 					MessageFormat.format(JGitText.get().reflogEntryNotFound,
782 							Integer.valueOf(number), ref.getName()));
783 		}
784 		ReflogEntry entry = reader.getReverseEntry(number);
785 		if (entry == null)
786 			throw new RevisionSyntaxException(MessageFormat.format(
787 					JGitText.get().reflogEntryNotFound,
788 					Integer.valueOf(number), ref.getName()));
789 
790 		return rw.parseCommit(entry.getNewId());
791 	}
792 
793 	private ObjectId resolveAbbreviation(final String revstr) throws IOException,
794 			AmbiguousObjectException {
795 		AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr);
796 		try (ObjectReader reader = newObjectReader()) {
797 			Collection<ObjectId> matches = reader.resolve(id);
798 			if (matches.size() == 0)
799 				return null;
800 			else if (matches.size() == 1)
801 				return matches.iterator().next();
802 			else
803 				throw new AmbiguousObjectException(id, matches);
804 		}
805 	}
806 
807 	/** Increment the use counter by one, requiring a matched {@link #close()}. */
808 	public void incrementOpen() {
809 		useCnt.incrementAndGet();
810 	}
811 
812 	/** Decrement the use count, and maybe close resources. */
813 	public void close() {
814 		if (useCnt.decrementAndGet() == 0) {
815 			doClose();
816 		}
817 	}
818 
819 	/**
820 	 * Invoked when the use count drops to zero during {@link #close()}.
821 	 * <p>
822 	 * The default implementation closes the object and ref databases.
823 	 */
824 	protected void doClose() {
825 		getObjectDatabase().close();
826 		getRefDatabase().close();
827 	}
828 
829 	@SuppressWarnings("nls")
830 	public String toString() {
831 		String desc;
832 		if (getDirectory() != null)
833 			desc = getDirectory().getPath();
834 		else
835 			desc = getClass().getSimpleName() + "-" //$NON-NLS-1$
836 					+ System.identityHashCode(this);
837 		return "Repository[" + desc + "]"; //$NON-NLS-1$
838 	}
839 
840 	/**
841 	 * Get the name of the reference that {@code HEAD} points to.
842 	 * <p>
843 	 * This is essentially the same as doing:
844 	 *
845 	 * <pre>
846 	 * return getRef(Constants.HEAD).getTarget().getName()
847 	 * </pre>
848 	 *
849 	 * Except when HEAD is detached, in which case this method returns the
850 	 * current ObjectId in hexadecimal string format.
851 	 *
852 	 * @return name of current branch (for example {@code refs/heads/master}),
853 	 *         an ObjectId in hex format if the current branch is detached,
854 	 *         or null if the repository is corrupt and has no HEAD reference.
855 	 * @throws IOException
856 	 */
857 	public String getFullBranch() throws IOException {
858 		Ref head = getRef(Constants.HEAD);
859 		if (head == null)
860 			return null;
861 		if (head.isSymbolic())
862 			return head.getTarget().getName();
863 		if (head.getObjectId() != null)
864 			return head.getObjectId().name();
865 		return null;
866 	}
867 
868 	/**
869 	 * Get the short name of the current branch that {@code HEAD} points to.
870 	 * <p>
871 	 * This is essentially the same as {@link #getFullBranch()}, except the
872 	 * leading prefix {@code refs/heads/} is removed from the reference before
873 	 * it is returned to the caller.
874 	 *
875 	 * @return name of current branch (for example {@code master}), an
876 	 *         ObjectId in hex format if the current branch is detached,
877 	 *         or null if the repository is corrupt and has no HEAD reference.
878 	 * @throws IOException
879 	 */
880 	public String getBranch() throws IOException {
881 		String name = getFullBranch();
882 		if (name != null)
883 			return shortenRefName(name);
884 		return name;
885 	}
886 
887 	/**
888 	 * Objects known to exist but not expressed by {@link #getAllRefs()}.
889 	 * <p>
890 	 * When a repository borrows objects from another repository, it can
891 	 * advertise that it safely has that other repository's references, without
892 	 * exposing any other details about the other repository.  This may help
893 	 * a client trying to push changes avoid pushing more than it needs to.
894 	 *
895 	 * @return unmodifiable collection of other known objects.
896 	 */
897 	public Set<ObjectId> getAdditionalHaves() {
898 		return Collections.emptySet();
899 	}
900 
901 	/**
902 	 * Get a ref by name.
903 	 *
904 	 * @param name
905 	 *            the name of the ref to lookup. May be a short-hand form, e.g.
906 	 *            "master" which is is automatically expanded to
907 	 *            "refs/heads/master" if "refs/heads/master" already exists.
908 	 * @return the Ref with the given name, or null if it does not exist
909 	 * @throws IOException
910 	 */
911 	public Ref getRef(final String name) throws IOException {
912 		return getRefDatabase().getRef(name);
913 	}
914 
915 	/**
916 	 * @return mutable map of all known refs (heads, tags, remotes).
917 	 */
918 	public Map<String, Ref> getAllRefs() {
919 		try {
920 			return getRefDatabase().getRefs(RefDatabase.ALL);
921 		} catch (IOException e) {
922 			return new HashMap<String, Ref>();
923 		}
924 	}
925 
926 	/**
927 	 * @return mutable map of all tags; key is short tag name ("v1.0") and value
928 	 *         of the entry contains the ref with the full tag name
929 	 *         ("refs/tags/v1.0").
930 	 */
931 	public Map<String, Ref> getTags() {
932 		try {
933 			return getRefDatabase().getRefs(Constants.R_TAGS);
934 		} catch (IOException e) {
935 			return new HashMap<String, Ref>();
936 		}
937 	}
938 
939 	/**
940 	 * Peel a possibly unpeeled reference to an annotated tag.
941 	 * <p>
942 	 * If the ref cannot be peeled (as it does not refer to an annotated tag)
943 	 * the peeled id stays null, but {@link Ref#isPeeled()} will be true.
944 	 *
945 	 * @param ref
946 	 *            The ref to peel
947 	 * @return <code>ref</code> if <code>ref.isPeeled()</code> is true; else a
948 	 *         new Ref object representing the same data as Ref, but isPeeled()
949 	 *         will be true and getPeeledObjectId will contain the peeled object
950 	 *         (or null).
951 	 */
952 	public Ref peel(final Ref ref) {
953 		try {
954 			return getRefDatabase().peel(ref);
955 		} catch (IOException e) {
956 			// Historical accident; if the reference cannot be peeled due
957 			// to some sort of repository access problem we claim that the
958 			// same as if the reference was not an annotated tag.
959 			return ref;
960 		}
961 	}
962 
963 	/**
964 	 * @return a map with all objects referenced by a peeled ref.
965 	 */
966 	public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
967 		Map<String, Ref> allRefs = getAllRefs();
968 		Map<AnyObjectId, Set<Ref>> ret = new HashMap<AnyObjectId, Set<Ref>>(allRefs.size());
969 		for (Ref ref : allRefs.values()) {
970 			ref = peel(ref);
971 			AnyObjectId target = ref.getPeeledObjectId();
972 			if (target == null)
973 				target = ref.getObjectId();
974 			// We assume most Sets here are singletons
975 			Set<Ref> oset = ret.put(target, Collections.singleton(ref));
976 			if (oset != null) {
977 				// that was not the case (rare)
978 				if (oset.size() == 1) {
979 					// Was a read-only singleton, we must copy to a new Set
980 					oset = new HashSet<Ref>(oset);
981 				}
982 				ret.put(target, oset);
983 				oset.add(ref);
984 			}
985 		}
986 		return ret;
987 	}
988 
989 	/**
990 	 * @return the index file location
991 	 * @throws NoWorkTreeException
992 	 *             if this is bare, which implies it has no working directory.
993 	 *             See {@link #isBare()}.
994 	 */
995 	public File getIndexFile() throws NoWorkTreeException {
996 		if (isBare())
997 			throw new NoWorkTreeException();
998 		return indexFile;
999 	}
1000 
1001 	/**
1002 	 * Create a new in-core index representation and read an index from disk.
1003 	 * <p>
1004 	 * The new index will be read before it is returned to the caller. Read
1005 	 * failures are reported as exceptions and therefore prevent the method from
1006 	 * returning a partially populated index.
1007 	 *
1008 	 * @return a cache representing the contents of the specified index file (if
1009 	 *         it exists) or an empty cache if the file does not exist.
1010 	 * @throws NoWorkTreeException
1011 	 *             if this is bare, which implies it has no working directory.
1012 	 *             See {@link #isBare()}.
1013 	 * @throws IOException
1014 	 *             the index file is present but could not be read.
1015 	 * @throws CorruptObjectException
1016 	 *             the index file is using a format or extension that this
1017 	 *             library does not support.
1018 	 */
1019 	public DirCache readDirCache() throws NoWorkTreeException,
1020 			CorruptObjectException, IOException {
1021 		return DirCache.read(this);
1022 	}
1023 
1024 	/**
1025 	 * Create a new in-core index representation, lock it, and read from disk.
1026 	 * <p>
1027 	 * The new index will be locked and then read before it is returned to the
1028 	 * caller. Read failures are reported as exceptions and therefore prevent
1029 	 * the method from returning a partially populated index.
1030 	 *
1031 	 * @return a cache representing the contents of the specified index file (if
1032 	 *         it exists) or an empty cache if the file does not exist.
1033 	 * @throws NoWorkTreeException
1034 	 *             if this is bare, which implies it has no working directory.
1035 	 *             See {@link #isBare()}.
1036 	 * @throws IOException
1037 	 *             the index file is present but could not be read, or the lock
1038 	 *             could not be obtained.
1039 	 * @throws CorruptObjectException
1040 	 *             the index file is using a format or extension that this
1041 	 *             library does not support.
1042 	 */
1043 	public DirCache lockDirCache() throws NoWorkTreeException,
1044 			CorruptObjectException, IOException {
1045 		// we want DirCache to inform us so that we can inform registered
1046 		// listeners about index changes
1047 		IndexChangedListener l = new IndexChangedListener() {
1048 
1049 			public void onIndexChanged(IndexChangedEvent event) {
1050 				notifyIndexChanged();
1051 			}
1052 		};
1053 		return DirCache.lock(this, l);
1054 	}
1055 
1056 	static byte[] gitInternalSlash(byte[] bytes) {
1057 		if (File.separatorChar == '/')
1058 			return bytes;
1059 		for (int i=0; i<bytes.length; ++i)
1060 			if (bytes[i] == File.separatorChar)
1061 				bytes[i] = '/';
1062 		return bytes;
1063 	}
1064 
1065 	/**
1066 	 * @return an important state
1067 	 */
1068 	public RepositoryState getRepositoryState() {
1069 		if (isBare() || getDirectory() == null)
1070 			return RepositoryState.BARE;
1071 
1072 		// Pre Git-1.6 logic
1073 		if (new File(getWorkTree(), ".dotest").exists()) //$NON-NLS-1$
1074 			return RepositoryState.REBASING;
1075 		if (new File(getDirectory(), ".dotest-merge").exists()) //$NON-NLS-1$
1076 			return RepositoryState.REBASING_INTERACTIVE;
1077 
1078 		// From 1.6 onwards
1079 		if (new File(getDirectory(),"rebase-apply/rebasing").exists()) //$NON-NLS-1$
1080 			return RepositoryState.REBASING_REBASING;
1081 		if (new File(getDirectory(),"rebase-apply/applying").exists()) //$NON-NLS-1$
1082 			return RepositoryState.APPLY;
1083 		if (new File(getDirectory(),"rebase-apply").exists()) //$NON-NLS-1$
1084 			return RepositoryState.REBASING;
1085 
1086 		if (new File(getDirectory(),"rebase-merge/interactive").exists()) //$NON-NLS-1$
1087 			return RepositoryState.REBASING_INTERACTIVE;
1088 		if (new File(getDirectory(),"rebase-merge").exists()) //$NON-NLS-1$
1089 			return RepositoryState.REBASING_MERGE;
1090 
1091 		// Both versions
1092 		if (new File(getDirectory(), Constants.MERGE_HEAD).exists()) {
1093 			// we are merging - now check whether we have unmerged paths
1094 			try {
1095 				if (!readDirCache().hasUnmergedPaths()) {
1096 					// no unmerged paths -> return the MERGING_RESOLVED state
1097 					return RepositoryState.MERGING_RESOLVED;
1098 				}
1099 			} catch (IOException e) {
1100 				// Can't decide whether unmerged paths exists. Return
1101 				// MERGING state to be on the safe side (in state MERGING
1102 				// you are not allow to do anything)
1103 			}
1104 			return RepositoryState.MERGING;
1105 		}
1106 
1107 		if (new File(getDirectory(), "BISECT_LOG").exists()) //$NON-NLS-1$
1108 			return RepositoryState.BISECTING;
1109 
1110 		if (new File(getDirectory(), Constants.CHERRY_PICK_HEAD).exists()) {
1111 			try {
1112 				if (!readDirCache().hasUnmergedPaths()) {
1113 					// no unmerged paths
1114 					return RepositoryState.CHERRY_PICKING_RESOLVED;
1115 				}
1116 			} catch (IOException e) {
1117 				// fall through to CHERRY_PICKING
1118 			}
1119 
1120 			return RepositoryState.CHERRY_PICKING;
1121 		}
1122 
1123 		if (new File(getDirectory(), Constants.REVERT_HEAD).exists()) {
1124 			try {
1125 				if (!readDirCache().hasUnmergedPaths()) {
1126 					// no unmerged paths
1127 					return RepositoryState.REVERTING_RESOLVED;
1128 				}
1129 			} catch (IOException e) {
1130 				// fall through to REVERTING
1131 			}
1132 
1133 			return RepositoryState.REVERTING;
1134 		}
1135 
1136 		return RepositoryState.SAFE;
1137 	}
1138 
1139 	/**
1140 	 * Check validity of a ref name. It must not contain character that has
1141 	 * a special meaning in a Git object reference expression. Some other
1142 	 * dangerous characters are also excluded.
1143 	 *
1144 	 * For portability reasons '\' is excluded
1145 	 *
1146 	 * @param refName
1147 	 *
1148 	 * @return true if refName is a valid ref name
1149 	 */
1150 	public static boolean isValidRefName(final String refName) {
1151 		final int len = refName.length();
1152 		if (len == 0)
1153 			return false;
1154 		if (refName.endsWith(".lock")) //$NON-NLS-1$
1155 			return false;
1156 
1157 		// Refs may be stored as loose files so invalid paths
1158 		// on the local system must also be invalid refs.
1159 		try {
1160 			SystemReader.getInstance().checkPath(refName);
1161 		} catch (CorruptObjectException e) {
1162 			return false;
1163 		}
1164 
1165 		int components = 1;
1166 		char p = '\0';
1167 		for (int i = 0; i < len; i++) {
1168 			final char c = refName.charAt(i);
1169 			if (c <= ' ')
1170 				return false;
1171 			switch (c) {
1172 			case '.':
1173 				switch (p) {
1174 				case '\0': case '/': case '.':
1175 					return false;
1176 				}
1177 				if (i == len -1)
1178 					return false;
1179 				break;
1180 			case '/':
1181 				if (i == 0 || i == len - 1)
1182 					return false;
1183 				if (p == '/')
1184 					return false;
1185 				components++;
1186 				break;
1187 			case '{':
1188 				if (p == '@')
1189 					return false;
1190 				break;
1191 			case '~': case '^': case ':':
1192 			case '?': case '[': case '*':
1193 			case '\\':
1194 			case '\u007F':
1195 				return false;
1196 			}
1197 			p = c;
1198 		}
1199 		return components > 1;
1200 	}
1201 
1202 	/**
1203 	 * Strip work dir and return normalized repository path.
1204 	 *
1205 	 * @param workDir Work dir
1206 	 * @param file File whose path shall be stripped of its workdir
1207 	 * @return normalized repository relative path or the empty
1208 	 *         string if the file is not relative to the work directory.
1209 	 */
1210 	public static String stripWorkDir(File workDir, File file) {
1211 		final String filePath = file.getPath();
1212 		final String workDirPath = workDir.getPath();
1213 
1214 		if (filePath.length() <= workDirPath.length() ||
1215 		    filePath.charAt(workDirPath.length()) != File.separatorChar ||
1216 		    !filePath.startsWith(workDirPath)) {
1217 			File absWd = workDir.isAbsolute() ? workDir : workDir.getAbsoluteFile();
1218 			File absFile = file.isAbsolute() ? file : file.getAbsoluteFile();
1219 			if (absWd == workDir && absFile == file)
1220 				return ""; //$NON-NLS-1$
1221 			return stripWorkDir(absWd, absFile);
1222 		}
1223 
1224 		String relName = filePath.substring(workDirPath.length() + 1);
1225 		if (File.separatorChar != '/')
1226 			relName = relName.replace(File.separatorChar, '/');
1227 		return relName;
1228 	}
1229 
1230 	/**
1231 	 * @return true if this is bare, which implies it has no working directory.
1232 	 */
1233 	public boolean isBare() {
1234 		return workTree == null;
1235 	}
1236 
1237 	/**
1238 	 * @return the root directory of the working tree, where files are checked
1239 	 *         out for viewing and editing.
1240 	 * @throws NoWorkTreeException
1241 	 *             if this is bare, which implies it has no working directory.
1242 	 *             See {@link #isBare()}.
1243 	 */
1244 	public File getWorkTree() throws NoWorkTreeException {
1245 		if (isBare())
1246 			throw new NoWorkTreeException();
1247 		return workTree;
1248 	}
1249 
1250 	/**
1251 	 * Force a scan for changed refs.
1252 	 *
1253 	 * @throws IOException
1254 	 */
1255 	public abstract void scanForRepoChanges() throws IOException;
1256 
1257 	/**
1258 	 * Notify that the index changed
1259 	 */
1260 	public abstract void notifyIndexChanged();
1261 
1262 	/**
1263 	 * @param refName
1264 	 *
1265 	 * @return a more user friendly ref name
1266 	 */
1267 	public static String shortenRefName(String refName) {
1268 		if (refName.startsWith(Constants.R_HEADS))
1269 			return refName.substring(Constants.R_HEADS.length());
1270 		if (refName.startsWith(Constants.R_TAGS))
1271 			return refName.substring(Constants.R_TAGS.length());
1272 		if (refName.startsWith(Constants.R_REMOTES))
1273 			return refName.substring(Constants.R_REMOTES.length());
1274 		return refName;
1275 	}
1276 
1277 	/**
1278 	 * @param refName
1279 	 * @return the remote branch name part of <code>refName</code>, i.e. without
1280 	 *         the <code>refs/remotes/&lt;remote&gt;</code> prefix, if
1281 	 *         <code>refName</code> represents a remote tracking branch;
1282 	 *         otherwise null.
1283 	 * @since 3.4
1284 	 */
1285 	public String shortenRemoteBranchName(String refName) {
1286 		for (String remote : getRemoteNames()) {
1287 			String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
1288 			if (refName.startsWith(remotePrefix))
1289 				return refName.substring(remotePrefix.length());
1290 		}
1291 		return null;
1292 	}
1293 
1294 	/**
1295 	 * @param refName
1296 	 * @return the remote name part of <code>refName</code>, i.e. without the
1297 	 *         <code>refs/remotes/&lt;remote&gt;</code> prefix, if
1298 	 *         <code>refName</code> represents a remote tracking branch;
1299 	 *         otherwise null.
1300 	 * @since 3.4
1301 	 */
1302 	public String getRemoteName(String refName) {
1303 		for (String remote : getRemoteNames()) {
1304 			String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
1305 			if (refName.startsWith(remotePrefix))
1306 				return remote;
1307 		}
1308 		return null;
1309 	}
1310 
1311 	/**
1312 	 * @param refName
1313 	 * @return a {@link ReflogReader} for the supplied refname, or null if the
1314 	 *         named ref does not exist.
1315 	 * @throws IOException
1316 	 *             the ref could not be accessed.
1317 	 * @since 3.0
1318 	 */
1319 	public abstract ReflogReader getReflogReader(String refName)
1320 			throws IOException;
1321 
1322 	/**
1323 	 * Return the information stored in the file $GIT_DIR/MERGE_MSG. In this
1324 	 * file operations triggering a merge will store a template for the commit
1325 	 * message of the merge commit.
1326 	 *
1327 	 * @return a String containing the content of the MERGE_MSG file or
1328 	 *         {@code null} if this file doesn't exist
1329 	 * @throws IOException
1330 	 * @throws NoWorkTreeException
1331 	 *             if this is bare, which implies it has no working directory.
1332 	 *             See {@link #isBare()}.
1333 	 */
1334 	public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
1335 		return readCommitMsgFile(Constants.MERGE_MSG);
1336 	}
1337 
1338 	/**
1339 	 * Write new content to the file $GIT_DIR/MERGE_MSG. In this file operations
1340 	 * triggering a merge will store a template for the commit message of the
1341 	 * merge commit. If <code>null</code> is specified as message the file will
1342 	 * be deleted.
1343 	 *
1344 	 * @param msg
1345 	 *            the message which should be written or <code>null</code> to
1346 	 *            delete the file
1347 	 *
1348 	 * @throws IOException
1349 	 */
1350 	public void writeMergeCommitMsg(String msg) throws IOException {
1351 		File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
1352 		writeCommitMsg(mergeMsgFile, msg);
1353 	}
1354 
1355 	/**
1356 	 * Return the information stored in the file $GIT_DIR/COMMIT_EDITMSG. In
1357 	 * this file hooks triggered by an operation may read or modify the current
1358 	 * commit message.
1359 	 *
1360 	 * @return a String containing the content of the COMMIT_EDITMSG file or
1361 	 *         {@code null} if this file doesn't exist
1362 	 * @throws IOException
1363 	 * @throws NoWorkTreeException
1364 	 *             if this is bare, which implies it has no working directory.
1365 	 *             See {@link #isBare()}.
1366 	 * @since 4.0
1367 	 */
1368 	public String readCommitEditMsg() throws IOException, NoWorkTreeException {
1369 		return readCommitMsgFile(Constants.COMMIT_EDITMSG);
1370 	}
1371 
1372 	/**
1373 	 * Write new content to the file $GIT_DIR/COMMIT_EDITMSG. In this file hooks
1374 	 * triggered by an operation may read or modify the current commit message.
1375 	 * If {@code null} is specified as message the file will be deleted.
1376 	 *
1377 	 * @param msg
1378 	 *            the message which should be written or {@code null} to delete
1379 	 *            the file
1380 	 *
1381 	 * @throws IOException
1382 	 * @since 4.0
1383 	 */
1384 	public void writeCommitEditMsg(String msg) throws IOException {
1385 		File commiEditMsgFile = new File(gitDir, Constants.COMMIT_EDITMSG);
1386 		writeCommitMsg(commiEditMsgFile, msg);
1387 	}
1388 
1389 	/**
1390 	 * Return the information stored in the file $GIT_DIR/MERGE_HEAD. In this
1391 	 * file operations triggering a merge will store the IDs of all heads which
1392 	 * should be merged together with HEAD.
1393 	 *
1394 	 * @return a list of commits which IDs are listed in the MERGE_HEAD file or
1395 	 *         {@code null} if this file doesn't exist. Also if the file exists
1396 	 *         but is empty {@code null} will be returned
1397 	 * @throws IOException
1398 	 * @throws NoWorkTreeException
1399 	 *             if this is bare, which implies it has no working directory.
1400 	 *             See {@link #isBare()}.
1401 	 */
1402 	public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException {
1403 		if (isBare() || getDirectory() == null)
1404 			throw new NoWorkTreeException();
1405 
1406 		byte[] raw = readGitDirectoryFile(Constants.MERGE_HEAD);
1407 		if (raw == null)
1408 			return null;
1409 
1410 		LinkedList<ObjectId> heads = new LinkedList<ObjectId>();
1411 		for (int p = 0; p < raw.length;) {
1412 			heads.add(ObjectId.fromString(raw, p));
1413 			p = RawParseUtils
1414 					.nextLF(raw, p + Constants.OBJECT_ID_STRING_LENGTH);
1415 		}
1416 		return heads;
1417 	}
1418 
1419 	/**
1420 	 * Write new merge-heads into $GIT_DIR/MERGE_HEAD. In this file operations
1421 	 * triggering a merge will store the IDs of all heads which should be merged
1422 	 * together with HEAD. If <code>null</code> is specified as list of commits
1423 	 * the file will be deleted
1424 	 *
1425 	 * @param heads
1426 	 *            a list of commits which IDs should be written to
1427 	 *            $GIT_DIR/MERGE_HEAD or <code>null</code> to delete the file
1428 	 * @throws IOException
1429 	 */
1430 	public void writeMergeHeads(List<? extends ObjectId> heads) throws IOException {
1431 		writeHeadsFile(heads, Constants.MERGE_HEAD);
1432 	}
1433 
1434 	/**
1435 	 * Return the information stored in the file $GIT_DIR/CHERRY_PICK_HEAD.
1436 	 *
1437 	 * @return object id from CHERRY_PICK_HEAD file or {@code null} if this file
1438 	 *         doesn't exist. Also if the file exists but is empty {@code null}
1439 	 *         will be returned
1440 	 * @throws IOException
1441 	 * @throws NoWorkTreeException
1442 	 *             if this is bare, which implies it has no working directory.
1443 	 *             See {@link #isBare()}.
1444 	 */
1445 	public ObjectId readCherryPickHead() throws IOException,
1446 			NoWorkTreeException {
1447 		if (isBare() || getDirectory() == null)
1448 			throw new NoWorkTreeException();
1449 
1450 		byte[] raw = readGitDirectoryFile(Constants.CHERRY_PICK_HEAD);
1451 		if (raw == null)
1452 			return null;
1453 
1454 		return ObjectId.fromString(raw, 0);
1455 	}
1456 
1457 	/**
1458 	 * Return the information stored in the file $GIT_DIR/REVERT_HEAD.
1459 	 *
1460 	 * @return object id from REVERT_HEAD file or {@code null} if this file
1461 	 *         doesn't exist. Also if the file exists but is empty {@code null}
1462 	 *         will be returned
1463 	 * @throws IOException
1464 	 * @throws NoWorkTreeException
1465 	 *             if this is bare, which implies it has no working directory.
1466 	 *             See {@link #isBare()}.
1467 	 */
1468 	public ObjectId readRevertHead() throws IOException, NoWorkTreeException {
1469 		if (isBare() || getDirectory() == null)
1470 			throw new NoWorkTreeException();
1471 
1472 		byte[] raw = readGitDirectoryFile(Constants.REVERT_HEAD);
1473 		if (raw == null)
1474 			return null;
1475 		return ObjectId.fromString(raw, 0);
1476 	}
1477 
1478 	/**
1479 	 * Write cherry pick commit into $GIT_DIR/CHERRY_PICK_HEAD. This is used in
1480 	 * case of conflicts to store the cherry which was tried to be picked.
1481 	 *
1482 	 * @param head
1483 	 *            an object id of the cherry commit or <code>null</code> to
1484 	 *            delete the file
1485 	 * @throws IOException
1486 	 */
1487 	public void writeCherryPickHead(ObjectId head) throws IOException {
1488 		List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
1489 				: null;
1490 		writeHeadsFile(heads, Constants.CHERRY_PICK_HEAD);
1491 	}
1492 
1493 	/**
1494 	 * Write revert commit into $GIT_DIR/REVERT_HEAD. This is used in case of
1495 	 * conflicts to store the revert which was tried to be picked.
1496 	 *
1497 	 * @param head
1498 	 *            an object id of the revert commit or <code>null</code> to
1499 	 *            delete the file
1500 	 * @throws IOException
1501 	 */
1502 	public void writeRevertHead(ObjectId head) throws IOException {
1503 		List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
1504 				: null;
1505 		writeHeadsFile(heads, Constants.REVERT_HEAD);
1506 	}
1507 
1508 	/**
1509 	 * Write original HEAD commit into $GIT_DIR/ORIG_HEAD.
1510 	 *
1511 	 * @param head
1512 	 *            an object id of the original HEAD commit or <code>null</code>
1513 	 *            to delete the file
1514 	 * @throws IOException
1515 	 */
1516 	public void writeOrigHead(ObjectId head) throws IOException {
1517 		List<ObjectId> heads = head != null ? Collections.singletonList(head)
1518 				: null;
1519 		writeHeadsFile(heads, Constants.ORIG_HEAD);
1520 	}
1521 
1522 	/**
1523 	 * Return the information stored in the file $GIT_DIR/ORIG_HEAD.
1524 	 *
1525 	 * @return object id from ORIG_HEAD file or {@code null} if this file
1526 	 *         doesn't exist. Also if the file exists but is empty {@code null}
1527 	 *         will be returned
1528 	 * @throws IOException
1529 	 * @throws NoWorkTreeException
1530 	 *             if this is bare, which implies it has no working directory.
1531 	 *             See {@link #isBare()}.
1532 	 */
1533 	public ObjectId readOrigHead() throws IOException, NoWorkTreeException {
1534 		if (isBare() || getDirectory() == null)
1535 			throw new NoWorkTreeException();
1536 
1537 		byte[] raw = readGitDirectoryFile(Constants.ORIG_HEAD);
1538 		return raw != null ? ObjectId.fromString(raw, 0) : null;
1539 	}
1540 
1541 	/**
1542 	 * Return the information stored in the file $GIT_DIR/SQUASH_MSG. In this
1543 	 * file operations triggering a squashed merge will store a template for the
1544 	 * commit message of the squash commit.
1545 	 *
1546 	 * @return a String containing the content of the SQUASH_MSG file or
1547 	 *         {@code null} if this file doesn't exist
1548 	 * @throws IOException
1549 	 * @throws NoWorkTreeException
1550 	 *             if this is bare, which implies it has no working directory.
1551 	 *             See {@link #isBare()}.
1552 	 */
1553 	public String readSquashCommitMsg() throws IOException {
1554 		return readCommitMsgFile(Constants.SQUASH_MSG);
1555 	}
1556 
1557 	/**
1558 	 * Write new content to the file $GIT_DIR/SQUASH_MSG. In this file
1559 	 * operations triggering a squashed merge will store a template for the
1560 	 * commit message of the squash commit. If <code>null</code> is specified as
1561 	 * message the file will be deleted.
1562 	 *
1563 	 * @param msg
1564 	 *            the message which should be written or <code>null</code> to
1565 	 *            delete the file
1566 	 *
1567 	 * @throws IOException
1568 	 */
1569 	public void writeSquashCommitMsg(String msg) throws IOException {
1570 		File squashMsgFile = new File(gitDir, Constants.SQUASH_MSG);
1571 		writeCommitMsg(squashMsgFile, msg);
1572 	}
1573 
1574 	private String readCommitMsgFile(String msgFilename) throws IOException {
1575 		if (isBare() || getDirectory() == null)
1576 			throw new NoWorkTreeException();
1577 
1578 		File mergeMsgFile = new File(getDirectory(), msgFilename);
1579 		try {
1580 			return RawParseUtils.decode(IO.readFully(mergeMsgFile));
1581 		} catch (FileNotFoundException e) {
1582 			// the file has disappeared in the meantime ignore it
1583 			return null;
1584 		}
1585 	}
1586 
1587 	private void writeCommitMsg(File msgFile, String msg) throws IOException {
1588 		if (msg != null) {
1589 			FileOutputStream fos = new FileOutputStream(msgFile);
1590 			try {
1591 				fos.write(msg.getBytes(Constants.CHARACTER_ENCODING));
1592 			} finally {
1593 				fos.close();
1594 			}
1595 		} else {
1596 			FileUtils.delete(msgFile, FileUtils.SKIP_MISSING);
1597 		}
1598 	}
1599 
1600 	/**
1601 	 * Read a file from the git directory.
1602 	 *
1603 	 * @param filename
1604 	 * @return the raw contents or null if the file doesn't exist or is empty
1605 	 * @throws IOException
1606 	 */
1607 	private byte[] readGitDirectoryFile(String filename) throws IOException {
1608 		File file = new File(getDirectory(), filename);
1609 		try {
1610 			byte[] raw = IO.readFully(file);
1611 			return raw.length > 0 ? raw : null;
1612 		} catch (FileNotFoundException notFound) {
1613 			return null;
1614 		}
1615 	}
1616 
1617 	/**
1618 	 * Write the given heads to a file in the git directory.
1619 	 *
1620 	 * @param heads
1621 	 *            a list of object ids to write or null if the file should be
1622 	 *            deleted.
1623 	 * @param filename
1624 	 * @throws FileNotFoundException
1625 	 * @throws IOException
1626 	 */
1627 	private void writeHeadsFile(List<? extends ObjectId> heads, String filename)
1628 			throws FileNotFoundException, IOException {
1629 		File headsFile = new File(getDirectory(), filename);
1630 		if (heads != null) {
1631 			BufferedOutputStream bos = new SafeBufferedOutputStream(
1632 					new FileOutputStream(headsFile));
1633 			try {
1634 				for (ObjectId id : heads) {
1635 					id.copyTo(bos);
1636 					bos.write('\n');
1637 				}
1638 			} finally {
1639 				bos.close();
1640 			}
1641 		} else {
1642 			FileUtils.delete(headsFile, FileUtils.SKIP_MISSING);
1643 		}
1644 	}
1645 
1646 	/**
1647 	 * Read a file formatted like the git-rebase-todo file. The "done" file is
1648 	 * also formatted like the git-rebase-todo file. These files can be found in
1649 	 * .git/rebase-merge/ or .git/rebase-append/ folders.
1650 	 *
1651 	 * @param path
1652 	 *            path to the file relative to the repository's git-dir. E.g.
1653 	 *            "rebase-merge/git-rebase-todo" or "rebase-append/done"
1654 	 * @param includeComments
1655 	 *            <code>true</code> if also comments should be reported
1656 	 * @return the list of steps
1657 	 * @throws IOException
1658 	 * @since 3.2
1659 	 */
1660 	public List<RebaseTodoLine> readRebaseTodo(String path,
1661 			boolean includeComments)
1662 			throws IOException {
1663 		return new RebaseTodoFile(this).readRebaseTodo(path, includeComments);
1664 	}
1665 
1666 	/**
1667 	 * Write a file formatted like a git-rebase-todo file.
1668 	 *
1669 	 * @param path
1670 	 *            path to the file relative to the repository's git-dir. E.g.
1671 	 *            "rebase-merge/git-rebase-todo" or "rebase-append/done"
1672 	 * @param steps
1673 	 *            the steps to be written
1674 	 * @param append
1675 	 *            whether to append to an existing file or to write a new file
1676 	 * @throws IOException
1677 	 * @since 3.2
1678 	 */
1679 	public void writeRebaseTodoFile(String path, List<RebaseTodoLine> steps,
1680 			boolean append)
1681 			throws IOException {
1682 		new RebaseTodoFile(this).writeRebaseTodoFile(path, steps, append);
1683 	}
1684 
1685 	/**
1686 	 * @return the names of all known remotes
1687 	 * @since 3.4
1688 	 */
1689 	public Set<String> getRemoteNames() {
1690 		return getConfig()
1691 				.getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION);
1692 	}
1693 }