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}) or
853 	 *         an ObjectId in hex format if the current branch is detached.
854 	 * @throws IOException
855 	 */
856 	public String getFullBranch() throws IOException {
857 		Ref head = getRef(Constants.HEAD);
858 		if (head == null)
859 			return null;
860 		if (head.isSymbolic())
861 			return head.getTarget().getName();
862 		if (head.getObjectId() != null)
863 			return head.getObjectId().name();
864 		return null;
865 	}
866 
867 	/**
868 	 * Get the short name of the current branch that {@code HEAD} points to.
869 	 * <p>
870 	 * This is essentially the same as {@link #getFullBranch()}, except the
871 	 * leading prefix {@code refs/heads/} is removed from the reference before
872 	 * it is returned to the caller.
873 	 *
874 	 * @return name of current branch (for example {@code master}), or an
875 	 *         ObjectId in hex format if the current branch is detached.
876 	 * @throws IOException
877 	 */
878 	public String getBranch() throws IOException {
879 		String name = getFullBranch();
880 		if (name != null)
881 			return shortenRefName(name);
882 		return name;
883 	}
884 
885 	/**
886 	 * Objects known to exist but not expressed by {@link #getAllRefs()}.
887 	 * <p>
888 	 * When a repository borrows objects from another repository, it can
889 	 * advertise that it safely has that other repository's references, without
890 	 * exposing any other details about the other repository.  This may help
891 	 * a client trying to push changes avoid pushing more than it needs to.
892 	 *
893 	 * @return unmodifiable collection of other known objects.
894 	 */
895 	public Set<ObjectId> getAdditionalHaves() {
896 		return Collections.emptySet();
897 	}
898 
899 	/**
900 	 * Get a ref by name.
901 	 *
902 	 * @param name
903 	 *            the name of the ref to lookup. May be a short-hand form, e.g.
904 	 *            "master" which is is automatically expanded to
905 	 *            "refs/heads/master" if "refs/heads/master" already exists.
906 	 * @return the Ref with the given name, or null if it does not exist
907 	 * @throws IOException
908 	 */
909 	public Ref getRef(final String name) throws IOException {
910 		return getRefDatabase().getRef(name);
911 	}
912 
913 	/**
914 	 * @return mutable map of all known refs (heads, tags, remotes).
915 	 */
916 	public Map<String, Ref> getAllRefs() {
917 		try {
918 			return getRefDatabase().getRefs(RefDatabase.ALL);
919 		} catch (IOException e) {
920 			return new HashMap<String, Ref>();
921 		}
922 	}
923 
924 	/**
925 	 * @return mutable map of all tags; key is short tag name ("v1.0") and value
926 	 *         of the entry contains the ref with the full tag name
927 	 *         ("refs/tags/v1.0").
928 	 */
929 	public Map<String, Ref> getTags() {
930 		try {
931 			return getRefDatabase().getRefs(Constants.R_TAGS);
932 		} catch (IOException e) {
933 			return new HashMap<String, Ref>();
934 		}
935 	}
936 
937 	/**
938 	 * Peel a possibly unpeeled reference to an annotated tag.
939 	 * <p>
940 	 * If the ref cannot be peeled (as it does not refer to an annotated tag)
941 	 * the peeled id stays null, but {@link Ref#isPeeled()} will be true.
942 	 *
943 	 * @param ref
944 	 *            The ref to peel
945 	 * @return <code>ref</code> if <code>ref.isPeeled()</code> is true; else a
946 	 *         new Ref object representing the same data as Ref, but isPeeled()
947 	 *         will be true and getPeeledObjectId will contain the peeled object
948 	 *         (or null).
949 	 */
950 	public Ref peel(final Ref ref) {
951 		try {
952 			return getRefDatabase().peel(ref);
953 		} catch (IOException e) {
954 			// Historical accident; if the reference cannot be peeled due
955 			// to some sort of repository access problem we claim that the
956 			// same as if the reference was not an annotated tag.
957 			return ref;
958 		}
959 	}
960 
961 	/**
962 	 * @return a map with all objects referenced by a peeled ref.
963 	 */
964 	public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
965 		Map<String, Ref> allRefs = getAllRefs();
966 		Map<AnyObjectId, Set<Ref>> ret = new HashMap<AnyObjectId, Set<Ref>>(allRefs.size());
967 		for (Ref ref : allRefs.values()) {
968 			ref = peel(ref);
969 			AnyObjectId target = ref.getPeeledObjectId();
970 			if (target == null)
971 				target = ref.getObjectId();
972 			// We assume most Sets here are singletons
973 			Set<Ref> oset = ret.put(target, Collections.singleton(ref));
974 			if (oset != null) {
975 				// that was not the case (rare)
976 				if (oset.size() == 1) {
977 					// Was a read-only singleton, we must copy to a new Set
978 					oset = new HashSet<Ref>(oset);
979 				}
980 				ret.put(target, oset);
981 				oset.add(ref);
982 			}
983 		}
984 		return ret;
985 	}
986 
987 	/**
988 	 * @return the index file location
989 	 * @throws NoWorkTreeException
990 	 *             if this is bare, which implies it has no working directory.
991 	 *             See {@link #isBare()}.
992 	 */
993 	public File getIndexFile() throws NoWorkTreeException {
994 		if (isBare())
995 			throw new NoWorkTreeException();
996 		return indexFile;
997 	}
998 
999 	/**
1000 	 * Create a new in-core index representation and read an index from disk.
1001 	 * <p>
1002 	 * The new index will be read before it is returned to the caller. Read
1003 	 * failures are reported as exceptions and therefore prevent the method from
1004 	 * returning a partially populated index.
1005 	 *
1006 	 * @return a cache representing the contents of the specified index file (if
1007 	 *         it exists) or an empty cache if the file does not exist.
1008 	 * @throws NoWorkTreeException
1009 	 *             if this is bare, which implies it has no working directory.
1010 	 *             See {@link #isBare()}.
1011 	 * @throws IOException
1012 	 *             the index file is present but could not be read.
1013 	 * @throws CorruptObjectException
1014 	 *             the index file is using a format or extension that this
1015 	 *             library does not support.
1016 	 */
1017 	public DirCache readDirCache() throws NoWorkTreeException,
1018 			CorruptObjectException, IOException {
1019 		return DirCache.read(this);
1020 	}
1021 
1022 	/**
1023 	 * Create a new in-core index representation, lock it, and read from disk.
1024 	 * <p>
1025 	 * The new index will be locked and then read before it is returned to the
1026 	 * caller. Read failures are reported as exceptions and therefore prevent
1027 	 * the method from returning a partially populated index.
1028 	 *
1029 	 * @return a cache representing the contents of the specified index file (if
1030 	 *         it exists) or an empty cache if the file does not exist.
1031 	 * @throws NoWorkTreeException
1032 	 *             if this is bare, which implies it has no working directory.
1033 	 *             See {@link #isBare()}.
1034 	 * @throws IOException
1035 	 *             the index file is present but could not be read, or the lock
1036 	 *             could not be obtained.
1037 	 * @throws CorruptObjectException
1038 	 *             the index file is using a format or extension that this
1039 	 *             library does not support.
1040 	 */
1041 	public DirCache lockDirCache() throws NoWorkTreeException,
1042 			CorruptObjectException, IOException {
1043 		// we want DirCache to inform us so that we can inform registered
1044 		// listeners about index changes
1045 		IndexChangedListener l = new IndexChangedListener() {
1046 
1047 			public void onIndexChanged(IndexChangedEvent event) {
1048 				notifyIndexChanged();
1049 			}
1050 		};
1051 		return DirCache.lock(this, l);
1052 	}
1053 
1054 	static byte[] gitInternalSlash(byte[] bytes) {
1055 		if (File.separatorChar == '/')
1056 			return bytes;
1057 		for (int i=0; i<bytes.length; ++i)
1058 			if (bytes[i] == File.separatorChar)
1059 				bytes[i] = '/';
1060 		return bytes;
1061 	}
1062 
1063 	/**
1064 	 * @return an important state
1065 	 */
1066 	public RepositoryState getRepositoryState() {
1067 		if (isBare() || getDirectory() == null)
1068 			return RepositoryState.BARE;
1069 
1070 		// Pre Git-1.6 logic
1071 		if (new File(getWorkTree(), ".dotest").exists()) //$NON-NLS-1$
1072 			return RepositoryState.REBASING;
1073 		if (new File(getDirectory(), ".dotest-merge").exists()) //$NON-NLS-1$
1074 			return RepositoryState.REBASING_INTERACTIVE;
1075 
1076 		// From 1.6 onwards
1077 		if (new File(getDirectory(),"rebase-apply/rebasing").exists()) //$NON-NLS-1$
1078 			return RepositoryState.REBASING_REBASING;
1079 		if (new File(getDirectory(),"rebase-apply/applying").exists()) //$NON-NLS-1$
1080 			return RepositoryState.APPLY;
1081 		if (new File(getDirectory(),"rebase-apply").exists()) //$NON-NLS-1$
1082 			return RepositoryState.REBASING;
1083 
1084 		if (new File(getDirectory(),"rebase-merge/interactive").exists()) //$NON-NLS-1$
1085 			return RepositoryState.REBASING_INTERACTIVE;
1086 		if (new File(getDirectory(),"rebase-merge").exists()) //$NON-NLS-1$
1087 			return RepositoryState.REBASING_MERGE;
1088 
1089 		// Both versions
1090 		if (new File(getDirectory(), Constants.MERGE_HEAD).exists()) {
1091 			// we are merging - now check whether we have unmerged paths
1092 			try {
1093 				if (!readDirCache().hasUnmergedPaths()) {
1094 					// no unmerged paths -> return the MERGING_RESOLVED state
1095 					return RepositoryState.MERGING_RESOLVED;
1096 				}
1097 			} catch (IOException e) {
1098 				// Can't decide whether unmerged paths exists. Return
1099 				// MERGING state to be on the safe side (in state MERGING
1100 				// you are not allow to do anything)
1101 			}
1102 			return RepositoryState.MERGING;
1103 		}
1104 
1105 		if (new File(getDirectory(), "BISECT_LOG").exists()) //$NON-NLS-1$
1106 			return RepositoryState.BISECTING;
1107 
1108 		if (new File(getDirectory(), Constants.CHERRY_PICK_HEAD).exists()) {
1109 			try {
1110 				if (!readDirCache().hasUnmergedPaths()) {
1111 					// no unmerged paths
1112 					return RepositoryState.CHERRY_PICKING_RESOLVED;
1113 				}
1114 			} catch (IOException e) {
1115 				// fall through to CHERRY_PICKING
1116 			}
1117 
1118 			return RepositoryState.CHERRY_PICKING;
1119 		}
1120 
1121 		if (new File(getDirectory(), Constants.REVERT_HEAD).exists()) {
1122 			try {
1123 				if (!readDirCache().hasUnmergedPaths()) {
1124 					// no unmerged paths
1125 					return RepositoryState.REVERTING_RESOLVED;
1126 				}
1127 			} catch (IOException e) {
1128 				// fall through to REVERTING
1129 			}
1130 
1131 			return RepositoryState.REVERTING;
1132 		}
1133 
1134 		return RepositoryState.SAFE;
1135 	}
1136 
1137 	/**
1138 	 * Check validity of a ref name. It must not contain character that has
1139 	 * a special meaning in a Git object reference expression. Some other
1140 	 * dangerous characters are also excluded.
1141 	 *
1142 	 * For portability reasons '\' is excluded
1143 	 *
1144 	 * @param refName
1145 	 *
1146 	 * @return true if refName is a valid ref name
1147 	 */
1148 	public static boolean isValidRefName(final String refName) {
1149 		final int len = refName.length();
1150 		if (len == 0)
1151 			return false;
1152 		if (refName.endsWith(".lock")) //$NON-NLS-1$
1153 			return false;
1154 
1155 		// Refs may be stored as loose files so invalid paths
1156 		// on the local system must also be invalid refs.
1157 		try {
1158 			SystemReader.getInstance().checkPath(refName);
1159 		} catch (CorruptObjectException e) {
1160 			return false;
1161 		}
1162 
1163 		int components = 1;
1164 		char p = '\0';
1165 		for (int i = 0; i < len; i++) {
1166 			final char c = refName.charAt(i);
1167 			if (c <= ' ')
1168 				return false;
1169 			switch (c) {
1170 			case '.':
1171 				switch (p) {
1172 				case '\0': case '/': case '.':
1173 					return false;
1174 				}
1175 				if (i == len -1)
1176 					return false;
1177 				break;
1178 			case '/':
1179 				if (i == 0 || i == len - 1)
1180 					return false;
1181 				if (p == '/')
1182 					return false;
1183 				components++;
1184 				break;
1185 			case '{':
1186 				if (p == '@')
1187 					return false;
1188 				break;
1189 			case '~': case '^': case ':':
1190 			case '?': case '[': case '*':
1191 			case '\\':
1192 			case '\u007F':
1193 				return false;
1194 			}
1195 			p = c;
1196 		}
1197 		return components > 1;
1198 	}
1199 
1200 	/**
1201 	 * Strip work dir and return normalized repository path.
1202 	 *
1203 	 * @param workDir Work dir
1204 	 * @param file File whose path shall be stripped of its workdir
1205 	 * @return normalized repository relative path or the empty
1206 	 *         string if the file is not relative to the work directory.
1207 	 */
1208 	public static String stripWorkDir(File workDir, File file) {
1209 		final String filePath = file.getPath();
1210 		final String workDirPath = workDir.getPath();
1211 
1212 		if (filePath.length() <= workDirPath.length() ||
1213 		    filePath.charAt(workDirPath.length()) != File.separatorChar ||
1214 		    !filePath.startsWith(workDirPath)) {
1215 			File absWd = workDir.isAbsolute() ? workDir : workDir.getAbsoluteFile();
1216 			File absFile = file.isAbsolute() ? file : file.getAbsoluteFile();
1217 			if (absWd == workDir && absFile == file)
1218 				return ""; //$NON-NLS-1$
1219 			return stripWorkDir(absWd, absFile);
1220 		}
1221 
1222 		String relName = filePath.substring(workDirPath.length() + 1);
1223 		if (File.separatorChar != '/')
1224 			relName = relName.replace(File.separatorChar, '/');
1225 		return relName;
1226 	}
1227 
1228 	/**
1229 	 * @return true if this is bare, which implies it has no working directory.
1230 	 */
1231 	public boolean isBare() {
1232 		return workTree == null;
1233 	}
1234 
1235 	/**
1236 	 * @return the root directory of the working tree, where files are checked
1237 	 *         out for viewing and editing.
1238 	 * @throws NoWorkTreeException
1239 	 *             if this is bare, which implies it has no working directory.
1240 	 *             See {@link #isBare()}.
1241 	 */
1242 	public File getWorkTree() throws NoWorkTreeException {
1243 		if (isBare())
1244 			throw new NoWorkTreeException();
1245 		return workTree;
1246 	}
1247 
1248 	/**
1249 	 * Force a scan for changed refs.
1250 	 *
1251 	 * @throws IOException
1252 	 */
1253 	public abstract void scanForRepoChanges() throws IOException;
1254 
1255 	/**
1256 	 * Notify that the index changed
1257 	 */
1258 	public abstract void notifyIndexChanged();
1259 
1260 	/**
1261 	 * @param refName
1262 	 *
1263 	 * @return a more user friendly ref name
1264 	 */
1265 	public static String shortenRefName(String refName) {
1266 		if (refName.startsWith(Constants.R_HEADS))
1267 			return refName.substring(Constants.R_HEADS.length());
1268 		if (refName.startsWith(Constants.R_TAGS))
1269 			return refName.substring(Constants.R_TAGS.length());
1270 		if (refName.startsWith(Constants.R_REMOTES))
1271 			return refName.substring(Constants.R_REMOTES.length());
1272 		return refName;
1273 	}
1274 
1275 	/**
1276 	 * @param refName
1277 	 * @return the remote branch name part of <code>refName</code>, i.e. without
1278 	 *         the <code>refs/remotes/&lt;remote&gt;</code> prefix, if
1279 	 *         <code>refName</code> represents a remote tracking branch;
1280 	 *         otherwise null.
1281 	 * @since 3.4
1282 	 */
1283 	public String shortenRemoteBranchName(String refName) {
1284 		for (String remote : getRemoteNames()) {
1285 			String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
1286 			if (refName.startsWith(remotePrefix))
1287 				return refName.substring(remotePrefix.length());
1288 		}
1289 		return null;
1290 	}
1291 
1292 	/**
1293 	 * @param refName
1294 	 * @return the remote name part of <code>refName</code>, i.e. without the
1295 	 *         <code>refs/remotes/&lt;remote&gt;</code> prefix, if
1296 	 *         <code>refName</code> represents a remote tracking branch;
1297 	 *         otherwise null.
1298 	 * @since 3.4
1299 	 */
1300 	public String getRemoteName(String refName) {
1301 		for (String remote : getRemoteNames()) {
1302 			String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
1303 			if (refName.startsWith(remotePrefix))
1304 				return remote;
1305 		}
1306 		return null;
1307 	}
1308 
1309 	/**
1310 	 * @param refName
1311 	 * @return a {@link ReflogReader} for the supplied refname, or null if the
1312 	 *         named ref does not exist.
1313 	 * @throws IOException
1314 	 *             the ref could not be accessed.
1315 	 * @since 3.0
1316 	 */
1317 	public abstract ReflogReader getReflogReader(String refName)
1318 			throws IOException;
1319 
1320 	/**
1321 	 * Return the information stored in the file $GIT_DIR/MERGE_MSG. In this
1322 	 * file operations triggering a merge will store a template for the commit
1323 	 * message of the merge commit.
1324 	 *
1325 	 * @return a String containing the content of the MERGE_MSG file or
1326 	 *         {@code null} if this file doesn't exist
1327 	 * @throws IOException
1328 	 * @throws NoWorkTreeException
1329 	 *             if this is bare, which implies it has no working directory.
1330 	 *             See {@link #isBare()}.
1331 	 */
1332 	public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
1333 		return readCommitMsgFile(Constants.MERGE_MSG);
1334 	}
1335 
1336 	/**
1337 	 * Write new content to the file $GIT_DIR/MERGE_MSG. In this file operations
1338 	 * triggering a merge will store a template for the commit message of the
1339 	 * merge commit. If <code>null</code> is specified as message the file will
1340 	 * be deleted.
1341 	 *
1342 	 * @param msg
1343 	 *            the message which should be written or <code>null</code> to
1344 	 *            delete the file
1345 	 *
1346 	 * @throws IOException
1347 	 */
1348 	public void writeMergeCommitMsg(String msg) throws IOException {
1349 		File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
1350 		writeCommitMsg(mergeMsgFile, msg);
1351 	}
1352 
1353 	/**
1354 	 * Return the information stored in the file $GIT_DIR/COMMIT_EDITMSG. In
1355 	 * this file hooks triggered by an operation may read or modify the current
1356 	 * commit message.
1357 	 *
1358 	 * @return a String containing the content of the COMMIT_EDITMSG file or
1359 	 *         {@code null} if this file doesn't exist
1360 	 * @throws IOException
1361 	 * @throws NoWorkTreeException
1362 	 *             if this is bare, which implies it has no working directory.
1363 	 *             See {@link #isBare()}.
1364 	 * @since 4.0
1365 	 */
1366 	public String readCommitEditMsg() throws IOException, NoWorkTreeException {
1367 		return readCommitMsgFile(Constants.COMMIT_EDITMSG);
1368 	}
1369 
1370 	/**
1371 	 * Write new content to the file $GIT_DIR/COMMIT_EDITMSG. In this file hooks
1372 	 * triggered by an operation may read or modify the current commit message.
1373 	 * If {@code null} is specified as message the file will be deleted.
1374 	 *
1375 	 * @param msg
1376 	 *            the message which should be written or {@code null} to delete
1377 	 *            the file
1378 	 *
1379 	 * @throws IOException
1380 	 * @since 4.0
1381 	 */
1382 	public void writeCommitEditMsg(String msg) throws IOException {
1383 		File commiEditMsgFile = new File(gitDir, Constants.COMMIT_EDITMSG);
1384 		writeCommitMsg(commiEditMsgFile, msg);
1385 	}
1386 
1387 	/**
1388 	 * Return the information stored in the file $GIT_DIR/MERGE_HEAD. In this
1389 	 * file operations triggering a merge will store the IDs of all heads which
1390 	 * should be merged together with HEAD.
1391 	 *
1392 	 * @return a list of commits which IDs are listed in the MERGE_HEAD file or
1393 	 *         {@code null} if this file doesn't exist. Also if the file exists
1394 	 *         but is empty {@code null} will be returned
1395 	 * @throws IOException
1396 	 * @throws NoWorkTreeException
1397 	 *             if this is bare, which implies it has no working directory.
1398 	 *             See {@link #isBare()}.
1399 	 */
1400 	public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException {
1401 		if (isBare() || getDirectory() == null)
1402 			throw new NoWorkTreeException();
1403 
1404 		byte[] raw = readGitDirectoryFile(Constants.MERGE_HEAD);
1405 		if (raw == null)
1406 			return null;
1407 
1408 		LinkedList<ObjectId> heads = new LinkedList<ObjectId>();
1409 		for (int p = 0; p < raw.length;) {
1410 			heads.add(ObjectId.fromString(raw, p));
1411 			p = RawParseUtils
1412 					.nextLF(raw, p + Constants.OBJECT_ID_STRING_LENGTH);
1413 		}
1414 		return heads;
1415 	}
1416 
1417 	/**
1418 	 * Write new merge-heads into $GIT_DIR/MERGE_HEAD. In this file operations
1419 	 * triggering a merge will store the IDs of all heads which should be merged
1420 	 * together with HEAD. If <code>null</code> is specified as list of commits
1421 	 * the file will be deleted
1422 	 *
1423 	 * @param heads
1424 	 *            a list of commits which IDs should be written to
1425 	 *            $GIT_DIR/MERGE_HEAD or <code>null</code> to delete the file
1426 	 * @throws IOException
1427 	 */
1428 	public void writeMergeHeads(List<? extends ObjectId> heads) throws IOException {
1429 		writeHeadsFile(heads, Constants.MERGE_HEAD);
1430 	}
1431 
1432 	/**
1433 	 * Return the information stored in the file $GIT_DIR/CHERRY_PICK_HEAD.
1434 	 *
1435 	 * @return object id from CHERRY_PICK_HEAD file or {@code null} if this file
1436 	 *         doesn't exist. Also if the file exists but is empty {@code null}
1437 	 *         will be returned
1438 	 * @throws IOException
1439 	 * @throws NoWorkTreeException
1440 	 *             if this is bare, which implies it has no working directory.
1441 	 *             See {@link #isBare()}.
1442 	 */
1443 	public ObjectId readCherryPickHead() throws IOException,
1444 			NoWorkTreeException {
1445 		if (isBare() || getDirectory() == null)
1446 			throw new NoWorkTreeException();
1447 
1448 		byte[] raw = readGitDirectoryFile(Constants.CHERRY_PICK_HEAD);
1449 		if (raw == null)
1450 			return null;
1451 
1452 		return ObjectId.fromString(raw, 0);
1453 	}
1454 
1455 	/**
1456 	 * Return the information stored in the file $GIT_DIR/REVERT_HEAD.
1457 	 *
1458 	 * @return object id from REVERT_HEAD file or {@code null} if this file
1459 	 *         doesn't exist. Also if the file exists but is empty {@code null}
1460 	 *         will be returned
1461 	 * @throws IOException
1462 	 * @throws NoWorkTreeException
1463 	 *             if this is bare, which implies it has no working directory.
1464 	 *             See {@link #isBare()}.
1465 	 */
1466 	public ObjectId readRevertHead() throws IOException, NoWorkTreeException {
1467 		if (isBare() || getDirectory() == null)
1468 			throw new NoWorkTreeException();
1469 
1470 		byte[] raw = readGitDirectoryFile(Constants.REVERT_HEAD);
1471 		if (raw == null)
1472 			return null;
1473 		return ObjectId.fromString(raw, 0);
1474 	}
1475 
1476 	/**
1477 	 * Write cherry pick commit into $GIT_DIR/CHERRY_PICK_HEAD. This is used in
1478 	 * case of conflicts to store the cherry which was tried to be picked.
1479 	 *
1480 	 * @param head
1481 	 *            an object id of the cherry commit or <code>null</code> to
1482 	 *            delete the file
1483 	 * @throws IOException
1484 	 */
1485 	public void writeCherryPickHead(ObjectId head) throws IOException {
1486 		List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
1487 				: null;
1488 		writeHeadsFile(heads, Constants.CHERRY_PICK_HEAD);
1489 	}
1490 
1491 	/**
1492 	 * Write revert commit into $GIT_DIR/REVERT_HEAD. This is used in case of
1493 	 * conflicts to store the revert which was tried to be picked.
1494 	 *
1495 	 * @param head
1496 	 *            an object id of the revert commit or <code>null</code> to
1497 	 *            delete the file
1498 	 * @throws IOException
1499 	 */
1500 	public void writeRevertHead(ObjectId head) throws IOException {
1501 		List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
1502 				: null;
1503 		writeHeadsFile(heads, Constants.REVERT_HEAD);
1504 	}
1505 
1506 	/**
1507 	 * Write original HEAD commit into $GIT_DIR/ORIG_HEAD.
1508 	 *
1509 	 * @param head
1510 	 *            an object id of the original HEAD commit or <code>null</code>
1511 	 *            to delete the file
1512 	 * @throws IOException
1513 	 */
1514 	public void writeOrigHead(ObjectId head) throws IOException {
1515 		List<ObjectId> heads = head != null ? Collections.singletonList(head)
1516 				: null;
1517 		writeHeadsFile(heads, Constants.ORIG_HEAD);
1518 	}
1519 
1520 	/**
1521 	 * Return the information stored in the file $GIT_DIR/ORIG_HEAD.
1522 	 *
1523 	 * @return object id from ORIG_HEAD file or {@code null} if this file
1524 	 *         doesn't exist. Also if the file exists but is empty {@code null}
1525 	 *         will be returned
1526 	 * @throws IOException
1527 	 * @throws NoWorkTreeException
1528 	 *             if this is bare, which implies it has no working directory.
1529 	 *             See {@link #isBare()}.
1530 	 */
1531 	public ObjectId readOrigHead() throws IOException, NoWorkTreeException {
1532 		if (isBare() || getDirectory() == null)
1533 			throw new NoWorkTreeException();
1534 
1535 		byte[] raw = readGitDirectoryFile(Constants.ORIG_HEAD);
1536 		return raw != null ? ObjectId.fromString(raw, 0) : null;
1537 	}
1538 
1539 	/**
1540 	 * Return the information stored in the file $GIT_DIR/SQUASH_MSG. In this
1541 	 * file operations triggering a squashed merge will store a template for the
1542 	 * commit message of the squash commit.
1543 	 *
1544 	 * @return a String containing the content of the SQUASH_MSG file or
1545 	 *         {@code null} if this file doesn't exist
1546 	 * @throws IOException
1547 	 * @throws NoWorkTreeException
1548 	 *             if this is bare, which implies it has no working directory.
1549 	 *             See {@link #isBare()}.
1550 	 */
1551 	public String readSquashCommitMsg() throws IOException {
1552 		return readCommitMsgFile(Constants.SQUASH_MSG);
1553 	}
1554 
1555 	/**
1556 	 * Write new content to the file $GIT_DIR/SQUASH_MSG. In this file
1557 	 * operations triggering a squashed merge will store a template for the
1558 	 * commit message of the squash commit. If <code>null</code> is specified as
1559 	 * message the file will be deleted.
1560 	 *
1561 	 * @param msg
1562 	 *            the message which should be written or <code>null</code> to
1563 	 *            delete the file
1564 	 *
1565 	 * @throws IOException
1566 	 */
1567 	public void writeSquashCommitMsg(String msg) throws IOException {
1568 		File squashMsgFile = new File(gitDir, Constants.SQUASH_MSG);
1569 		writeCommitMsg(squashMsgFile, msg);
1570 	}
1571 
1572 	private String readCommitMsgFile(String msgFilename) throws IOException {
1573 		if (isBare() || getDirectory() == null)
1574 			throw new NoWorkTreeException();
1575 
1576 		File mergeMsgFile = new File(getDirectory(), msgFilename);
1577 		try {
1578 			return RawParseUtils.decode(IO.readFully(mergeMsgFile));
1579 		} catch (FileNotFoundException e) {
1580 			// the file has disappeared in the meantime ignore it
1581 			return null;
1582 		}
1583 	}
1584 
1585 	private void writeCommitMsg(File msgFile, String msg) throws IOException {
1586 		if (msg != null) {
1587 			FileOutputStream fos = new FileOutputStream(msgFile);
1588 			try {
1589 				fos.write(msg.getBytes(Constants.CHARACTER_ENCODING));
1590 			} finally {
1591 				fos.close();
1592 			}
1593 		} else {
1594 			FileUtils.delete(msgFile, FileUtils.SKIP_MISSING);
1595 		}
1596 	}
1597 
1598 	/**
1599 	 * Read a file from the git directory.
1600 	 *
1601 	 * @param filename
1602 	 * @return the raw contents or null if the file doesn't exist or is empty
1603 	 * @throws IOException
1604 	 */
1605 	private byte[] readGitDirectoryFile(String filename) throws IOException {
1606 		File file = new File(getDirectory(), filename);
1607 		try {
1608 			byte[] raw = IO.readFully(file);
1609 			return raw.length > 0 ? raw : null;
1610 		} catch (FileNotFoundException notFound) {
1611 			return null;
1612 		}
1613 	}
1614 
1615 	/**
1616 	 * Write the given heads to a file in the git directory.
1617 	 *
1618 	 * @param heads
1619 	 *            a list of object ids to write or null if the file should be
1620 	 *            deleted.
1621 	 * @param filename
1622 	 * @throws FileNotFoundException
1623 	 * @throws IOException
1624 	 */
1625 	private void writeHeadsFile(List<? extends ObjectId> heads, String filename)
1626 			throws FileNotFoundException, IOException {
1627 		File headsFile = new File(getDirectory(), filename);
1628 		if (heads != null) {
1629 			BufferedOutputStream bos = new SafeBufferedOutputStream(
1630 					new FileOutputStream(headsFile));
1631 			try {
1632 				for (ObjectId id : heads) {
1633 					id.copyTo(bos);
1634 					bos.write('\n');
1635 				}
1636 			} finally {
1637 				bos.close();
1638 			}
1639 		} else {
1640 			FileUtils.delete(headsFile, FileUtils.SKIP_MISSING);
1641 		}
1642 	}
1643 
1644 	/**
1645 	 * Read a file formatted like the git-rebase-todo file. The "done" file is
1646 	 * also formatted like the git-rebase-todo file. These files can be found in
1647 	 * .git/rebase-merge/ or .git/rebase-append/ folders.
1648 	 *
1649 	 * @param path
1650 	 *            path to the file relative to the repository's git-dir. E.g.
1651 	 *            "rebase-merge/git-rebase-todo" or "rebase-append/done"
1652 	 * @param includeComments
1653 	 *            <code>true</code> if also comments should be reported
1654 	 * @return the list of steps
1655 	 * @throws IOException
1656 	 * @since 3.2
1657 	 */
1658 	public List<RebaseTodoLine> readRebaseTodo(String path,
1659 			boolean includeComments)
1660 			throws IOException {
1661 		return new RebaseTodoFile(this).readRebaseTodo(path, includeComments);
1662 	}
1663 
1664 	/**
1665 	 * Write a file formatted like a git-rebase-todo file.
1666 	 *
1667 	 * @param path
1668 	 *            path to the file relative to the repository's git-dir. E.g.
1669 	 *            "rebase-merge/git-rebase-todo" or "rebase-append/done"
1670 	 * @param steps
1671 	 *            the steps to be written
1672 	 * @param append
1673 	 *            whether to append to an existing file or to write a new file
1674 	 * @throws IOException
1675 	 * @since 3.2
1676 	 */
1677 	public void writeRebaseTodoFile(String path, List<RebaseTodoLine> steps,
1678 			boolean append)
1679 			throws IOException {
1680 		new RebaseTodoFile(this).writeRebaseTodoFile(path, steps, append);
1681 	}
1682 
1683 	/**
1684 	 * @return the names of all known remotes
1685 	 * @since 3.4
1686 	 */
1687 	public Set<String> getRemoteNames() {
1688 		return getConfig()
1689 				.getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION);
1690 	}
1691 }