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