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