summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/.settings/.api_filters30
-rw-r--r--org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF52
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java69
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java41
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java44
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java27
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java133
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java91
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java110
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java155
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java144
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java16
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java202
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java81
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java55
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java19
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java38
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java81
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java133
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java31
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java82
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java37
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java180
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java63
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java87
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java81
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java37
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java34
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java28
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java36
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java164
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java31
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java24
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java26
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java35
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java28
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java44
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java66
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java379
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java191
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java91
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java31
76 files changed, 3100 insertions, 445 deletions
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index ed330b1517..b2a8f677f3 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -1,5 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component id="org.eclipse.jgit" version="2">
+ <resource path="src/org/eclipse/jgit/attributes/AttributesNode.java" type="org.eclipse.jgit.attributes.AttributesNode">
+ <filter comment="attributes weren't really usable in earlier versions" id="338792546">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.attributes.AttributesNode"/>
+ <message_argument value="getAttributes(String, boolean, Map&lt;String,Attribute&gt;)"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="src/org/eclipse/jgit/lib/BitmapIndex.java" type="org.eclipse.jgit.lib.BitmapIndex$BitmapBuilder">
<filter comment="interface is implemented by extenders but not clients of the API" id="403804204">
<message_arguments>
@@ -14,6 +22,14 @@
</message_arguments>
</filter>
</resource>
+ <resource path="src/org/eclipse/jgit/lib/Repository.java" type="org.eclipse.jgit.lib.Repository">
+ <filter comment="Only implementors of Repository are affected. That should be allowed" id="336695337">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.Repository"/>
+ <message_argument value="createAttributesNodeProvider()"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="src/org/eclipse/jgit/transport/PushCertificate.java" type="org.eclipse.jgit.transport.PushCertificate">
<filter comment="PushCertificate wasn't really usable in 4.0" id="338722907">
<message_arguments>
@@ -35,6 +51,20 @@
</message_arguments>
</filter>
</resource>
+ <resource path="src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java" type="org.eclipse.jgit.treewalk.WorkingTreeIterator">
+ <filter comment="attributes weren't really usable in earlier versions" id="338792546">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.treewalk.WorkingTreeIterator"/>
+ <message_argument value="getGlobalAttributesNode()"/>
+ </message_arguments>
+ </filter>
+ <filter comment="attributes weren't really usable in earlier versions" id="338792546">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.treewalk.WorkingTreeIterator"/>
+ <message_argument value="getInfoAttributesNode()"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="src/org/eclipse/jgit/util/FileUtils.java" type="org.eclipse.jgit.util.FileUtils">
<filter id="338792546">
<message_arguments>
diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
index 195987db64..45d6d2c4c0 100644
--- a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
@@ -2,7 +2,7 @@ eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled
org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jgit.annotations.NonNull
-org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jgit.annotations.NonByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jgit.annotations.NonNullByDefault
org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jgit.annotations.Nullable
org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index b6899b2801..2a953b559d 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -6,7 +6,8 @@ Bundle-Version: 4.2.0.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.api;version="4.2.0";
+Export-Package: org.eclipse.jgit.annotations;version="4.2.0",
+ org.eclipse.jgit.api;version="4.2.0";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff,
@@ -20,9 +21,7 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0";
org.eclipse.jgit.submodule,
org.eclipse.jgit.transport,
org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="4.2.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.errors",
+ org.eclipse.jgit.api.errors;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
org.eclipse.jgit.attributes;version="4.2.0",
org.eclipse.jgit.blame;version="4.2.0";
uses:="org.eclipse.jgit.lib,
@@ -47,8 +46,7 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0";
org.eclipse.jgit.internal.storage.pack,
org.eclipse.jgit.transport,
org.eclipse.jgit.dircache",
- org.eclipse.jgit.events;version="4.2.0";
- uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.events;version="4.2.0";uses:="org.eclipse.jgit.lib",
org.eclipse.jgit.fnmatch;version="4.2.0",
org.eclipse.jgit.gitrepo;version="4.2.0";
uses:="org.eclipse.jgit.api,
@@ -57,14 +55,11 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0";
org.xml.sax.helpers,
org.xml.sax",
org.eclipse.jgit.gitrepo.internal;version="4.2.0";x-internal:=true,
- org.eclipse.jgit.hooks;version="4.2.0";
- uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.hooks;version="4.2.0";uses:="org.eclipse.jgit.lib",
org.eclipse.jgit.ignore;version="4.2.0",
org.eclipse.jgit.ignore.internal;version="4.2.0";x-friends:="org.eclipse.jgit.test",
org.eclipse.jgit.internal;version="4.2.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.storage.dfs;version="4.2.0";
- x-friends:="org.eclipse.jgit.test,
- org.eclipse.jgit.http.server",
+ org.eclipse.jgit.internal.storage.dfs;version="4.2.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.server",
org.eclipse.jgit.internal.storage.file;version="4.2.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.junit,
@@ -96,31 +91,18 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0";
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="4.2.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="4.2.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.patch;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
org.eclipse.jgit.revwalk;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff,
org.eclipse.jgit.revwalk.filter",
- org.eclipse.jgit.revwalk.filter;version="4.2.0";
- uses:="org.eclipse.jgit.revwalk,
- org.eclipse.jgit.lib,
- org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="4.2.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="4.2.0";
- uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="4.2.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.treewalk.filter,
- org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.revwalk.filter;version="4.2.0";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="4.2.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
org.eclipse.jgit.transport;version="4.2.0";
uses:="org.eclipse.jgit.transport.resolver,
org.eclipse.jgit.revwalk,
@@ -133,11 +115,8 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0";
org.eclipse.jgit.transport.http,
org.eclipse.jgit.errors,
org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="4.2.0";
- uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="4.2.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.transport",
+ org.eclipse.jgit.transport.http;version="4.2.0";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
org.eclipse.jgit.treewalk;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
@@ -145,8 +124,7 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0";
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util,
org.eclipse.jgit.dircache",
- org.eclipse.jgit.treewalk.filter;version="4.2.0";
- uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.treewalk.filter;version="4.2.0";uses:="org.eclipse.jgit.treewalk",
org.eclipse.jgit.util;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.transport.http,
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index 51e44fd778..0e9b0b59e6 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -20,6 +20,7 @@ argumentIsNotAValidCommentString=Invalid comment: {0}
atLeastOnePathIsRequired=At least one path is required.
atLeastOnePatternIsRequired=At least one pattern is required.
atLeastTwoFiltersNeeded=At least two filters needed.
+atomicPushNotSupported=Atomic push not supported.
authenticationNotSupported=authentication not supported
badBase64InputCharacterAt=Bad Base64 input character at {0} : {1} (decimal)
badEntryDelimiter=Bad entry delimiter
@@ -45,6 +46,7 @@ cannotBeCombined=Cannot be combined.
cannotBeRecursiveWhenTreesAreIncluded=TreeWalk shouldn't be recursive when tree objects are included.
cannotChangeActionOnComment=Cannot change action on comment line in git-rebase-todo file, old action: {0}, new action: {1}.
cannotChangeToComment=Cannot change a non-comment line to a comment line.
+cannotCheckoutFromUnbornBranch=Cannot checkout from unborn branch
cannotCheckoutOursSwitchBranch=Checking out ours/theirs is only possible when checking out index, not when switching branches.
cannotCombineSquashWithNoff=Cannot combine --squash with --no-ff.
cannotCombineTreeFilterWithRevFilter=Cannot combine TreeFilter {0} with RevFilter {1}.
@@ -87,6 +89,7 @@ cannotReadBlob=Cannot read blob {0}
cannotReadCommit=Cannot read commit {0}
cannotReadFile=Cannot read file {0}
cannotReadHEAD=cannot read HEAD: {0} {1}
+cannotReadIndex=The index file {0} exists but cannot be read
cannotReadObject=Cannot read object
cannotReadObjectsPath=Cannot read {0}/{1}: {2}
cannotReadTree=Cannot read tree {0}
@@ -279,6 +282,8 @@ fileCannotBeDeleted=File cannot be deleted: {0}
fileIsTooBigForThisConvenienceMethod=File is too big for this convenience method ({0} bytes).
fileIsTooLarge=File is too large: {0}
fileModeNotSetForPath=FileMode not set for path {0}
+filterExecutionFailed=Execution of filter command ''{0}'' on file ''{1}'' failed
+filterExecutionFailedRc=Execution of filter command ''{0}'' on file ''{1}'' failed with return code ''{2}'', message on stderr: ''{3}''
findingGarbage=Finding garbage
flagIsDisposed={0} is disposed.
flagNotFromThis={0} not from this.
@@ -344,6 +349,7 @@ invalidPathReservedOnWindows=Invalid path (''{0}'' is reserved on Windows): {1}
invalidReflogRevision=Invalid reflog revision: {0}
invalidRefName=Invalid ref name: {0}
invalidRemote=Invalid remote: {0}
+invalidRepositoryStateNoHead=Invalid repository --- cannot read HEAD
invalidShallowObject=invalid shallow object {0}, expected commit
invalidStageForPath=Invalid stage {0} for path {1}
invalidTagOption=Invalid tag option: {0}
@@ -450,6 +456,7 @@ packfileIsTruncated=Packfile {0} is truncated.
packfileIsTruncatedNoParam=Packfile is truncated.
packHandleIsStale=Pack file {0} handle is stale, removing it from pack list
packHasUnresolvedDeltas=pack has unresolved deltas
+packInaccessible=Pack file {0} now inaccessible; removing it from pack list
packingCancelledDuringObjectsWriting=Packing cancelled during objects writing
packObjectCountMismatch=Pack object count mismatch: pack {0} index {1}: {2}
packRefs=Pack refs
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java
new file mode 100644
index 0000000000..08f81cb9f8
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015, Andrey Loskutov <loskutov@gmx.de>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * JGit's replacement for the {@code javax.annotation.Nonnull}.
+ * <p>
+ * Denotes that a local variable, parameter, field, method return value expected
+ * to be non {@code null}.
+ *
+ * @since 4.2
+ */
+@Documented
+@Retention(RetentionPolicy.CLASS)
+@Target({ FIELD, METHOD, PARAMETER, LOCAL_VARIABLE })
+public @interface NonNull {
+ // marker annotation with no members
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java
index 254920e7a3..7b9156710f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java
@@ -54,13 +54,46 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * JGit's replacement for the {@code javax.annotations.Nullable}.
+ * Marks types that can hold the value {@code null} at run time.
* <p>
- * Denotes that a local variable, parameter, field, method return value can be
- * {@code null}.
+ * Unlike {@code org.eclipse.jdt.annotation.Nullable}, this has run-time
+ * retention, allowing the annotation to be recognized by
+ * <a href="https://github.com/google/guice/wiki/UseNullable">Guice</a>. Unlike
+ * {@code javax.annotation.Nullable}, this does not involve importing new classes
+ * to a standard (Java EE) package, so it can be deployed in an OSGi container
+ * without running into
+ * <a href="http://wiki.osgi.org/wiki/Split_Packages">split-package</a>
+ * <a href="https://gerrit-review.googlesource.com/50112">problems</a>.
+ * <p>
+ * You can use this annotation to qualify a type in a method signature or local
+ * variable declaration. The entity whose type has this annotation is allowed to
+ * hold the value {@code null} at run time. This allows annotation based null
+ * analysis to infer that
+ * <ul>
+ * <li>Binding a {@code null} value to the entity is legal.
+ * <li>Dereferencing the entity is unsafe and can trigger a
+ * {@code NullPointerException}.
+ * </ul>
+ * <p>
+ * To avoid a dependency on Java 8, this annotation does not use
+ * {@link Target @Target} {@code TYPE_USE}. That may change when JGit starts
+ * requiring Java 8.
+ * <p>
+ * <b>Warning:</b> Please do not use this annotation on arrays. Different
+ * annotation processors treat {@code @Nullable Object[]} differently: some
+ * treat it as an array of nullable objects, for consistency with versions of
+ * {@code Nullable} defined with {@code @Target} {@code TYPE_USE}, while others
+ * treat it as a nullable array of objects. JGit therefore avoids using this
+ * annotation on arrays altogether.
+ *
+ * @see <a href=
+ * "http://types.cs.washington.edu/checker-framework/current/checker-framework-manual.html#faq-array-syntax-meaning">
+ * The checker-framework manual</a>
+ *
+ * @since 4.2
*/
@Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.RUNTIME)
@Target({ FIELD, METHOD, PARAMETER, LOCAL_VARIABLE })
public @interface Nullable {
// marker annotation with no members
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
index de6c32a808..67fb342fe2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
@@ -48,6 +48,7 @@ import java.io.InputStream;
import java.util.Collection;
import java.util.LinkedList;
+import org.eclipse.jgit.api.errors.FilterFailedException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
@@ -63,6 +64,7 @@ import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
@@ -139,6 +141,7 @@ public class AddCommand extends GitCommand<DirCache> {
try (ObjectInserter inserter = repo.newObjectInserter();
final TreeWalk tw = new TreeWalk(repo)) {
+ tw.setOperationType(OperationType.CHECKIN_OP);
dc = repo.lockDirCache();
DirCacheIterator c;
@@ -146,6 +149,7 @@ public class AddCommand extends GitCommand<DirCache> {
tw.addTree(new DirCacheBuildIterator(builder));
if (workingTreeIterator == null)
workingTreeIterator = new FileTreeIterator(repo);
+ workingTreeIterator.setDirCacheIterator(tw, 0);
tw.addTree(workingTreeIterator);
tw.setRecursive(true);
if (!addAll)
@@ -208,6 +212,9 @@ public class AddCommand extends GitCommand<DirCache> {
builder.commit();
setCallable(false);
} catch (IOException e) {
+ Throwable cause = e.getCause();
+ if (cause != null && cause instanceof FilterFailedException)
+ throw (FilterFailedException) cause;
throw new JGitInternalException(
JGitText.get().exceptionCaughtDuringExecutionOfAddCommand, e);
} finally {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
index 8d85bfcb15..8743ea9ac7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -222,6 +222,12 @@ public class CheckoutCommand extends GitCommand<Ref> {
}
Ref headRef = repo.getRef(Constants.HEAD);
+ if (headRef == null) {
+ // TODO Git CLI supports checkout from unborn branch, we should
+ // also allow this
+ throw new UnsupportedOperationException(
+ JGitText.get().cannotCheckoutFromUnbornBranch);
+ }
String shortHeadRef = getShortBranchName(headRef);
String refLogMessage = "checkout: moving from " + shortHeadRef; //$NON-NLS-1$
ObjectId branch;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
index 6174d48d3a..9466dab74e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -86,6 +86,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.util.ChangeIdUtil;
/**
@@ -328,9 +329,12 @@ public class CommitCommand extends GitCommand<RevCommit> {
boolean emptyCommit = true;
try (TreeWalk treeWalk = new TreeWalk(repo)) {
+ treeWalk.setOperationType(OperationType.CHECKIN_OP);
int dcIdx = treeWalk
.addTree(new DirCacheBuildIterator(existingBuilder));
- int fIdx = treeWalk.addTree(new FileTreeIterator(repo));
+ FileTreeIterator fti = new FileTreeIterator(repo);
+ fti.setDirCacheIterator(treeWalk, 0);
+ int fIdx = treeWalk.addTree(fti);
int hIdx = -1;
if (headId != null)
hIdx = treeWalk.addTree(rw.parseTree(headId));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
index addca4c469..2cd5f59a71 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -713,8 +713,48 @@ public class Git implements AutoCloseable {
}
/**
- * @return the git repository this class is interacting with; see {@link
- * #close()} for notes on closing this repository.
+ * Return a command used to list the available remotes.
+ *
+ * @return a {@link RemoteListCommand}
+ * @since 4.2
+ */
+ public RemoteListCommand remoteList() {
+ return new RemoteListCommand(repo);
+ }
+
+ /**
+ * Return a command used to add a new remote.
+ *
+ * @return a {@link RemoteAddCommand}
+ * @since 4.2
+ */
+ public RemoteAddCommand remoteAdd() {
+ return new RemoteAddCommand(repo);
+ }
+
+ /**
+ * Return a command used to remove an existing remote.
+ *
+ * @return a {@link RemoteRemoveCommand}
+ * @since 4.2
+ */
+ public RemoteRemoveCommand remoteRemove() {
+ return new RemoteRemoveCommand(repo);
+ }
+
+ /**
+ * Return a command used to change the URL of an existing remote.
+ *
+ * @return a {@link RemoteSetUrlCommand}
+ * @since 4.2
+ */
+ public RemoteSetUrlCommand remoteSetUrl() {
+ return new RemoteSetUrlCommand(repo);
+ }
+
+ /**
+ * @return the git repository this class is interacting with; see
+ * {@link #close()} for notes on closing this repository.
*/
public Repository getRepository() {
return repo;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
index 227e32236d..f5b82bdd7d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
@@ -89,9 +89,8 @@ public class PushCommand extends
private String receivePack = RemoteConfig.DEFAULT_RECEIVE_PACK;
private boolean dryRun;
-
+ private boolean atomic;
private boolean force;
-
private boolean thin = Transport.DEFAULT_PUSH_THIN;
private OutputStream out;
@@ -145,6 +144,7 @@ public class PushCommand extends
transports = Transport.openAll(repo, remote, Transport.Operation.PUSH);
for (final Transport transport : transports) {
transport.setPushThin(thin);
+ transport.setPushAtomic(atomic);
if (receivePack != null)
transport.setOptionReceivePack(receivePack);
transport.setDryRun(dryRun);
@@ -397,6 +397,29 @@ public class PushCommand extends
}
/**
+ * @return true if all-or-nothing behavior is requested.
+ * @since 4.2
+ */
+ public boolean isAtomic() {
+ return atomic;
+ }
+
+ /**
+ * Requests atomic push (all references updated, or no updates).
+ *
+ * Default setting is false.
+ *
+ * @param atomic
+ * @return {@code this}
+ * @since 4.2
+ */
+ public PushCommand setAtomic(boolean atomic) {
+ checkCallable();
+ this.atomic = atomic;
+ return this;
+ }
+
+ /**
* @return the force preference for push operation
*/
public boolean isForce() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index ff29008420..8582bbb0dc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -668,12 +668,13 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
private void writeRewrittenHashes() throws RevisionSyntaxException,
- IOException {
+ IOException, RefNotFoundException {
File currentCommitFile = rebaseState.getFile(CURRENT_COMMIT);
if (!currentCommitFile.exists())
return;
- String head = repo.resolve(Constants.HEAD).getName();
+ ObjectId headId = getHead().getObjectId();
+ String head = headId.getName();
String currentCommits = rebaseState.readFile(CURRENT_COMMIT);
for (String current : currentCommits.split("\n")) //$NON-NLS-1$
RebaseState
@@ -743,8 +744,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private void resetSoftToParent() throws IOException,
GitAPIException, CheckoutConflictException {
- Ref orig_head = repo.getRef(Constants.ORIG_HEAD);
- ObjectId orig_headId = orig_head.getObjectId();
+ Ref ref = repo.getRef(Constants.ORIG_HEAD);
+ ObjectId orig_head = ref == null ? null : ref.getObjectId();
try {
// we have already commited the cherry-picked commit.
// what we need is to have changes introduced by this
@@ -755,7 +756,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
} finally {
// set ORIG_HEAD back to where we started because soft
// reset moved it
- repo.writeOrigHead(orig_headId);
+ repo.writeOrigHead(orig_head);
}
}
@@ -980,6 +981,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
try {
raw = IO.readFully(authorScriptFile);
} catch (FileNotFoundException notFound) {
+ if (authorScriptFile.exists()) {
+ throw notFound;
+ }
return null;
}
return parseAuthor(raw);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java
new file mode 100644
index 0000000000..679566903f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * Used to add a new remote.
+ *
+ * This class has setters for all supported options and arguments of this
+ * command and a {@link #call()} method to finally execute the command.
+ *
+ * @see <a href=
+ * "http://www.kernel.org/pub/software/scm/git/docs/git-remote.html" > Git
+ * documentation about Remote</a>
+ *
+ * @since 4.2
+ */
+public class RemoteAddCommand extends GitCommand<RemoteConfig> {
+
+ private String name;
+
+ private URIish uri;
+
+ /**
+ * @param repo
+ */
+ protected RemoteAddCommand(Repository repo) {
+ super(repo);
+ }
+
+ /**
+ * The name of the remote to add.
+ *
+ * @param name
+ * a remote name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * The URL of the repository for the new remote.
+ *
+ * @param uri
+ * an URL for the remote
+ */
+ public void setUri(URIish uri) {
+ this.uri = uri;
+ }
+
+ /**
+ * Executes the {@code remote add} command with all the options and
+ * parameters collected by the setter methods of this class.
+ *
+ * @return the {@link RemoteConfig} object of the added remote
+ */
+ @Override
+ public RemoteConfig call() throws GitAPIException {
+ checkCallable();
+
+ try {
+ StoredConfig config = repo.getConfig();
+ RemoteConfig remote = new RemoteConfig(config, name);
+
+ RefSpec refSpec = new RefSpec();
+ refSpec = refSpec.setForceUpdate(true);
+ refSpec = refSpec.setSourceDestination(Constants.R_HEADS + "*", //$NON-NLS-1$
+ Constants.R_REMOTES + name + "/*"); //$NON-NLS-1$
+ remote.addFetchRefSpec(refSpec);
+
+ remote.addURI(uri);
+
+ remote.update(config);
+ config.save();
+ return remote;
+ } catch (IOException | URISyntaxException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java
new file mode 100644
index 0000000000..f778eaa28c
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.RemoteConfig;
+
+/**
+ * Used to obtain the list of remotes.
+ *
+ * This class has setters for all supported options and arguments of this
+ * command and a {@link #call()} method to finally execute the command.
+ *
+ * @see <a href=
+ * "http://www.kernel.org/pub/software/scm/git/docs/git-remote.html" > Git
+ * documentation about Remote</a>
+ *
+ * @since 4.2
+ */
+public class RemoteListCommand extends GitCommand<List<RemoteConfig>> {
+
+ /**
+ * @param repo
+ */
+ protected RemoteListCommand(Repository repo) {
+ super(repo);
+ }
+
+ /**
+ * Executes the {@code remote} command with all the options and parameters
+ * collected by the setter methods of this class.
+ *
+ * @return a list of {@link RemoteConfig} objects.
+ */
+ @Override
+ public List<RemoteConfig> call() throws GitAPIException {
+ checkCallable();
+
+ try {
+ return RemoteConfig.getAllRemoteConfigs(repo.getConfig());
+ } catch (URISyntaxException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
new file mode 100644
index 0000000000..5782bf61b5
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.RemoteConfig;
+
+/**
+ * Used to remove an existing remote.
+ *
+ * This class has setters for all supported options and arguments of this
+ * command and a {@link #call()} method to finally execute the command.
+ *
+ * @see <a href=
+ * "http://www.kernel.org/pub/software/scm/git/docs/git-remote.html" > Git
+ * documentation about Remote</a>
+ *
+ * @since 4.2
+ */
+public class RemoteRemoveCommand extends GitCommand<RemoteConfig> {
+
+ private String name;
+
+ /**
+ * @param repo
+ */
+ protected RemoteRemoveCommand(Repository repo) {
+ super(repo);
+ }
+
+ /**
+ * The name of the remote to remove.
+ *
+ * @param name
+ * a remote name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Executes the {@code remote} command with all the options and parameters
+ * collected by the setter methods of this class.
+ *
+ * @return the {@link RemoteConfig} object of the removed remote
+ */
+ @Override
+ public RemoteConfig call() throws GitAPIException {
+ checkCallable();
+
+ try {
+ StoredConfig config = repo.getConfig();
+ RemoteConfig remote = new RemoteConfig(config, name);
+ config.unsetSection(ConfigConstants.CONFIG_KEY_REMOTE, name);
+ config.save();
+ return remote;
+ } catch (IOException | URISyntaxException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
new file mode 100644
index 0000000000..6bd2ac7993
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * Used to to change the URL of a remote.
+ *
+ * This class has setters for all supported options and arguments of this
+ * command and a {@link #call()} method to finally execute the command.
+ *
+ * @see <a href=
+ * "http://www.kernel.org/pub/software/scm/git/docs/git-remote.html" > Git
+ * documentation about Remote</a>
+ *
+ * @since 4.2
+ */
+public class RemoteSetUrlCommand extends GitCommand<RemoteConfig> {
+
+ private String name;
+
+ private URIish uri;
+
+ private boolean push;
+
+ /**
+ * @param repo
+ */
+ protected RemoteSetUrlCommand(Repository repo) {
+ super(repo);
+ }
+
+ /**
+ * The name of the remote to change the URL for.
+ *
+ * @param name
+ * a remote name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * The new URL for the remote.
+ *
+ * @param uri
+ * an URL for the remote
+ */
+ public void setUri(URIish uri) {
+ this.uri = uri;
+ }
+
+ /**
+ * Whether to change the push URL of the remote instead of the fetch URL.
+ *
+ * @param push
+ * <code>true</code> to set the push url, <code>false</code> to
+ * set the fetch url
+ */
+ public void setPush(boolean push) {
+ this.push = push;
+ }
+
+ /**
+ * Executes the {@code remote} command with all the options and parameters
+ * collected by the setter methods of this class.
+ *
+ * @return the {@link RemoteConfig} object of the modified remote
+ */
+ @Override
+ public RemoteConfig call() throws GitAPIException {
+ checkCallable();
+
+ try {
+ StoredConfig config = repo.getConfig();
+ RemoteConfig remote = new RemoteConfig(config, name);
+ if (push) {
+ List<URIish> uris = remote.getPushURIs();
+ if (uris.size() > 1) {
+ throw new JGitInternalException(
+ "remote.newtest.pushurl has multiple values"); //$NON-NLS-1$
+ } else if (uris.size() == 1) {
+ remote.removePushURI(uris.get(0));
+ }
+ remote.addPushURI(uri);
+ } else {
+ List<URIish> uris = remote.getURIs();
+ if (uris.size() > 1) {
+ throw new JGitInternalException(
+ "remote.newtest.url has multiple values"); //$NON-NLS-1$
+ } else if (uris.size() == 1) {
+ remote.removeURI(uris.get(0));
+ }
+ remote.addURI(uri);
+ }
+
+ remote.update(config);
+ config.save();
+ return remote;
+ } catch (IOException | URISyntaxException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
index 607253b76d..0731dd45ec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
@@ -51,6 +51,7 @@ import org.eclipse.jgit.api.errors.DetachedHeadException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.internal.JGitText;
@@ -121,6 +122,10 @@ public class RenameBranchCommand extends GitCommand<Ref> {
fullOldName = ref.getName();
} else {
fullOldName = repo.getFullBranch();
+ if (fullOldName == null) {
+ throw new NoHeadException(
+ JGitText.get().invalidRepositoryStateNoHead);
+ }
if (ObjectId.isId(fullOldName))
throw new DetachedHeadException();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java
new file mode 100644
index 0000000000..fbc30ef162
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick@sap.com> and
+ * other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v1.0 which accompanies this
+ * distribution, is reproduced below, and is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.api.errors;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.internal.JGitText;
+
+/**
+ * Exception thrown when the execution of a filter command failed
+ *
+ * @since 4.2
+ */
+public class FilterFailedException extends GitAPIException {
+ private static final long serialVersionUID = 1L;
+
+ private String filterCommand;
+
+ private String path;
+
+ private byte[] stdout;
+
+ private String stderr;
+
+ private int rc;
+
+ /**
+ * Thrown if during execution of filter command an exception occurred
+ *
+ * @param cause
+ * the exception
+ * @param filterCommand
+ * the command which failed
+ * @param path
+ * the path processed by the filter
+ */
+ public FilterFailedException(Exception cause, String filterCommand,
+ String path) {
+ super(MessageFormat.format(JGitText.get().filterExecutionFailed,
+ filterCommand, path), cause);
+ this.filterCommand = filterCommand;
+ this.path = path;
+ }
+
+ /**
+ * Thrown if a filter command returns a non-zero return code
+ *
+ * @param rc
+ * the return code
+ * @param filterCommand
+ * the command which failed
+ * @param path
+ * the path processed by the filter
+ * @param stdout
+ * the output the filter generated so far. This should be limited
+ * to reasonable size.
+ * @param stderr
+ * the stderr output of the filter
+ */
+ @SuppressWarnings("boxing")
+ public FilterFailedException(int rc, String filterCommand, String path,
+ byte[] stdout, String stderr) {
+ super(MessageFormat.format(JGitText.get().filterExecutionFailedRc,
+ filterCommand, path, rc, stderr));
+ this.rc = rc;
+ this.filterCommand = filterCommand;
+ this.path = path;
+ this.stdout = stdout;
+ this.stderr = stderr;
+ }
+
+ /**
+ * @return the filterCommand
+ */
+ public String getFilterCommand() {
+ return filterCommand;
+ }
+
+ /**
+ * @return the path of the file processed by the filter command
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * @return the output generated by the filter command. Might be truncated to
+ * limit memory consumption.
+ */
+ public byte[] getOutput() {
+ return stdout;
+ }
+
+ /**
+ * @return the error output returned by the filter command
+ */
+ public String getError() {
+ return stderr;
+ }
+
+ /**
+ * @return the return code returned by the filter command
+ */
+ public int getReturnCode() {
+ return rc;
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
index d3ce685187..905ad76929 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
@@ -50,8 +50,10 @@ package org.eclipse.jgit.attributes;
* <li>Set - represented by {@link State#SET}</li>
* <li>Unset - represented by {@link State#UNSET}</li>
* <li>Set to a value - represented by {@link State#CUSTOM}</li>
- * <li>Unspecified - <code>null</code> is used instead of an instance of this
- * class</li>
+ * <li>Unspecified - used to revert an attribute . This is crucial in order to
+ * mark an attribute as unspecified in the attributes map and thus preventing
+ * following (with lower priority) nodes from setting the attribute to a value
+ * at all</li>
* </ul>
* </p>
*
@@ -61,6 +63,7 @@ public final class Attribute {
/**
* The attribute value state
+ * see also https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
*/
public static enum State {
/** the attribute is set */
@@ -69,6 +72,13 @@ public final class Attribute {
/** the attribute is unset */
UNSET,
+ /**
+ * the attribute appears as if it would not be defined at all
+ *
+ * @since 4.2
+ */
+ UNSPECIFIED,
+
/** the attribute is set to a custom value */
CUSTOM
}
@@ -176,6 +186,8 @@ public final class Attribute {
return key;
case UNSET:
return "-" + key; //$NON-NLS-1$
+ case UNSPECIFIED:
+ return "!" + key; //$NON-NLS-1$
case CUSTOM:
default:
return key + "=" + value; //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java
new file mode 100644
index 0000000000..0810e31682
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.attributes;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.attributes.Attribute.State;
+
+/**
+ * Represents a set of attributes for a path
+ * <p>
+ *
+ * @since 4.2
+ */
+public final class Attributes {
+ private final Map<String, Attribute> map = new LinkedHashMap<>();
+
+ /**
+ * Creates a new instance
+ *
+ * @param attributes
+ */
+ public Attributes(Attribute... attributes) {
+ if (attributes != null) {
+ for (Attribute a : attributes) {
+ put(a);
+ }
+ }
+ }
+
+ /**
+ * @return true if the set does not contain any attributes
+ */
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ /**
+ * @param key
+ * @return the attribute or null
+ */
+ public Attribute get(String key) {
+ return map.get(key);
+ }
+
+ /**
+ * @return all attributes
+ */
+ public Collection<Attribute> getAll() {
+ return new ArrayList<>(map.values());
+ }
+
+ /**
+ * @param a
+ */
+ public void put(Attribute a) {
+ map.put(a.getKey(), a);
+ }
+
+ /**
+ * @param key
+ */
+ public void remove(String key) {
+ map.remove(key);
+ }
+
+ /**
+ * @param key
+ * @return true if the {@link Attributes} contains this key
+ */
+ public boolean containsKey(String key) {
+ return map.containsKey(key);
+ }
+
+ /**
+ * Returns the state.
+ *
+ * @param key
+ *
+ * @return the state (never returns <code>null</code>)
+ */
+ public Attribute.State getState(String key) {
+ Attribute a = map.get(key);
+ return a != null ? a.getState() : Attribute.State.UNSPECIFIED;
+ }
+
+ /**
+ * @param key
+ * @return true if the key is {@link State#SET}, false in all other cases
+ */
+ public boolean isSet(String key) {
+ return (getState(key) == State.SET);
+ }
+
+ /**
+ * @param key
+ * @return true if the key is {@link State#UNSET}, false in all other cases
+ */
+ public boolean isUnset(String key) {
+ return (getState(key) == State.UNSET);
+ }
+
+ /**
+ * @param key
+ * @return true if the key is {@link State#UNSPECIFIED}, false in all other
+ * cases
+ */
+ public boolean isUnspecified(String key) {
+ return (getState(key) == State.UNSPECIFIED);
+ }
+
+ /**
+ * @param key
+ * @return true if the key is {@link State#CUSTOM}, false in all other cases
+ * see {@link #getValue(String)} for the value of the key
+ */
+ public boolean isCustom(String key) {
+ return (getState(key) == State.CUSTOM);
+ }
+
+ /**
+ * @param key
+ * @return the attribute value (may be <code>null</code>)
+ */
+ public String getValue(String key) {
+ Attribute a = map.get(key);
+ return a != null ? a.getValue() : null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append(getClass().getSimpleName());
+ buf.append("["); //$NON-NLS-1$
+ buf.append(" "); //$NON-NLS-1$
+ for (Attribute a : map.values()) {
+ buf.append(a.toString());
+ buf.append(" "); //$NON-NLS-1$
+ }
+ buf.append("]"); //$NON-NLS-1$
+ return buf.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return map.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof Attributes))
+ return false;
+ Attributes other = (Attributes) obj;
+ return this.map.equals(other.map);
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
index 70f56ff964..5c0aba2e0e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
@@ -50,7 +50,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
-import java.util.Map;
import org.eclipse.jgit.lib.Constants;
@@ -134,11 +133,12 @@ public class AttributesNode {
* true if the target item is a directory.
* @param attributes
* Map that will hold the attributes matching this entry path. If
- * it is not empty, this method will NOT override any
- * existing entry.
+ * it is not empty, this method will NOT override any existing
+ * entry.
+ * @since 4.2
*/
- public void getAttributes(String entryPath, boolean isDirectory,
- Map<String, Attribute> attributes) {
+ public void getAttributes(String entryPath,
+ boolean isDirectory, Attributes attributes) {
// Parse rules in the reverse order that they were read since the last
// entry should be used
ListIterator<AttributesRule> ruleIterator = rules.listIterator(rules
@@ -153,7 +153,7 @@ public class AttributesNode {
while (attributeIte.hasPrevious()) {
Attribute attr = attributeIte.previous();
if (!attributes.containsKey(attr.getKey()))
- attributes.put(attr.getKey(), attr);
+ attributes.put(attr);
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java
new file mode 100644
index 0000000000..6f2ebad677
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014, Arthur Daussy <arthur.daussy@obeo.fr>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.lib.CoreConfig;
+
+/**
+ * An interface used to retrieve the global and info {@link AttributesNode}s.
+ *
+ * @since 4.2
+ *
+ */
+public interface AttributesNodeProvider {
+
+ /**
+ * Retrieve the {@link AttributesNode} that holds the information located
+ * in $GIT_DIR/info/attributes file.
+ *
+ * @return the {@link AttributesNode} that holds the information located in
+ * $GIT_DIR/info/attributes file.
+ * @throws IOException
+ * if an error is raised while parsing the attributes file
+ */
+ public AttributesNode getInfoAttributesNode() throws IOException;
+
+ /**
+ * Retrieve the {@link AttributesNode} that holds the information located
+ * in the global gitattributes file.
+ *
+ * @return the {@link AttributesNode} that holds the information located in
+ * the global gitattributes file.
+ * @throws IOException
+ * IOException if an error is raised while parsing the
+ * attributes file
+ * @see CoreConfig#getAttributesFile()
+ */
+ public AttributesNode getGlobalAttributesNode() throws IOException;
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java
new file mode 100644
index 0000000000..1037f697d4
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+/**
+ * Interface for classes which provide git attributes
+ *
+ * @since 4.2
+ */
+public interface AttributesProvider {
+ /**
+ * @return the currently active attributes
+ */
+ public Attributes getAttributes();
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
index bcac14b5ff..35d18c4b2a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
@@ -84,6 +84,13 @@ public class AttributesRule {
continue;
}
+ if (attribute.startsWith("!")) {//$NON-NLS-1$
+ if (attribute.length() > 1)
+ result.add(new Attribute(attribute.substring(1),
+ State.UNSPECIFIED));
+ continue;
+ }
+
final int equalsIndex = attribute.indexOf("="); //$NON-NLS-1$
if (equalsIndex == -1)
result.add(new Attribute(attribute, State.SET));
@@ -200,4 +207,16 @@ public class AttributesRule {
boolean match = matcher.matches(relativeTarget, isDirectory);
return match;
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(pattern);
+ for (Attribute a : attributes) {
+ sb.append(" "); //$NON-NLS-1$
+ sb.append(a);
+ }
+ return sb.toString();
+
+ }
} \ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
index 0dc4b05787..444ab1cb83 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
@@ -132,7 +132,11 @@ public abstract class ContentSource {
@Override
public long size(String path, ObjectId id) throws IOException {
- return reader.getObjectSize(id, Constants.OBJ_BLOB);
+ try {
+ return reader.getObjectSize(id, Constants.OBJ_BLOB);
+ } catch (MissingObjectException ignore) {
+ return 0;
+ }
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
index 6d9a32db92..fa0339544f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
@@ -63,6 +63,7 @@ import java.util.Comparator;
import java.util.List;
import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.IndexReadException;
import org.eclipse.jgit.errors.LockFailedException;
import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.events.IndexChangedEvent;
@@ -70,12 +71,15 @@ import org.eclipse.jgit.events.IndexChangedListener;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.FileSnapshot;
import org.eclipse.jgit.internal.storage.file.LockFile;
+import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO;
@@ -145,6 +149,28 @@ public class DirCache {
}
/**
+ * Create a new in memory index read from the contents of a tree.
+ *
+ * @param reader
+ * reader to access the tree objects from a repository.
+ * @param treeId
+ * tree to read. Must identify a tree, not a tree-ish.
+ * @return a new cache which has no backing store file, but contains the
+ * contents of {@code treeId}.
+ * @throws IOException
+ * one or more trees not available from the ObjectReader.
+ * @since 4.2
+ */
+ public static DirCache read(ObjectReader reader, AnyObjectId treeId)
+ throws IOException {
+ DirCache d = newInCore();
+ DirCacheBuilder b = d.builder();
+ b.addTree(null, DirCacheEntry.STAGE_0, reader, treeId);
+ b.finish();
+ return d;
+ }
+
+ /**
* Create a new in-core index representation and read an index from disk.
* <p>
* The new index will be read before it is returned to the caller. Read
@@ -417,6 +443,12 @@ public class DirCache {
}
}
} catch (FileNotFoundException fnfe) {
+ if (liveFile.exists()) {
+ // Panic: the index file exists but we can't read it
+ throw new IndexReadException(
+ MessageFormat.format(JGitText.get().cannotReadIndex,
+ liveFile.getAbsolutePath(), fnfe));
+ }
// Someone must have deleted it between our exists test
// and actually opening the path. That's fine, its empty.
//
@@ -869,8 +901,8 @@ public class DirCache {
*/
public DirCacheEntry[] getEntriesWithin(String path) {
if (path.length() == 0) {
- final DirCacheEntry[] r = new DirCacheEntry[sortedEntries.length];
- System.arraycopy(sortedEntries, 0, r, 0, sortedEntries.length);
+ DirCacheEntry[] r = new DirCacheEntry[entryCnt];
+ System.arraycopy(sortedEntries, 0, r, 0, entryCnt);
return r;
}
if (!path.endsWith("/")) //$NON-NLS-1$
@@ -963,6 +995,7 @@ public class DirCache {
private void updateSmudgedEntries() throws IOException {
List<String> paths = new ArrayList<String>(128);
try (TreeWalk walk = new TreeWalk(repository)) {
+ walk.setOperationType(OperationType.CHECKIN_OP);
for (int i = 0; i < entryCnt; i++)
if (sortedEntries[i].isSmudged())
paths.add(sortedEntries[i].getPathString());
@@ -974,6 +1007,7 @@ public class DirCache {
FileTreeIterator fIter = new FileTreeIterator(repository);
walk.addTree(iIter);
walk.addTree(fIter);
+ fIter.setDirCacheIterator(walk, 0);
walk.setRecursive(true);
while (walk.next()) {
iIter = walk.getTree(0, DirCacheIterator.class);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
index 6c7a70c52e..cfebe2d073 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
@@ -44,6 +44,9 @@
package org.eclipse.jgit.dircache;
+import static org.eclipse.jgit.lib.FileMode.TYPE_MASK;
+import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
+
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Arrays;
@@ -51,9 +54,7 @@ import java.util.Arrays;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
-import org.eclipse.jgit.treewalk.TreeWalk;
/**
* Updates a {@link DirCache} by adding individual {@link DirCacheEntry}s.
@@ -102,8 +103,9 @@ public class DirCacheBuilder extends BaseDirCacheEditor {
*/
public void add(final DirCacheEntry newEntry) {
if (newEntry.getRawMode() == 0)
- throw new IllegalArgumentException(MessageFormat.format(JGitText.get().fileModeNotSetForPath
- , newEntry.getPathString()));
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().fileModeNotSetForPath,
+ newEntry.getPathString()));
beforeAdd(newEntry);
fastAdd(newEntry);
}
@@ -162,27 +164,56 @@ public class DirCacheBuilder extends BaseDirCacheEditor {
* @throws IOException
* a tree cannot be read to iterate through its entries.
*/
- public void addTree(final byte[] pathPrefix, final int stage,
- final ObjectReader reader, final AnyObjectId tree) throws IOException {
- final TreeWalk tw = new TreeWalk(reader);
- tw.addTree(new CanonicalTreeParser(pathPrefix, reader, tree
- .toObjectId()));
- tw.setRecursive(true);
- if (tw.next()) {
- final DirCacheEntry newEntry = toEntry(stage, tw);
- beforeAdd(newEntry);
- fastAdd(newEntry);
- while (tw.next())
- fastAdd(toEntry(stage, tw));
+ public void addTree(byte[] pathPrefix, int stage, ObjectReader reader,
+ AnyObjectId tree) throws IOException {
+ CanonicalTreeParser p = createTreeParser(pathPrefix, reader, tree);
+ while (!p.eof()) {
+ if (isTree(p)) {
+ p = enterTree(p, reader);
+ continue;
+ }
+
+ DirCacheEntry first = toEntry(stage, p);
+ beforeAdd(first);
+ fastAdd(first);
+ p = p.next();
+ break;
+ }
+
+ // Rest of tree entries are correctly sorted; use fastAdd().
+ while (!p.eof()) {
+ if (isTree(p)) {
+ p = enterTree(p, reader);
+ } else {
+ fastAdd(toEntry(stage, p));
+ p = p.next();
+ }
}
}
- private DirCacheEntry toEntry(final int stage, final TreeWalk tw) {
- final DirCacheEntry e = new DirCacheEntry(tw.getRawPath(), stage);
- final AbstractTreeIterator i;
+ private static CanonicalTreeParser createTreeParser(byte[] pathPrefix,
+ ObjectReader reader, AnyObjectId tree) throws IOException {
+ return new CanonicalTreeParser(pathPrefix, reader, tree);
+ }
+
+ private static boolean isTree(CanonicalTreeParser p) {
+ return (p.getEntryRawMode() & TYPE_MASK) == TYPE_TREE;
+ }
+
+ private static CanonicalTreeParser enterTree(CanonicalTreeParser p,
+ ObjectReader reader) throws IOException {
+ p = p.createSubtreeIterator(reader);
+ return p.eof() ? p.next() : p;
+ }
+
+ private static DirCacheEntry toEntry(int stage, CanonicalTreeParser i) {
+ byte[] buf = i.getEntryPathBuffer();
+ int len = i.getEntryPathLength();
+ byte[] path = new byte[len];
+ System.arraycopy(buf, 0, path, 0, len);
- i = tw.getTree(0, AbstractTreeIterator.class);
- e.setFileMode(tw.getFileMode(0));
+ DirCacheEntry e = new DirCacheEntry(path, stage);
+ e.setFileMode(i.getEntryRawMode());
e.setObjectIdFromRaw(i.idBuffer(), i.idOffset());
return e;
}
@@ -242,9 +273,9 @@ public class DirCacheBuilder extends BaseDirCacheEditor {
sorted = true;
}
- private static IllegalStateException bad(final DirCacheEntry a,
- final String msg) {
- return new IllegalStateException(msg + ": " + a.getStage() + " " //$NON-NLS-1$ //$NON-NLS-2$
- + a.getPathString());
+ private static IllegalStateException bad(DirCacheEntry a, String msg) {
+ return new IllegalStateException(String.format(
+ "%s: %d %s", //$NON-NLS-1$
+ msg, Integer.valueOf(a.getStage()), a.getPathString()));
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 0036ab5089..4eb688170c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -52,15 +52,18 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.eclipse.jgit.api.errors.FilterFailedException;
import org.eclipse.jgit.errors.CheckoutConflictException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.IndexWriteException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
import org.eclipse.jgit.lib.CoreConfig.SymLinks;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectChecker;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
@@ -76,6 +79,7 @@ import org.eclipse.jgit.treewalk.WorkingTreeIterator;
import org.eclipse.jgit.treewalk.WorkingTreeOptions;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.ExecutionResult;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.SystemReader;
@@ -85,9 +89,10 @@ import org.eclipse.jgit.util.io.AutoCRLFOutputStream;
* This class handles checking out one or two trees merging with the index.
*/
public class DirCacheCheckout {
+ private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024;
private Repository repo;
- private HashMap<String, ObjectId> updated = new HashMap<String, ObjectId>();
+ private HashMap<String, String> updated = new HashMap<String, String>();
private ArrayList<String> conflicts = new ArrayList<String>();
@@ -112,9 +117,9 @@ public class DirCacheCheckout {
private boolean emptyDirCache;
/**
- * @return a list of updated paths and objectIds
+ * @return a list of updated paths and smudgeFilterCommands
*/
- public Map<String, ObjectId> getUpdated() {
+ public Map<String, String> getUpdated() {
return updated;
}
@@ -447,7 +452,8 @@ public class DirCacheCheckout {
for (String path : updated.keySet()) {
DirCacheEntry entry = dc.getEntry(path);
if (!FileMode.GITLINK.equals(entry.getRawMode()))
- checkoutEntry(repo, entry, objectReader, false);
+ checkoutEntry(repo, entry, objectReader, false,
+ updated.get(path));
}
// commit the index builder - a new index is persisted
@@ -996,9 +1002,12 @@ public class DirCacheCheckout {
removed.add(path);
}
- private void update(String path, ObjectId mId, FileMode mode) {
+ private void update(String path, ObjectId mId, FileMode mode)
+ throws IOException {
if (!FileMode.TREE.equals(mode)) {
- updated.put(path, mId);
+ updated.put(path,
+ walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE));
+
DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0);
entry.setObjectId(mId);
entry.setFileMode(mode);
@@ -1150,7 +1159,7 @@ public class DirCacheCheckout {
*/
public static void checkoutEntry(Repository repo, DirCacheEntry entry,
ObjectReader or) throws IOException {
- checkoutEntry(repo, entry, or, false);
+ checkoutEntry(repo, entry, or, false, null);
}
/**
@@ -1186,6 +1195,46 @@ public class DirCacheCheckout {
*/
public static void checkoutEntry(Repository repo, DirCacheEntry entry,
ObjectReader or, boolean deleteRecursive) throws IOException {
+ checkoutEntry(repo, entry, or, deleteRecursive, null);
+ }
+
+ /**
+ * Updates the file in the working tree with content and mode from an entry
+ * in the index. The new content is first written to a new temporary file in
+ * the same directory as the real file. Then that new file is renamed to the
+ * final filename.
+ *
+ * <p>
+ * <b>Note:</b> if the entry path on local file system exists as a file, it
+ * will be deleted and if it exists as a directory, it will be deleted
+ * recursively, independently if has any content.
+ * </p>
+ *
+ * <p>
+ * TODO: this method works directly on File IO, we may need another
+ * abstraction (like WorkingTreeIterator). This way we could tell e.g.
+ * Eclipse that Files in the workspace got changed
+ * </p>
+ *
+ * @param repo
+ * repository managing the destination work tree.
+ * @param entry
+ * the entry containing new mode and content
+ * @param or
+ * object reader to use for checkout
+ * @param deleteRecursive
+ * true to recursively delete final path if it exists on the file
+ * system
+ * @param smudgeFilterCommand
+ * the filter command to be run for smudging the entry to be
+ * checked out
+ *
+ * @throws IOException
+ * @since 4.2
+ */
+ public static void checkoutEntry(Repository repo, DirCacheEntry entry,
+ ObjectReader or, boolean deleteRecursive,
+ String smudgeFilterCommand) throws IOException {
ObjectLoader ol = or.open(entry.getObjectId());
File f = new File(repo.getWorkTree(), entry.getPathString());
File parentDir = f.getParentFile();
@@ -1210,14 +1259,52 @@ public class DirCacheCheckout {
OutputStream channel = new FileOutputStream(tmpFile);
if (opt.getAutoCRLF() == AutoCRLF.TRUE)
channel = new AutoCRLFOutputStream(channel);
- try {
- ol.copyTo(channel);
- } finally {
- channel.close();
+ if (smudgeFilterCommand != null) {
+ ProcessBuilder filterProcessBuilder = fs
+ .runInShell(smudgeFilterCommand, new String[0]);
+ filterProcessBuilder.directory(repo.getWorkTree());
+ filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
+ repo.getDirectory().getAbsolutePath());
+ ExecutionResult result;
+ int rc;
+ try {
+ // TODO: wire correctly with AUTOCRLF
+ result = fs.execute(filterProcessBuilder, ol.openStream());
+ rc = result.getRc();
+ if (rc == 0) {
+ result.getStdout().writeTo(channel,
+ NullProgressMonitor.INSTANCE);
+ }
+ } catch (IOException | InterruptedException e) {
+ throw new IOException(new FilterFailedException(e,
+ smudgeFilterCommand, entry.getPathString()));
+
+ } finally {
+ channel.close();
+ }
+ if (rc != 0) {
+ throw new IOException(new FilterFailedException(rc,
+ smudgeFilterCommand, entry.getPathString(),
+ result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE),
+ RawParseUtils.decode(result.getStderr()
+ .toByteArray(MAX_EXCEPTION_TEXT_SIZE))));
+ }
+ } else {
+ try {
+ ol.copyTo(channel);
+ } finally {
+ channel.close();
+ }
+ }
+ // The entry needs to correspond to the on-disk filesize. If the content
+ // was filtered (either by autocrlf handling or smudge filters) ask the
+ // filesystem again for the length. Otherwise the objectloader knows the
+ // size
+ if (opt.getAutoCRLF() == AutoCRLF.TRUE || smudgeFilterCommand != null) {
+ entry.setLength(tmpFile.length());
+ } else {
+ entry.setLength(ol.getSize());
}
- entry.setLength(opt.getAutoCRLF() == AutoCRLF.TRUE ? //
- tmpFile.length() // AutoCRLF wants on-disk-size
- : (int) ol.getSize());
if (opt.isFileMode() && fs.supportsExecute()) {
if (FileMode.EXECUTABLE_FILE.equals(entry.getRawMode())) {
@@ -1255,24 +1342,6 @@ public class DirCacheCheckout {
checkValidPathSegment(chk, i);
}
- /**
- * Check if path is a valid path for a checked out file name or ref name.
- *
- * @param path
- * @throws InvalidPathException
- * if the path is invalid
- * @since 3.3
- */
- static void checkValidPath(String path) throws InvalidPathException {
- try {
- SystemReader.getInstance().checkPath(path);
- } catch (CorruptObjectException e) {
- InvalidPathException p = new InvalidPathException(path);
- p.initCause(e);
- throw p;
- }
- }
-
private static void checkValidPathSegment(ObjectChecker chk,
CanonicalTreeParser t) throws InvalidPathException {
try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
index eef2e6d3c3..c8bc0960f4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
@@ -65,6 +65,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.MutableInteger;
import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.SystemReader;
/**
* A single file (or stage of a file) in a {@link DirCache}.
@@ -191,7 +192,7 @@ public class DirCacheEntry {
}
try {
- DirCacheCheckout.checkValidPath(toString(path));
+ checkPath(path);
} catch (InvalidPathException e) {
CorruptObjectException p =
new CorruptObjectException(e.getMessage());
@@ -263,7 +264,7 @@ public class DirCacheEntry {
/**
* Create an empty entry at the specified stage.
*
- * @param newPath
+ * @param path
* name of the cache entry, in the standard encoding.
* @param stage
* the stage index of the new entry.
@@ -274,16 +275,16 @@ public class DirCacheEntry {
* range 0..3, inclusive.
*/
@SuppressWarnings("boxing")
- public DirCacheEntry(final byte[] newPath, final int stage) {
- DirCacheCheckout.checkValidPath(toString(newPath));
+ public DirCacheEntry(byte[] path, final int stage) {
+ checkPath(path);
if (stage < 0 || 3 < stage)
throw new IllegalArgumentException(MessageFormat.format(
JGitText.get().invalidStageForPath,
- stage, toString(newPath)));
+ stage, toString(path)));
info = new byte[INFO_LEN];
infoOffset = 0;
- path = newPath;
+ this.path = path;
int flags = ((stage & 0x3) << 12);
if (path.length < NAME_MASK)
@@ -498,12 +499,16 @@ public class DirCacheEntry {
switch (mode.getBits() & FileMode.TYPE_MASK) {
case FileMode.TYPE_MISSING:
case FileMode.TYPE_TREE:
- throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidModeForPath
- , mode, getPathString()));
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().invalidModeForPath, mode, getPathString()));
}
NB.encodeInt32(info, infoOffset + P_MODE, mode.getBits());
}
+ void setFileMode(int mode) {
+ NB.encodeInt32(info, infoOffset + P_MODE, mode);
+ }
+
/**
* Get the cached creation time of this file, in milliseconds.
*
@@ -730,6 +735,16 @@ public class DirCacheEntry {
return 0;
}
+ private static void checkPath(byte[] path) {
+ try {
+ SystemReader.getInstance().checkPath(path);
+ } catch (CorruptObjectException e) {
+ InvalidPathException p = new InvalidPathException(toString(path));
+ p.initCause(e);
+ throw p;
+ }
+ }
+
private static String toString(final byte[] path) {
return Constants.CHARSET.decode(ByteBuffer.wrap(path)).toString();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
index 354a07439a..ad93f7213f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
@@ -103,9 +103,6 @@ public class DirCacheIterator extends AbstractTreeIterator {
/** The subtree containing {@link #currentEntry} if this is first entry. */
protected DirCacheTree currentSubtree;
- /** Holds an {@link AttributesNode} for the current entry */
- private AttributesNode attributesNode;
-
/**
* Create a new iterator for an already loaded DirCache instance.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java
new file mode 100644
index 0000000000..70f650dde6
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick@sap.com> and
+ * other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v1.0 which accompanies this
+ * distribution, is reproduced below, and is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.errors;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.internal.JGitText;
+
+/**
+ * Cannot read the index. This is a serious error that users need to be made
+ * aware of.
+ *
+ * @since 4.2
+ */
+public class IndexReadException extends IOException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs an IndexReadException with the default message.
+ */
+ public IndexReadException() {
+ super(JGitText.get().indexWriteException);
+ }
+
+ /**
+ * Constructs an IndexReadException with the specified detail message.
+ *
+ * @param s
+ * message
+ */
+ public IndexReadException(final String s) {
+ super(s);
+ }
+
+ /**
+ * Constructs an IndexReadException with the specified detail message.
+ *
+ * @param s
+ * message
+ * @param cause
+ * root cause exception
+ */
+ public IndexReadException(final String s, final Throwable cause) {
+ super(s);
+ initCause(cause);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index 1d2d3bfaaf..ff9f233aa5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -42,6 +42,9 @@
*/
package org.eclipse.jgit.gitrepo;
+import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME;
+import static org.eclipse.jgit.lib.Constants.R_REMOTES;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -574,7 +577,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
private static String findRef(String ref, Repository repo)
throws IOException {
if (!ObjectId.isId(ref)) {
- Ref r = repo.getRef(Constants.DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$
+ Ref r = repo.exactRef(R_REMOTES + DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$
if (r != null)
return r.getName();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
index 2e6582819f..a501fee901 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
@@ -113,6 +113,9 @@ public class PrePushHook extends GitHook<String> {
*/
@Override
protected String[] getParameters() {
+ if (remoteName == null) {
+ remoteName = remoteLocation;
+ }
return new String[] { remoteName, remoteLocation };
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index e39469bd8c..796eaaebf5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -79,6 +79,7 @@ public class JGitText extends TranslationBundle {
/***/ public String atLeastOnePathIsRequired;
/***/ public String atLeastOnePatternIsRequired;
/***/ public String atLeastTwoFiltersNeeded;
+ /***/ public String atomicPushNotSupported;
/***/ public String authenticationNotSupported;
/***/ public String badBase64InputCharacterAt;
/***/ public String badEntryDelimiter;
@@ -104,6 +105,7 @@ public class JGitText extends TranslationBundle {
/***/ public String cannotBeRecursiveWhenTreesAreIncluded;
/***/ public String cannotChangeActionOnComment;
/***/ public String cannotChangeToComment;
+ /***/ public String cannotCheckoutFromUnbornBranch;
/***/ public String cannotCheckoutOursSwitchBranch;
/***/ public String cannotCombineSquashWithNoff;
/***/ public String cannotCombineTreeFilterWithRevFilter;
@@ -146,6 +148,7 @@ public class JGitText extends TranslationBundle {
/***/ public String cannotReadCommit;
/***/ public String cannotReadFile;
/***/ public String cannotReadHEAD;
+ /***/ public String cannotReadIndex;
/***/ public String cannotReadObject;
/***/ public String cannotReadObjectsPath;
/***/ public String cannotReadTree;
@@ -338,6 +341,8 @@ public class JGitText extends TranslationBundle {
/***/ public String fileIsTooBigForThisConvenienceMethod;
/***/ public String fileIsTooLarge;
/***/ public String fileModeNotSetForPath;
+ /***/ public String filterExecutionFailed;
+ /***/ public String filterExecutionFailedRc;
/***/ public String findingGarbage;
/***/ public String flagIsDisposed;
/***/ public String flagNotFromThis;
@@ -410,6 +415,7 @@ public class JGitText extends TranslationBundle {
/***/ public String invalidURL;
/***/ public String invalidWildcards;
/***/ public String invalidRefSpec;
+ /***/ public String invalidRepositoryStateNoHead;
/***/ public String invalidWindowSize;
/***/ public String isAStaticFlagAndHasNorevWalkInstance;
/***/ public String JRELacksMD5Implementation;
@@ -509,6 +515,7 @@ public class JGitText extends TranslationBundle {
/***/ public String packfileIsTruncatedNoParam;
/***/ public String packHandleIsStale;
/***/ public String packHasUnresolvedDeltas;
+ /***/ public String packInaccessible;
/***/ public String packingCancelledDuringObjectsWriting;
/***/ public String packObjectCountMismatch;
/***/ public String packRefs;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
index 122f6d3d19..0d5fd0f859 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
@@ -44,8 +44,13 @@
package org.eclipse.jgit.internal.storage.dfs;
import java.io.IOException;
+import java.io.InputStream;
import java.text.MessageFormat;
+import java.util.Collections;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
+import org.eclipse.jgit.attributes.AttributesRule;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.RefUpdate;
@@ -126,4 +131,36 @@ public abstract class DfsRepository extends Repository {
public ReflogReader getReflogReader(String refName) throws IOException {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public AttributesNodeProvider createAttributesNodeProvider() {
+ // TODO Check if the implementation used in FileRepository can be used
+ // for this kind of repository
+ return new EmptyAttributesNodeProvider();
+ }
+
+ private static class EmptyAttributesNodeProvider implements
+ AttributesNodeProvider {
+ private EmptyAttributesNode emptyAttributesNode = new EmptyAttributesNode();
+
+ public AttributesNode getInfoAttributesNode() throws IOException {
+ return emptyAttributesNode;
+ }
+
+ public AttributesNode getGlobalAttributesNode() throws IOException {
+ return emptyAttributesNode;
+ }
+
+ private static class EmptyAttributesNode extends AttributesNode {
+
+ public EmptyAttributesNode() {
+ super(Collections.<AttributesRule> emptyList());
+ }
+
+ @Override
+ public void parse(InputStream in) throws IOException {
+ // Do nothing
+ }
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
index 832e4fb6a8..1c664b4097 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
@@ -13,14 +13,22 @@ import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Ref.Storage;
import org.eclipse.jgit.lib.SymbolicRef;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.util.RefList;
/**
@@ -46,8 +54,8 @@ public class InMemoryRepository extends DfsRepository {
static final AtomicInteger packId = new AtomicInteger();
private final DfsObjDatabase objdb;
-
private final DfsRefDatabase refdb;
+ private boolean performsAtomicTransactions = true;
/**
* Initialize a new in-memory repository.
@@ -76,6 +84,17 @@ public class InMemoryRepository extends DfsRepository {
return refdb;
}
+ /**
+ * Enable (or disable) the atomic reference transaction support.
+ * <p>
+ * Useful for testing atomic support enabled or disabled.
+ *
+ * @param atomic
+ */
+ public void setPerformsAtomicTransactions(boolean atomic) {
+ performsAtomicTransactions = atomic;
+ }
+
private class MemObjDatabase extends DfsObjDatabase {
private List<DfsPackDescription> packs = new ArrayList<DfsPackDescription>();
@@ -235,41 +254,143 @@ public class InMemoryRepository extends DfsRepository {
private class MemRefDatabase extends DfsRefDatabase {
private final ConcurrentMap<String, Ref> refs = new ConcurrentHashMap<String, Ref>();
+ private final ReadWriteLock lock = new ReentrantReadWriteLock(true /* fair */);
MemRefDatabase() {
super(InMemoryRepository.this);
}
@Override
+ public boolean performsAtomicTransactions() {
+ return performsAtomicTransactions;
+ }
+
+ @Override
+ public BatchRefUpdate newBatchUpdate() {
+ return new BatchRefUpdate(this) {
+ @Override
+ public void execute(RevWalk walk, ProgressMonitor monitor)
+ throws IOException {
+ if (performsAtomicTransactions()) {
+ try {
+ lock.writeLock().lock();
+ batch(walk, getCommands());
+ } finally {
+ lock.writeLock().unlock();
+ }
+ } else {
+ super.execute(walk, monitor);
+ }
+ }
+ };
+ }
+
+ @Override
protected RefCache scanAllRefs() throws IOException {
RefList.Builder<Ref> ids = new RefList.Builder<Ref>();
RefList.Builder<Ref> sym = new RefList.Builder<Ref>();
- for (Ref ref : refs.values()) {
- if (ref.isSymbolic())
- sym.add(ref);
- ids.add(ref);
+ try {
+ lock.readLock().lock();
+ for (Ref ref : refs.values()) {
+ if (ref.isSymbolic())
+ sym.add(ref);
+ ids.add(ref);
+ }
+ } finally {
+ lock.readLock().unlock();
}
ids.sort();
sym.sort();
return new RefCache(ids.toRefList(), sym.toRefList());
}
+ private void batch(RevWalk walk, List<ReceiveCommand> cmds) {
+ // Validate that the target exists in a new RevWalk, as the RevWalk
+ // from the RefUpdate might be reading back unflushed objects.
+ Map<ObjectId, ObjectId> peeled = new HashMap<>();
+ try (RevWalk rw = new RevWalk(getRepository())) {
+ for (ReceiveCommand c : cmds) {
+ if (!ObjectId.zeroId().equals(c.getNewId())) {
+ try {
+ RevObject o = rw.parseAny(c.getNewId());
+ if (o instanceof RevTag) {
+ peeled.put(o, rw.peel(o).copy());
+ }
+ } catch (IOException e) {
+ c.setResult(ReceiveCommand.Result.REJECTED_MISSING_OBJECT);
+ reject(cmds);
+ return;
+ }
+ }
+ }
+ }
+
+ // Check all references conform to expected old value.
+ for (ReceiveCommand c : cmds) {
+ Ref r = refs.get(c.getRefName());
+ if (r == null) {
+ if (c.getType() != ReceiveCommand.Type.CREATE) {
+ c.setResult(ReceiveCommand.Result.LOCK_FAILURE);
+ reject(cmds);
+ return;
+ }
+ } else if (r.isSymbolic() || r.getObjectId() == null
+ || !r.getObjectId().equals(c.getOldId())) {
+ c.setResult(ReceiveCommand.Result.LOCK_FAILURE);
+ reject(cmds);
+ return;
+ }
+ }
+
+ // Write references.
+ for (ReceiveCommand c : cmds) {
+ if (c.getType() == ReceiveCommand.Type.DELETE) {
+ refs.remove(c.getRefName());
+ c.setResult(ReceiveCommand.Result.OK);
+ continue;
+ }
+
+ ObjectId p = peeled.get(c.getNewId());
+ Ref r;
+ if (p != null) {
+ r = new ObjectIdRef.PeeledTag(Storage.PACKED,
+ c.getRefName(), c.getNewId(), p);
+ } else {
+ r = new ObjectIdRef.PeeledNonTag(Storage.PACKED,
+ c.getRefName(), c.getNewId());
+ }
+ refs.put(r.getName(), r);
+ c.setResult(ReceiveCommand.Result.OK);
+ }
+ clearCache();
+ }
+
+ private void reject(List<ReceiveCommand> cmds) {
+ for (ReceiveCommand c : cmds) {
+ if (c.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
+ c.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON,
+ JGitText.get().transactionAborted);
+ }
+ }
+ }
+
@Override
protected boolean compareAndPut(Ref oldRef, Ref newRef)
throws IOException {
- ObjectId id = newRef.getObjectId();
- if (id != null) {
- try (RevWalk rw = new RevWalk(getRepository())) {
- // Validate that the target exists in a new RevWalk, as the RevWalk
- // from the RefUpdate might be reading back unflushed objects.
- rw.parseAny(id);
+ try {
+ lock.writeLock().lock();
+ ObjectId id = newRef.getObjectId();
+ if (id != null) {
+ try (RevWalk rw = new RevWalk(getRepository())) {
+ // Validate that the target exists in a new RevWalk, as the RevWalk
+ // from the RefUpdate might be reading back unflushed objects.
+ rw.parseAny(id);
+ }
}
- }
- String name = newRef.getName();
- if (oldRef == null)
- return refs.putIfAbsent(name, newRef) == null;
+ String name = newRef.getName();
+ if (oldRef == null)
+ return refs.putIfAbsent(name, newRef) == null;
- synchronized (refs) {
Ref cur = refs.get(name);
Ref toCompare = cur;
if (toCompare != null) {
@@ -294,22 +415,29 @@ public class InMemoryRepository extends DfsRepository {
if (eq(toCompare, oldRef))
return refs.replace(name, cur, newRef);
}
- }
- if (oldRef.getStorage() == Storage.NEW)
- return refs.putIfAbsent(name, newRef) == null;
+ if (oldRef.getStorage() == Storage.NEW)
+ return refs.putIfAbsent(name, newRef) == null;
- return false;
+ return false;
+ } finally {
+ lock.writeLock().unlock();
+ }
}
@Override
protected boolean compareAndRemove(Ref oldRef) throws IOException {
- String name = oldRef.getName();
- Ref cur = refs.get(name);
- if (cur != null && eq(cur, oldRef))
- return refs.remove(name, cur);
- else
- return false;
+ try {
+ lock.writeLock().lock();
+ String name = oldRef.getName();
+ Ref cur = refs.get(name);
+ if (cur != null && eq(cur, oldRef))
+ return refs.remove(name, cur);
+ else
+ return false;
+ } finally {
+ lock.writeLock().unlock();
+ }
}
private boolean eq(Ref a, Ref b) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
index 995621ee3e..490cbcaa81 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
@@ -49,11 +49,15 @@ package org.eclipse.jgit.internal.storage.file;
import static org.eclipse.jgit.lib.RefDatabase.ALL;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Set;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.events.ConfigChangedEvent;
import org.eclipse.jgit.events.ConfigChangedListener;
@@ -479,4 +483,63 @@ public class FileRepository extends Repository {
return new ReflogReaderImpl(this, ref.getName());
return null;
}
+
+ @Override
+ public AttributesNodeProvider createAttributesNodeProvider() {
+ return new AttributesNodeProviderImpl(this);
+ }
+
+ /**
+ * Implementation a {@link AttributesNodeProvider} for a
+ * {@link FileRepository}.
+ *
+ * @author <a href="mailto:arthur.daussy@obeo.fr">Arthur Daussy</a>
+ *
+ */
+ static class AttributesNodeProviderImpl implements
+ AttributesNodeProvider {
+
+ private AttributesNode infoAttributesNode;
+
+ private AttributesNode globalAttributesNode;
+
+ /**
+ * Constructor.
+ *
+ * @param repo
+ * {@link Repository} that will provide the attribute nodes.
+ */
+ protected AttributesNodeProviderImpl(Repository repo) {
+ infoAttributesNode = new InfoAttributesNode(repo);
+ globalAttributesNode = new GlobalAttributesNode(repo);
+ }
+
+ public AttributesNode getInfoAttributesNode() throws IOException {
+ if (infoAttributesNode instanceof InfoAttributesNode)
+ infoAttributesNode = ((InfoAttributesNode) infoAttributesNode)
+ .load();
+ return infoAttributesNode;
+ }
+
+ public AttributesNode getGlobalAttributesNode() throws IOException {
+ if (globalAttributesNode instanceof GlobalAttributesNode)
+ globalAttributesNode = ((GlobalAttributesNode) globalAttributesNode)
+ .load();
+ return globalAttributesNode;
+ }
+
+ static void loadRulesFromFile(AttributesNode r, File attrs)
+ throws FileNotFoundException, IOException {
+ if (attrs.exists()) {
+ FileInputStream in = new FileInputStream(attrs);
+ try {
+ r.parse(in);
+ } finally {
+ in.close();
+ }
+ }
+ }
+
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
index e7005c247e..4c40538b6a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
@@ -90,6 +90,7 @@ import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Ref.Storage;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -592,7 +593,11 @@ public class GC {
* @throws IOException
*/
private Set<ObjectId> listRefLogObjects(Ref ref, long minTime) throws IOException {
- List<ReflogEntry> rlEntries = repo.getReflogReader(ref.getName())
+ ReflogReader reflogReader = repo.getReflogReader(ref.getName());
+ if (reflogReader == null) {
+ return Collections.emptySet();
+ }
+ List<ReflogEntry> rlEntries = reflogReader
.getReverseEntries();
if (rlEntries == null || rlEntries.isEmpty())
return Collections.<ObjectId> emptySet();
@@ -635,10 +640,7 @@ public class GC {
*/
private Set<ObjectId> listNonHEADIndexObjects()
throws CorruptObjectException, IOException {
- try {
- if (repo.getIndexFile() == null)
- return Collections.emptySet();
- } catch (NoWorkTreeException e) {
+ if (repo.isBare()) {
return Collections.emptySet();
}
try (TreeWalk treeWalk = new TreeWalk(repo)) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java
new file mode 100644
index 0000000000..454d3bff69
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014, Arthur Daussy <arthur.daussy@obeo.fr>
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.FS;
+
+/** Attribute node loaded from global system-wide file. */
+public class GlobalAttributesNode extends AttributesNode {
+ final Repository repository;
+
+ /**
+ * @param repository
+ */
+ public GlobalAttributesNode(Repository repository) {
+ this.repository = repository;
+ }
+
+ /**
+ * @return the attributes node
+ * @throws IOException
+ */
+ public AttributesNode load() throws IOException {
+ AttributesNode r = new AttributesNode();
+
+ FS fs = repository.getFS();
+ String path = repository.getConfig().get(CoreConfig.KEY)
+ .getAttributesFile();
+ if (path != null) {
+ File attributesFile;
+ if (path.startsWith("~/")) { //$NON-NLS-1$
+ attributesFile = fs.resolve(fs.userHome(),
+ path.substring(2));
+ } else {
+ attributesFile = fs.resolve(null, path);
+ }
+ FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributesFile);
+ }
+ return r.getRules().isEmpty() ? null : r;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
new file mode 100644
index 0000000000..bda5cbeba4
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014, Arthur Daussy <arthur.daussy@obeo.fr>
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.FS;
+
+/** Attribute node loaded from the $GIT_DIR/info/attributes file. */
+public class InfoAttributesNode extends AttributesNode {
+ final Repository repository;
+
+ /**
+ * @param repository
+ */
+ public InfoAttributesNode(Repository repository) {
+ this.repository = repository;
+ }
+
+ /**
+ * @return the attributes node
+ * @throws IOException
+ */
+ public AttributesNode load() throws IOException {
+ AttributesNode r = new AttributesNode();
+
+ FS fs = repository.getFS();
+
+ File attributes = fs.resolve(repository.getDirectory(),
+ Constants.INFO_ATTRIBUTES);
+ FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributes);
+
+ return r.getRules().isEmpty() ? null : r;
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
index 50297a97a0..e23ca741b8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
@@ -227,6 +227,10 @@ public class LockFile {
fis.close();
}
} catch (FileNotFoundException fnfe) {
+ if (ref.exists()) {
+ unlock();
+ throw fnfe;
+ }
// Don't worry about a file that doesn't exist yet, it
// conceptually has no current content to copy.
//
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
index e7ef127dd7..bd1d488d94 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -433,16 +433,14 @@ public class ObjectDirectory extends FileObjectDatabase {
ObjectLoader openLooseObject(WindowCursor curs, AnyObjectId id)
throws IOException {
- try {
- File path = fileFor(id);
- FileInputStream in = new FileInputStream(path);
- try {
- unpackedObjectCache.add(id);
- return UnpackedObject.open(in, path, id, curs);
- } finally {
- in.close();
- }
+ File path = fileFor(id);
+ try (FileInputStream in = new FileInputStream(path)) {
+ unpackedObjectCache.add(id);
+ return UnpackedObject.open(in, path, id, curs);
} catch (FileNotFoundException noFile) {
+ if (path.exists()) {
+ throw noFile;
+ }
unpackedObjectCache.remove(id);
return null;
}
@@ -513,15 +511,14 @@ public class ObjectDirectory extends FileObjectDatabase {
private long getLooseObjectSize(WindowCursor curs, AnyObjectId id)
throws IOException {
- try {
- FileInputStream in = new FileInputStream(fileFor(id));
- try {
- unpackedObjectCache.add(id);
- return UnpackedObject.getSize(in, id, curs);
- } finally {
- in.close();
- }
+ File f = fileFor(id);
+ try (FileInputStream in = new FileInputStream(f)) {
+ unpackedObjectCache.add(id);
+ return UnpackedObject.getSize(in, id, curs);
} catch (FileNotFoundException noFile) {
+ if (f.exists()) {
+ throw noFile;
+ }
unpackedObjectCache.remove(id);
return -1;
}
@@ -561,7 +558,11 @@ public class ObjectDirectory extends FileObjectDatabase {
// Assume the pack is corrupted, and remove it from the list.
removePack(p);
} else if (e instanceof FileNotFoundException) {
- warnTmpl = JGitText.get().packWasDeleted;
+ if (p.getPackFile().exists()) {
+ warnTmpl = JGitText.get().packInaccessible;
+ } else {
+ warnTmpl = JGitText.get().packWasDeleted;
+ }
removePack(p);
} else if (FileUtils.isStaleFileHandle(e)) {
warnTmpl = JGitText.get().packHandleIsStale;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index 731bbd3839..69f7e97071 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
@@ -262,6 +262,30 @@ public class RefDirectory extends RefDatabase {
}
@Override
+ public Ref exactRef(String name) throws IOException {
+ RefList<Ref> packed = getPackedRefs();
+ Ref ref;
+ try {
+ ref = readRef(name, packed);
+ if (ref != null) {
+ ref = resolve(ref, 0, null, null, packed);
+ }
+ } catch (IOException e) {
+ if (name.contains("/") //$NON-NLS-1$
+ || !(e.getCause() instanceof InvalidObjectIdException)) {
+ throw e;
+ }
+
+ // While looking for a ref outside of refs/ (e.g., 'config'), we
+ // found a non-ref file (e.g., a config file) instead. Treat this
+ // as a ref-not-found condition.
+ ref = null;
+ }
+ fireRefsChanged();
+ return ref;
+ }
+
+ @Override
public Ref getRef(final String needle) throws IOException {
final RefList<Ref> packed = getPackedRefs();
Ref ref = null;
@@ -270,6 +294,8 @@ public class RefDirectory extends RefDatabase {
ref = readRef(prefix + needle, packed);
if (ref != null) {
ref = resolve(ref, 0, null, null, packed);
+ }
+ if (ref != null) {
break;
}
} catch (IOException e) {
@@ -762,6 +788,9 @@ public class RefDirectory extends RefDatabase {
new DigestInputStream(new FileInputStream(packedRefsFile),
digest), CHARSET));
} catch (FileNotFoundException noPackedRefs) {
+ if (packedRefsFile.exists()) {
+ throw noPackedRefs;
+ }
// Ignore it and leave the new list empty.
return PackedRefList.NO_PACKED_REFS;
}
@@ -918,7 +947,10 @@ public class RefDirectory extends RefDatabase {
try {
buf = IO.readSome(path, limit);
} catch (FileNotFoundException noFile) {
- return null; // doesn't exist; not a reference.
+ if (path.exists() && path.isFile()) {
+ throw noFile;
+ }
+ return null; // doesn't exist or no file; not a reference.
}
int n = buf.length;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
index dadc631194..2f583b275a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
@@ -96,6 +96,9 @@ class ReflogReaderImpl implements ReflogReader {
try {
log = IO.readFully(logName);
} catch (FileNotFoundException e) {
+ if (logName.exists()) {
+ throw e;
+ }
return null;
}
@@ -118,6 +121,9 @@ class ReflogReaderImpl implements ReflogReader {
try {
log = IO.readFully(logName);
} catch (FileNotFoundException e) {
+ if (logName.exists()) {
+ throw e;
+ }
return Collections.emptyList();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
index 2cc2563962..a02743720d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
@@ -399,6 +399,9 @@ public class UnpackedObject {
try {
in = buffer(new FileInputStream(path));
} catch (FileNotFoundException gone) {
+ if (path.exists()) {
+ throw gone;
+ }
// If the loose file no longer exists, it may have been
// moved into a pack file in the mean time. Try again
// to locate the object.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index 535a6ee175..d30edaf41b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -273,6 +273,13 @@ public final class Constants {
public static final String INFO_EXCLUDE = "info/exclude";
/**
+ * Attributes-override-file
+ *
+ * @since 4.2
+ */
+ public static final String INFO_ATTRIBUTES = "info/attributes";
+
+ /**
* The system property that contains the system user name
*
* @since 3.6
@@ -363,6 +370,27 @@ public final class Constants {
*/
public static final String DOT_GIT_ATTRIBUTES = ".gitattributes";
+ /**
+ * Key for filters in .gitattributes
+ *
+ * @since 4.2
+ */
+ public static final String ATTR_FILTER = "filter";
+
+ /**
+ * clean command name, used to call filter driver
+ *
+ * @since 4.2
+ */
+ public static final String ATTR_FILTER_TYPE_CLEAN = "clean";
+
+ /**
+ * smudge command name, used to call filter driver
+ *
+ * @since 4.2
+ */
+ public static final String ATTR_FILTER_TYPE_SMUDGE = "smudge";
+
/** Name of the ignore file */
public static final String DOT_GIT_IGNORE = ".gitignore";
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
index 3fde2f919b..9e474f86a8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
@@ -74,6 +74,7 @@ import org.eclipse.jgit.treewalk.EmptyTreeIterator;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
import org.eclipse.jgit.treewalk.filter.IndexDiffFilter;
import org.eclipse.jgit.treewalk.filter.SkipWorkTreeFilter;
@@ -403,6 +404,7 @@ public class IndexDiff {
dirCache = repository.readDirCache();
try (TreeWalk treeWalk = new TreeWalk(repository)) {
+ treeWalk.setOperationType(OperationType.CHECKIN_OP);
treeWalk.setRecursive(true);
// add the trees (tree, dirchache, workdir)
if (tree != null)
@@ -411,6 +413,7 @@ public class IndexDiff {
treeWalk.addTree(new EmptyTreeIterator());
treeWalk.addTree(new DirCacheIterator(dirCache));
treeWalk.addTree(initialWorkingTreeIterator);
+ initialWorkingTreeIterator.setDirCacheIterator(treeWalk, 1);
Collection<TreeFilter> filters = new ArrayList<TreeFilter>(4);
if (monitor != null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
index ef22fb90fe..986666f2f4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -51,6 +51,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+
/**
* Abstraction of name to {@link ObjectId} mapping.
* <p>
@@ -132,6 +135,7 @@ public abstract class RefDatabase {
* @since 2.3
* @see #isNameConflicting(String)
*/
+ @NonNull
public Collection<String> getConflictingNames(String name)
throws IOException {
Map<String, Ref> allRefs = getRefs(ALL);
@@ -169,6 +173,7 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @NonNull
public abstract RefUpdate newUpdate(String name, boolean detach)
throws IOException;
@@ -183,6 +188,7 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @NonNull
public abstract RefRename newRename(String fromName, String toName)
throws IOException;
@@ -193,6 +199,7 @@ public abstract class RefDatabase {
*
* @return a new batch update object.
*/
+ @NonNull
public BatchRefUpdate newBatchUpdate() {
return new BatchRefUpdate(this);
}
@@ -223,6 +230,7 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @Nullable
public abstract Ref getRef(String name) throws IOException;
/**
@@ -238,21 +246,13 @@ public abstract class RefDatabase {
* the reference space cannot be accessed.
* @since 4.1
*/
+ @Nullable
public Ref exactRef(String name) throws IOException {
- int slash = name.lastIndexOf('/');
- String prefix = name.substring(0, slash + 1);
- String rest = name.substring(slash + 1);
- Ref result = getRefs(prefix).get(rest);
- if (result != null || slash != -1) {
- return result;
- }
-
- for (Ref ref : getAdditionalRefs()) {
- if (name.equals(ref.getName())) {
- return ref;
- }
+ Ref ref = getRef(name);
+ if (ref == null || !name.equals(ref.getName())) {
+ return null;
}
- return null;
+ return ref;
}
/**
@@ -270,6 +270,7 @@ public abstract class RefDatabase {
* the reference space cannot be accessed.
* @since 4.1
*/
+ @NonNull
public Map<String, Ref> exactRef(String... refs) throws IOException {
Map<String, Ref> result = new HashMap<>(refs.length);
for (String name : refs) {
@@ -294,6 +295,7 @@ public abstract class RefDatabase {
* the reference space cannot be accessed.
* @since 4.1
*/
+ @Nullable
public Ref firstExactRef(String... refs) throws IOException {
for (String name : refs) {
Ref ref = exactRef(name);
@@ -317,6 +319,7 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @NonNull
public abstract Map<String, Ref> getRefs(String prefix) throws IOException;
/**
@@ -331,6 +334,7 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @NonNull
public abstract List<Ref> getAdditionalRefs() throws IOException;
/**
@@ -347,10 +351,11 @@ public abstract class RefDatabase {
* @return {@code ref} if {@code ref.isPeeled()} is true; otherwise a new
* Ref object representing the same data as Ref, but isPeeled() will
* be true and getPeeledObjectId() will contain the peeled object
- * (or null).
+ * (or {@code null}).
* @throws IOException
* the reference space or object space cannot be accessed.
*/
+ @NonNull
public abstract Ref peel(Ref ref) throws IOException;
/**
@@ -374,9 +379,10 @@ public abstract class RefDatabase {
* @param name
* short name of ref to find, e.g. "master" to find
* "refs/heads/master" in map.
- * @return The first ref matching the name, or null if not found.
+ * @return The first ref matching the name, or {@code null} if not found.
* @since 3.4
*/
+ @Nullable
public static Ref findRef(Map<String, Ref> map, String name) {
for (String prefix : SEARCH_PATH) {
String fullname = prefix + name;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java
index 05eb31183d..59f852b8c7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java
@@ -170,7 +170,7 @@ public abstract class RefRename {
*/
protected boolean needToUpdateHEAD() throws IOException {
Ref head = source.getRefDatabase().getRef(Constants.HEAD);
- if (head.isSymbolic()) {
+ if (head != null && head.isSymbolic()) {
head = head.getTarget();
return head.getName().equals(source.getName());
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index d4c72cb9cb..49a970d03a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -64,6 +64,9 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.errors.CorruptObjectException;
@@ -137,6 +140,7 @@ public abstract class Repository implements AutoCloseable {
}
/** @return listeners observing only events on this repository. */
+ @NonNull
public ListenerList getListenerList() {
return myListeners;
}
@@ -181,7 +185,16 @@ public abstract class Repository implements AutoCloseable {
*/
public abstract void create(boolean bare) throws IOException;
- /** @return local metadata directory; null if repository isn't local. */
+ /**
+ * @return local metadata directory; {@code null} if repository isn't local.
+ */
+ /*
+ * TODO This method should be annotated as Nullable, because in some
+ * specific configurations metadata is not located in the local file system
+ * (for example in memory databases). In "usual" repositories this
+ * annotation would only cause compiler errors at places where the actual
+ * directory can never be null.
+ */
public File getDirectory() {
return gitDir;
}
@@ -189,28 +202,52 @@ public abstract class Repository implements AutoCloseable {
/**
* @return the object database which stores this repository's data.
*/
+ @NonNull
public abstract ObjectDatabase getObjectDatabase();
/** @return a new inserter to create objects in {@link #getObjectDatabase()} */
+ @NonNull
public ObjectInserter newObjectInserter() {
return getObjectDatabase().newInserter();
}
/** @return a new reader to read objects from {@link #getObjectDatabase()} */
+ @NonNull
public ObjectReader newObjectReader() {
return getObjectDatabase().newReader();
}
/** @return the reference database which stores the reference namespace. */
+ @NonNull
public abstract RefDatabase getRefDatabase();
/**
* @return the configuration of this repository
*/
+ @NonNull
public abstract StoredConfig getConfig();
/**
- * @return the used file system abstraction
+ * @return a new {@link AttributesNodeProvider}. This
+ * {@link AttributesNodeProvider} is lazy loaded only once. It means
+ * that it will not be updated after loading. Prefer creating new
+ * instance for each use.
+ * @since 4.2
+ */
+ @NonNull
+ public abstract AttributesNodeProvider createAttributesNodeProvider();
+
+
+ /**
+ * @return the used file system abstraction, or or {@code null} if
+ * repository isn't local.
+ */
+ /*
+ * TODO This method should be annotated as Nullable, because in some
+ * specific configurations metadata is not located in the local file system
+ * (for example in memory databases). In "usual" repositories this
+ * annotation would only cause compiler errors at places where the actual
+ * directory can never be null.
*/
public FS getFS() {
return fs;
@@ -244,6 +281,7 @@ public abstract class Repository implements AutoCloseable {
* @throws IOException
* the object store cannot be accessed.
*/
+ @NonNull
public ObjectLoader open(final AnyObjectId objectId)
throws MissingObjectException, IOException {
return getObjectDatabase().open(objectId);
@@ -271,6 +309,7 @@ public abstract class Repository implements AutoCloseable {
* @throws IOException
* the object store cannot be accessed.
*/
+ @NonNull
public ObjectLoader open(AnyObjectId objectId, int typeHint)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
@@ -289,6 +328,7 @@ public abstract class Repository implements AutoCloseable {
* a symbolic ref was passed in and could not be resolved back
* to the base ref, as the symbolic ref could not be read.
*/
+ @NonNull
public RefUpdate updateRef(final String ref) throws IOException {
return updateRef(ref, false);
}
@@ -307,6 +347,7 @@ public abstract class Repository implements AutoCloseable {
* a symbolic ref was passed in and could not be resolved back
* to the base ref, as the symbolic ref could not be read.
*/
+ @NonNull
public RefUpdate updateRef(final String ref, final boolean detach) throws IOException {
return getRefDatabase().newUpdate(ref, detach);
}
@@ -323,6 +364,7 @@ public abstract class Repository implements AutoCloseable {
* the rename could not be performed.
*
*/
+ @NonNull
public RefRename renameRef(final String fromRef, final String toRef) throws IOException {
return getRefDatabase().newRename(fromRef, toRef);
}
@@ -362,7 +404,8 @@ public abstract class Repository implements AutoCloseable {
*
* @param revstr
* A git object references expression
- * @return an ObjectId or null if revstr can't be resolved to any ObjectId
+ * @return an ObjectId or {@code null} if revstr can't be resolved to any
+ * ObjectId
* @throws AmbiguousObjectException
* {@code revstr} contains an abbreviated ObjectId and this
* repository contains more than one object which match to the
@@ -376,6 +419,7 @@ public abstract class Repository implements AutoCloseable {
* @throws IOException
* on serious errors
*/
+ @Nullable
public ObjectId resolve(final String revstr)
throws AmbiguousObjectException, IncorrectObjectTypeException,
RevisionSyntaxException, IOException {
@@ -397,10 +441,12 @@ public abstract class Repository implements AutoCloseable {
* expects a branch or revision id.
*
* @param revstr
- * @return object id or ref name from resolved expression
+ * @return object id or ref name from resolved expression or {@code null} if
+ * given expression cannot be resolved
* @throws AmbiguousObjectException
* @throws IOException
*/
+ @Nullable
public String simplify(final String revstr)
throws AmbiguousObjectException, IOException {
try (RevWalk rw = new RevWalk(this)) {
@@ -414,6 +460,7 @@ public abstract class Repository implements AutoCloseable {
}
}
+ @Nullable
private Object resolve(final RevWalk rw, final String revstr)
throws IOException {
char[] revChars = revstr.toCharArray();
@@ -717,11 +764,13 @@ public abstract class Repository implements AutoCloseable {
return true;
}
+ @Nullable
private RevObject parseSimple(RevWalk rw, String revstr) throws IOException {
ObjectId id = resolveSimple(revstr);
return id != null ? rw.parseAny(id) : null;
}
+ @Nullable
private ObjectId resolveSimple(final String revstr) throws IOException {
if (ObjectId.isId(revstr))
return ObjectId.fromString(revstr);
@@ -749,6 +798,7 @@ public abstract class Repository implements AutoCloseable {
return null;
}
+ @Nullable
private String resolveReflogCheckout(int checkoutNo)
throws IOException {
ReflogReader reader = getReflogReader(Constants.HEAD);
@@ -790,6 +840,7 @@ public abstract class Repository implements AutoCloseable {
return rw.parseCommit(entry.getNewId());
}
+ @Nullable
private ObjectId resolveAbbreviation(final String revstr) throws IOException,
AmbiguousObjectException {
AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr);
@@ -826,11 +877,13 @@ public abstract class Repository implements AutoCloseable {
getRefDatabase().close();
}
+ @NonNull
@SuppressWarnings("nls")
public String toString() {
String desc;
- if (getDirectory() != null)
- desc = getDirectory().getPath();
+ File directory = getDirectory();
+ if (directory != null)
+ desc = directory.getPath();
else
desc = getClass().getSimpleName() + "-" //$NON-NLS-1$
+ System.identityHashCode(this);
@@ -850,10 +903,12 @@ public abstract class Repository implements AutoCloseable {
* current ObjectId in hexadecimal string format.
*
* @return name of current branch (for example {@code refs/heads/master}),
- * an ObjectId in hex format if the current branch is detached,
- * or null if the repository is corrupt and has no HEAD reference.
+ * an ObjectId in hex format if the current branch is detached, or
+ * {@code null} if the repository is corrupt and has no HEAD
+ * reference.
* @throws IOException
*/
+ @Nullable
public String getFullBranch() throws IOException {
Ref head = getRef(Constants.HEAD);
if (head == null)
@@ -872,16 +927,17 @@ public abstract class Repository implements AutoCloseable {
* leading prefix {@code refs/heads/} is removed from the reference before
* it is returned to the caller.
*
- * @return name of current branch (for example {@code master}), an
- * ObjectId in hex format if the current branch is detached,
- * or null if the repository is corrupt and has no HEAD reference.
+ * @return name of current branch (for example {@code master}), an ObjectId
+ * in hex format if the current branch is detached, or {@code null}
+ * if the repository is corrupt and has no HEAD reference.
* @throws IOException
*/
+ @Nullable
public String getBranch() throws IOException {
String name = getFullBranch();
if (name != null)
return shortenRefName(name);
- return name;
+ return null;
}
/**
@@ -894,6 +950,7 @@ public abstract class Repository implements AutoCloseable {
*
* @return unmodifiable collection of other known objects.
*/
+ @NonNull
public Set<ObjectId> getAdditionalHaves() {
return Collections.emptySet();
}
@@ -905,16 +962,53 @@ public abstract class Repository implements AutoCloseable {
* the name of the ref to lookup. May be a short-hand form, e.g.
* "master" which is is automatically expanded to
* "refs/heads/master" if "refs/heads/master" already exists.
- * @return the Ref with the given name, or null if it does not exist
+ * @return the Ref with the given name, or {@code null} if it does not exist
* @throws IOException
+ * @deprecated Use {@link #exactRef(String)} or {@link #findRef(String)}
+ * instead.
*/
+ @Deprecated
+ @Nullable
public Ref getRef(final String name) throws IOException {
+ return findRef(name);
+ }
+
+ /**
+ * Get a ref by name.
+ *
+ * @param name
+ * the name of the ref to lookup. Must not be a short-hand
+ * form; e.g., "master" is not automatically expanded to
+ * "refs/heads/master".
+ * @return the Ref with the given name, or {@code null} if it does not exist
+ * @throws IOException
+ * @since 4.2
+ */
+ @Nullable
+ public Ref exactRef(String name) throws IOException {
+ return getRefDatabase().exactRef(name);
+ }
+
+ /**
+ * Search for a ref by (possibly abbreviated) name.
+ *
+ * @param name
+ * the name of the ref to lookup. May be a short-hand form, e.g.
+ * "master" which is is automatically expanded to
+ * "refs/heads/master" if "refs/heads/master" already exists.
+ * @return the Ref with the given name, or {@code null} if it does not exist
+ * @throws IOException
+ * @since 4.2
+ */
+ @Nullable
+ public Ref findRef(String name) throws IOException {
return getRefDatabase().getRef(name);
}
/**
* @return mutable map of all known refs (heads, tags, remotes).
*/
+ @NonNull
public Map<String, Ref> getAllRefs() {
try {
return getRefDatabase().getRefs(RefDatabase.ALL);
@@ -928,6 +1022,7 @@ public abstract class Repository implements AutoCloseable {
* of the entry contains the ref with the full tag name
* ("refs/tags/v1.0").
*/
+ @NonNull
public Map<String, Ref> getTags() {
try {
return getRefDatabase().getRefs(Constants.R_TAGS);
@@ -949,6 +1044,7 @@ public abstract class Repository implements AutoCloseable {
* will be true and getPeeledObjectId will contain the peeled object
* (or null).
*/
+ @NonNull
public Ref peel(final Ref ref) {
try {
return getRefDatabase().peel(ref);
@@ -963,6 +1059,7 @@ public abstract class Repository implements AutoCloseable {
/**
* @return a map with all objects referenced by a peeled ref.
*/
+ @NonNull
public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
Map<String, Ref> allRefs = getAllRefs();
Map<AnyObjectId, Set<Ref>> ret = new HashMap<AnyObjectId, Set<Ref>>(allRefs.size());
@@ -987,11 +1084,13 @@ public abstract class Repository implements AutoCloseable {
}
/**
- * @return the index file location
+ * @return the index file location or {@code null} if repository isn't
+ * local.
* @throws NoWorkTreeException
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @NonNull
public File getIndexFile() throws NoWorkTreeException {
if (isBare())
throw new NoWorkTreeException();
@@ -1016,6 +1115,7 @@ public abstract class Repository implements AutoCloseable {
* the index file is using a format or extension that this
* library does not support.
*/
+ @NonNull
public DirCache readDirCache() throws NoWorkTreeException,
CorruptObjectException, IOException {
return DirCache.read(this);
@@ -1040,6 +1140,7 @@ public abstract class Repository implements AutoCloseable {
* the index file is using a format or extension that this
* library does not support.
*/
+ @NonNull
public DirCache lockDirCache() throws NoWorkTreeException,
CorruptObjectException, IOException {
// we want DirCache to inform us so that we can inform registered
@@ -1065,6 +1166,7 @@ public abstract class Repository implements AutoCloseable {
/**
* @return an important state
*/
+ @NonNull
public RepositoryState getRepositoryState() {
if (isBare() || getDirectory() == null)
return RepositoryState.BARE;
@@ -1207,6 +1309,7 @@ public abstract class Repository implements AutoCloseable {
* @return normalized repository relative path or the empty
* string if the file is not relative to the work directory.
*/
+ @NonNull
public static String stripWorkDir(File workDir, File file) {
final String filePath = file.getPath();
final String workDirPath = workDir.getPath();
@@ -1241,6 +1344,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @NonNull
public File getWorkTree() throws NoWorkTreeException {
if (isBare())
throw new NoWorkTreeException();
@@ -1264,6 +1368,7 @@ public abstract class Repository implements AutoCloseable {
*
* @return a more user friendly ref name
*/
+ @NonNull
public static String shortenRefName(String refName) {
if (refName.startsWith(Constants.R_HEADS))
return refName.substring(Constants.R_HEADS.length());
@@ -1279,9 +1384,10 @@ public abstract class Repository implements AutoCloseable {
* @return the remote branch name part of <code>refName</code>, i.e. without
* the <code>refs/remotes/&lt;remote&gt;</code> prefix, if
* <code>refName</code> represents a remote tracking branch;
- * otherwise null.
+ * otherwise {@code null}.
* @since 3.4
*/
+ @Nullable
public String shortenRemoteBranchName(String refName) {
for (String remote : getRemoteNames()) {
String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
@@ -1296,9 +1402,10 @@ public abstract class Repository implements AutoCloseable {
* @return the remote name part of <code>refName</code>, i.e. without the
* <code>refs/remotes/&lt;remote&gt;</code> prefix, if
* <code>refName</code> represents a remote tracking branch;
- * otherwise null.
+ * otherwise {@code null}.
* @since 3.4
*/
+ @Nullable
public String getRemoteName(String refName) {
for (String remote : getRemoteNames()) {
String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
@@ -1310,12 +1417,13 @@ public abstract class Repository implements AutoCloseable {
/**
* @param refName
- * @return a {@link ReflogReader} for the supplied refname, or null if the
- * named ref does not exist.
+ * @return a {@link ReflogReader} for the supplied refname, or {@code null}
+ * if the named ref does not exist.
* @throws IOException
* the ref could not be accessed.
* @since 3.0
*/
+ @Nullable
public abstract ReflogReader getReflogReader(String refName)
throws IOException;
@@ -1331,6 +1439,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
return readCommitMsgFile(Constants.MERGE_MSG);
}
@@ -1365,6 +1474,7 @@ public abstract class Repository implements AutoCloseable {
* See {@link #isBare()}.
* @since 4.0
*/
+ @Nullable
public String readCommitEditMsg() throws IOException, NoWorkTreeException {
return readCommitMsgFile(Constants.COMMIT_EDITMSG);
}
@@ -1399,6 +1509,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException {
if (isBare() || getDirectory() == null)
throw new NoWorkTreeException();
@@ -1442,6 +1553,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public ObjectId readCherryPickHead() throws IOException,
NoWorkTreeException {
if (isBare() || getDirectory() == null)
@@ -1465,6 +1577,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public ObjectId readRevertHead() throws IOException, NoWorkTreeException {
if (isBare() || getDirectory() == null)
throw new NoWorkTreeException();
@@ -1530,6 +1643,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public ObjectId readOrigHead() throws IOException, NoWorkTreeException {
if (isBare() || getDirectory() == null)
throw new NoWorkTreeException();
@@ -1550,6 +1664,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public String readSquashCommitMsg() throws IOException {
return readCommitMsgFile(Constants.SQUASH_MSG);
}
@@ -1571,6 +1686,7 @@ public abstract class Repository implements AutoCloseable {
writeCommitMsg(squashMsgFile, msg);
}
+ @Nullable
private String readCommitMsgFile(String msgFilename) throws IOException {
if (isBare() || getDirectory() == null)
throw new NoWorkTreeException();
@@ -1579,6 +1695,9 @@ public abstract class Repository implements AutoCloseable {
try {
return RawParseUtils.decode(IO.readFully(mergeMsgFile));
} catch (FileNotFoundException e) {
+ if (mergeMsgFile.exists()) {
+ throw e;
+ }
// the file has disappeared in the meantime ignore it
return null;
}
@@ -1601,15 +1720,20 @@ public abstract class Repository implements AutoCloseable {
* Read a file from the git directory.
*
* @param filename
- * @return the raw contents or null if the file doesn't exist or is empty
+ * @return the raw contents or {@code null} if the file doesn't exist or is
+ * empty
* @throws IOException
*/
+ @Nullable
private byte[] readGitDirectoryFile(String filename) throws IOException {
File file = new File(getDirectory(), filename);
try {
byte[] raw = IO.readFully(file);
return raw.length > 0 ? raw : null;
} catch (FileNotFoundException notFound) {
+ if (file.exists()) {
+ throw notFound;
+ }
return null;
}
}
@@ -1657,6 +1781,7 @@ public abstract class Repository implements AutoCloseable {
* @throws IOException
* @since 3.2
*/
+ @NonNull
public List<RebaseTodoLine> readRebaseTodo(String path,
boolean includeComments)
throws IOException {
@@ -1686,6 +1811,7 @@ public abstract class Repository implements AutoCloseable {
* @return the names of all known remotes
* @since 3.4
*/
+ @NonNull
public Set<String> getRemoteNames() {
return getConfig()
.getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
index aef47c58cf..e0556447ce 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
@@ -57,8 +57,6 @@ import java.util.List;
import java.util.TimeZone;
import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.dircache.DirCacheBuilder;
-import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.NoMergeBaseException;
import org.eclipse.jgit.internal.JGitText;
@@ -70,7 +68,6 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
-import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
/**
@@ -181,7 +178,7 @@ public class RecursiveMerger extends ResolveMerger {
WorkingTreeIterator oldWTreeIt = workingTreeIterator;
workingTreeIterator = null;
try {
- dircache = dircacheFromTree(currentBase.getTree());
+ dircache = DirCache.read(reader, currentBase.getTree());
inCore = true;
List<RevCommit> parents = new ArrayList<RevCommit>();
@@ -256,30 +253,4 @@ public class RecursiveMerger extends ResolveMerger {
new Date((time + 1) * 1000L),
TimeZone.getTimeZone("GMT+0000")); //$NON-NLS-1$
}
-
- /**
- * Create a new in memory dircache which has the same content as a given
- * tree.
- *
- * @param treeId
- * the tree which should be used to fill the dircache
- * @return a new in memory dircache
- * @throws IOException
- */
- private DirCache dircacheFromTree(ObjectId treeId) throws IOException {
- DirCache ret = DirCache.newInCore();
- DirCacheBuilder aBuilder = ret.builder();
- try (TreeWalk atw = new TreeWalk(reader)) {
- atw.addTree(treeId);
- atw.setRecursive(true);
- while (atw.next()) {
- DirCacheEntry e = new DirCacheEntry(atw.getRawPath());
- e.setFileMode(atw.getFileMode(0));
- e.setObjectId(atw.getObjectId(0));
- aBuilder.add(e);
- }
- }
- aBuilder.finish();
- return ret;
- }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index 8a6343c3cc..de08e4b6a0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -786,11 +786,6 @@ public class ResolveMerger extends ThreeWayMerger {
private File writeMergedFile(MergeResult<RawText> result)
throws FileNotFoundException, IOException {
File workTree = db.getWorkTree();
- if (workTree == null)
- // TODO: This should be handled by WorkingTreeIterators which
- // support write operations
- throw new UnsupportedOperationException();
-
FS fs = db.getFS();
File of = new File(workTree, tw.getPathString());
File parentFolder = of.getParentFile();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index 5509fc6a70..29c7f98411 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -163,6 +163,9 @@ public class FileBasedConfig extends StoredConfig {
hash = newHash;
}
} catch (FileNotFoundException noFile) {
+ if (configFile.exists()) {
+ throw noFile;
+ }
clear();
snapshot = newSnapshot;
} catch (IOException e) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
index 24fb3be64c..0834c359aa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
@@ -44,6 +44,8 @@
package org.eclipse.jgit.transport;
+import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_ATOMIC;
+
import java.io.IOException;
import java.io.OutputStream;
import java.text.MessageFormat;
@@ -110,17 +112,15 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
public static final String CAPABILITY_SIDE_BAND_64K = GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
private final boolean thinPack;
+ private final boolean atomic;
+ private boolean capableAtomic;
private boolean capableDeleteRefs;
-
private boolean capableReport;
-
private boolean capableSideBand;
-
private boolean capableOfsDelta;
private boolean sentCommand;
-
private boolean writePack;
/** Time in milliseconds spent transferring the pack data. */
@@ -135,6 +135,7 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
public BasePackPushConnection(final PackTransport packTransport) {
super(packTransport);
thinPack = transport.isPushThin();
+ atomic = transport.isPushAtomic();
}
public void push(final ProgressMonitor monitor,
@@ -224,6 +225,11 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
private void writeCommands(final Collection<RemoteRefUpdate> refUpdates,
final ProgressMonitor monitor, OutputStream outputStream) throws IOException {
final String capabilities = enableCapabilities(monitor, outputStream);
+ if (atomic && !capableAtomic) {
+ throw new TransportException(uri,
+ JGitText.get().atomicPushNotSupported);
+ }
+
for (final RemoteRefUpdate rru : refUpdates) {
if (!capableDeleteRefs && rru.isDelete()) {
rru.setStatus(Status.REJECTED_NODELETE);
@@ -231,9 +237,11 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
}
final StringBuilder sb = new StringBuilder();
- final Ref advertisedRef = getRef(rru.getRemoteName());
- final ObjectId oldId = (advertisedRef == null ? ObjectId.zeroId()
- : advertisedRef.getObjectId());
+ ObjectId oldId = rru.getExpectedOldObjectId();
+ if (oldId == null) {
+ Ref adv = getRef(rru.getRemoteName());
+ oldId = adv != null ? adv.getObjectId() : ObjectId.zeroId();
+ }
sb.append(oldId.name());
sb.append(' ');
sb.append(rru.getNewObjectId().name());
@@ -259,6 +267,8 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
private String enableCapabilities(final ProgressMonitor monitor,
OutputStream outputStream) {
final StringBuilder line = new StringBuilder();
+ if (atomic)
+ capableAtomic = wantCapability(line, CAPABILITY_ATOMIC);
capableReport = wantCapability(line, CAPABILITY_REPORT_STATUS);
capableDeleteRefs = wantCapability(line, CAPABILITY_DELETE_REFS);
capableOfsDelta = wantCapability(line, CAPABILITY_OFS_DELTA);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
index 7e9434a0f0..622680a27f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
@@ -42,6 +42,7 @@
*/
package org.eclipse.jgit.transport;
+import java.io.File;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@@ -85,12 +86,16 @@ public class HMACSHA1NonceGenerator implements NonceGenerator {
public synchronized String createNonce(Repository repo, long timestamp)
throws IllegalStateException {
String path;
- if (repo instanceof DfsRepository)
+ if (repo instanceof DfsRepository) {
path = ((DfsRepository) repo).getDescription().getRepositoryName();
- else if (repo.getDirectory() != null)
- path = repo.getDirectory().getPath();
- else
- throw new IllegalStateException();
+ } else {
+ File directory = repo.getDirectory();
+ if (directory != null) {
+ path = directory.getPath();
+ } else {
+ throw new IllegalStateException();
+ }
+ }
String input = path + ":" + String.valueOf(timestamp); //$NON-NLS-1$
byte[] rawHmac;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
index 3594ea91b4..998f280014 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
@@ -219,7 +219,8 @@ abstract class HttpAuthMethod {
if (credentialsProvider.supports(u, p)
&& credentialsProvider.get(uri, u, p)) {
username = u.getValue();
- password = new String(p.getValue());
+ char[] v = p.getValue();
+ password = (v == null) ? null : new String(p.getValue());
p.clear();
} else
return false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
index 8947f2779d..d436e08df1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
@@ -65,7 +65,6 @@ import java.util.Map;
import java.util.NoSuchElementException;
import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEditor;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
import org.eclipse.jgit.dircache.DirCacheEntry;
@@ -448,13 +447,10 @@ public class PushCertificateStore implements AutoCloseable {
}
private DirCache newDirCache() throws IOException {
- DirCache dc = DirCache.newInCore();
if (commit != null) {
- DirCacheBuilder b = dc.builder();
- b.addTree(new byte[0], DirCacheEntry.STAGE_0, reader, commit.getTree());
- b.finish();
+ return DirCache.read(reader, commit.getTree());
}
- return dc;
+ return DirCache.newInCore();
}
private ObjectId saveCert(ObjectInserter inserter, DirCache dc,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
index 9721ee9eb0..4fd192dbb2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
@@ -47,6 +47,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -183,6 +184,7 @@ class PushProcess {
private Map<String, RemoteRefUpdate> prepareRemoteUpdates()
throws TransportException {
+ boolean atomic = transport.isPushAtomic();
final Map<String, RemoteRefUpdate> result = new HashMap<String, RemoteRefUpdate>();
for (final RemoteRefUpdate rru : toPush.values()) {
final Ref advertisedRef = connection.getRef(rru.getRemoteName());
@@ -205,8 +207,14 @@ class PushProcess {
if (rru.isExpectingOldObjectId()
&& !rru.getExpectedOldObjectId().equals(advertisedOld)) {
rru.setStatus(Status.REJECTED_REMOTE_CHANGED);
+ if (atomic) {
+ return rejectAll();
+ }
continue;
}
+ if (!rru.isExpectingOldObjectId()) {
+ rru.setExpectedOldObjectId(advertisedOld);
+ }
// create ref (hasn't existed on remote side) and delete ref
// are always fast-forward commands, feasible at this level
@@ -236,14 +244,28 @@ class PushProcess {
JGitText.get().readingObjectsFromLocalRepositoryFailed, x.getMessage()), x);
}
rru.setFastForward(fastForward);
- if (!fastForward && !rru.isForceUpdate())
+ if (!fastForward && !rru.isForceUpdate()) {
rru.setStatus(Status.REJECTED_NONFASTFORWARD);
- else
+ if (atomic) {
+ return rejectAll();
+ }
+ } else {
result.put(rru.getRemoteName(), rru);
+ }
}
return result;
}
+ private Map<String, RemoteRefUpdate> rejectAll() {
+ for (RemoteRefUpdate rru : toPush.values()) {
+ if (rru.getStatus() == Status.NOT_ATTEMPTED) {
+ rru.setStatus(RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
+ rru.setMessage(JGitText.get().transactionAborted);
+ }
+ }
+ return Collections.emptyMap();
+ }
+
private void modifyUpdatesForDryRun() {
for (final RemoteRefUpdate rru : toPush.values())
if (rru.getStatus() == Status.NOT_ATTEMPTED)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java
index 7c44dba4a2..5702b6d7b9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java
@@ -46,6 +46,7 @@ package org.eclipse.jgit.transport;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import org.eclipse.jgit.internal.JGitText;
@@ -127,26 +128,46 @@ public class ReceiveCommand {
}
/**
- * Filter a list of commands according to result.
+ * Filter a collection of commands according to result.
*
- * @param commands
+ * @param in
* commands to filter.
* @param want
* desired status to filter by.
* @return a copy of the command list containing only those commands with
* the desired status.
- * @since 2.0
+ * @since 4.2
*/
- public static List<ReceiveCommand> filter(List<ReceiveCommand> commands,
- final Result want) {
- List<ReceiveCommand> r = new ArrayList<ReceiveCommand>(commands.size());
- for (final ReceiveCommand cmd : commands) {
+ public static List<ReceiveCommand> filter(Iterable<ReceiveCommand> in,
+ Result want) {
+ List<ReceiveCommand> r;
+ if (in instanceof Collection)
+ r = new ArrayList<>(((Collection<?>) in).size());
+ else
+ r = new ArrayList<>();
+ for (ReceiveCommand cmd : in) {
if (cmd.getResult() == want)
r.add(cmd);
}
return r;
}
+ /**
+ * Filter a list of commands according to result.
+ *
+ * @param commands
+ * commands to filter.
+ * @param want
+ * desired status to filter by.
+ * @return a copy of the command list containing only those commands with
+ * the desired status.
+ * @since 2.0
+ */
+ public static List<ReceiveCommand> filter(List<ReceiveCommand> commands,
+ Result want) {
+ return filter((Iterable<ReceiveCommand>) commands, want);
+ }
+
private final ObjectId oldId;
private final ObjectId newId;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
index 1b82a36105..5c58346189 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
@@ -125,7 +125,7 @@ public class RemoteRefUpdate {
OK;
}
- private final ObjectId expectedOldObjectId;
+ private ObjectId expectedOldObjectId;
private final ObjectId newObjectId;
@@ -440,6 +440,10 @@ public class RemoteRefUpdate {
return message;
}
+ void setExpectedOldObjectId(ObjectId id) {
+ expectedOldObjectId = id;
+ }
+
void setStatus(final Status status) {
this.status = status;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
index cc7db47df5..6af153cbc9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -752,6 +752,9 @@ public abstract class Transport {
/** Should push produce thin-pack when sending objects to remote repository. */
private boolean pushThin = DEFAULT_PUSH_THIN;
+ /** Should push be all-or-nothing atomic behavior? */
+ private boolean pushAtomic;
+
/** Should push just check for operation result, not really push. */
private boolean dryRun;
@@ -970,6 +973,31 @@ public abstract class Transport {
}
/**
+ * Default setting is false.
+ *
+ * @return true if push requires all-or-nothing atomic behavior.
+ * @since 4.2
+ */
+ public boolean isPushAtomic() {
+ return pushAtomic;
+ }
+
+ /**
+ * Request atomic push (all references succeed, or none do).
+ * <p>
+ * Server must also support atomic push. If the server does not support the
+ * feature the push will abort without making changes.
+ *
+ * @param atomic
+ * true when push should be an all-or-nothing operation.
+ * @see PackTransport
+ * @since 4.2
+ */
+ public void setPushAtomic(final boolean atomic) {
+ this.pushAtomic = atomic;
+ }
+
+ /**
* @return true if destination refs should be removed if they no longer
* exist at the source repository.
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
index 7729c11ff9..23c506b128 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
@@ -148,8 +148,9 @@ public class TransportAmazonS3 extends HttpTransport implements WalkTransport {
super(local, uri);
Properties props = loadProperties();
- if (!props.containsKey("tmpdir") && local.getDirectory() != null) //$NON-NLS-1$
- props.put("tmpdir", local.getDirectory().getPath()); //$NON-NLS-1$
+ File directory = local.getDirectory();
+ if (!props.containsKey("tmpdir") && directory != null) //$NON-NLS-1$
+ props.put("tmpdir", directory.getPath()); //$NON-NLS-1$
s3 = new AmazonS3(props);
bucket = uri.getHost();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
index b27fa0d6b2..52f0f04562 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
@@ -46,6 +46,7 @@
package org.eclipse.jgit.transport;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
@@ -235,9 +236,10 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
ProcessBuilder pb = new ProcessBuilder();
pb.command(args);
- if (local.getDirectory() != null)
+ File directory = local.getDirectory();
+ if (directory != null)
pb.environment().put(Constants.GIT_DIR_KEY,
- local.getDirectory().getPath());
+ directory.getPath());
try {
return pb.start();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
index 3700b49555..9aeb840ebe 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
@@ -137,7 +137,11 @@ public class URIish implements Serializable {
+ OPT_PORT_P //
+ "(" // open a group capturing the user-home-dir-part //$NON-NLS-1$
+ (USER_HOME_P + "?") //$NON-NLS-1$
- + "[\\\\/])" //$NON-NLS-1$
+ + "(?:" // start non capturing group for host //$NON-NLS-1$
+ // separator or end of line
+ + "[\\\\/])|$" //$NON-NLS-1$
+ + ")" // close non capturing group for the host//$NON-NLS-1$
+ // separator or end of line
+ ")?" // close the optional group containing hostname //$NON-NLS-1$
+ "(.+)?" //$NON-NLS-1$
+ "$"); //$NON-NLS-1$
@@ -593,6 +597,8 @@ public class URIish implements Serializable {
private static boolean eq(final String a, final String b) {
if (a == b)
return true;
+ if (StringUtils.isEmptyOrNull(a) && StringUtils.isEmptyOrNull(b))
+ return true;
if (a == null || b == null)
return false;
return a.equals(b);
@@ -638,7 +644,7 @@ public class URIish implements Serializable {
if (getPath() != null) {
if (getScheme() != null) {
- if (!getPath().startsWith("/")) //$NON-NLS-1$
+ if (!getPath().startsWith("/") && !getPath().isEmpty()) //$NON-NLS-1$
r.append('/');
} else if (getHost() != null)
r.append(':');
@@ -709,9 +715,9 @@ public class URIish implements Serializable {
*/
public String getHumanishName() throws IllegalArgumentException {
String s = getPath();
- if ("/".equals(s)) //$NON-NLS-1$
+ if ("/".equals(s) || "".equals(s)) //$NON-NLS-1$
s = getHost();
- if ("".equals(s) || s == null) //$NON-NLS-1$
+ if (s == null) // $NON-NLS-1$
throw new IllegalArgumentException();
String[] elements;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
index 41e593eaa2..5e71889574 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
@@ -49,6 +49,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
+import org.eclipse.jgit.attributes.AttributesNode;
import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -93,6 +94,13 @@ public abstract class AbstractTreeIterator {
AbstractTreeIterator matches;
/**
+ * Parsed rules of .gitattributes file if it exists.
+ *
+ * @since 4.2
+ */
+ protected AttributesNode attributesNode;
+
+ /**
* Number of entries we moved forward to force a D/F conflict match.
*
* @see NameConflictTreeWalk
@@ -320,6 +328,42 @@ public abstract class AbstractTreeIterator {
}
/**
+ * Seek the iterator on a file, if present.
+ *
+ * @param name
+ * file name to find (will not find a directory).
+ * @return true if the file exists in this tree; false otherwise.
+ * @throws CorruptObjectException
+ * tree is invalid.
+ * @since 4.2
+ */
+ public boolean findFile(String name) throws CorruptObjectException {
+ return findFile(Constants.encode(name));
+ }
+
+ /**
+ * Seek the iterator on a file, if present.
+ *
+ * @param name
+ * file name to find (will not find a directory).
+ * @return true if the file exists in this tree; false otherwise.
+ * @throws CorruptObjectException
+ * tree is invalid.
+ * @since 4.2
+ */
+ public boolean findFile(byte[] name) throws CorruptObjectException {
+ for (; !eof(); next(1)) {
+ int cmp = pathCompare(name, 0, name.length, 0, pathOffset);
+ if (cmp == 0) {
+ return true;
+ } else if (cmp > 0) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
* Compare the path of this current entry to a raw buffer.
*
* @param buf
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
index 0805e5068c..c038f07725 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
@@ -44,13 +44,23 @@
package org.eclipse.jgit.treewalk;
+import static org.eclipse.jgit.lib.Constants.DOT_GIT_ATTRIBUTES;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+import static org.eclipse.jgit.lib.Constants.TYPE_TREE;
+import static org.eclipse.jgit.lib.Constants.encode;
+
import java.io.IOException;
+import java.io.InputStream;
import java.util.Arrays;
+import java.util.Collections;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesRule;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
@@ -59,6 +69,7 @@ import org.eclipse.jgit.lib.ObjectReader;
/** Parses raw Git trees from the canonical semi-text/semi-binary format. */
public class CanonicalTreeParser extends AbstractTreeIterator {
private static final byte[] EMPTY = {};
+ private static final byte[] ATTRS = encode(DOT_GIT_ATTRIBUTES);
private byte[] raw;
@@ -124,6 +135,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
* the raw tree content.
*/
public void reset(final byte[] treeData) {
+ attributesNode = null;
raw = treeData;
prevPtr = -1;
currPtr = 0;
@@ -199,7 +211,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
*/
public void reset(final ObjectReader reader, final AnyObjectId id)
throws IncorrectObjectTypeException, IOException {
- reset(reader.open(id, Constants.OBJ_TREE).getCachedBytes());
+ reset(reader.open(id, OBJ_TREE).getCachedBytes());
}
@Override
@@ -209,7 +221,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
idBuffer.fromRaw(idBuffer(), idOffset());
if (!FileMode.TREE.equals(mode)) {
final ObjectId me = idBuffer.toObjectId();
- throw new IncorrectObjectTypeException(me, Constants.TYPE_TREE);
+ throw new IncorrectObjectTypeException(me, TYPE_TREE);
}
return createSubtreeIterator0(reader, idBuffer);
}
@@ -254,7 +266,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
@Override
public int idOffset() {
- return nextPtr - Constants.OBJECT_ID_LENGTH;
+ return nextPtr - OBJECT_ID_LENGTH;
}
@Override
@@ -292,7 +304,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
prevPtr = ptr;
while (raw[ptr] != 0)
ptr++;
- ptr += Constants.OBJECT_ID_LENGTH + 1;
+ ptr += OBJECT_ID_LENGTH + 1;
}
if (delta != 0)
throw new ArrayIndexOutOfBoundsException(delta);
@@ -328,7 +340,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
trace[delta] = ptr;
while (raw[ptr] != 0)
ptr++;
- ptr += Constants.OBJECT_ID_LENGTH + 1;
+ ptr += OBJECT_ID_LENGTH + 1;
}
if (trace[1] == -1)
throw new ArrayIndexOutOfBoundsException(delta);
@@ -363,6 +375,46 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
}
}
pathLen = tmp;
- nextPtr = ptr + Constants.OBJECT_ID_LENGTH;
+ nextPtr = ptr + OBJECT_ID_LENGTH;
+ }
+
+ /**
+ * Retrieve the {@link AttributesNode} for the current entry.
+ *
+ * @param reader
+ * {@link ObjectReader} used to parse the .gitattributes entry.
+ * @return {@link AttributesNode} for the current entry.
+ * @throws IOException
+ * @since 4.2
+ */
+ public AttributesNode getEntryAttributesNode(ObjectReader reader)
+ throws IOException {
+ if (attributesNode == null) {
+ attributesNode = findAttributes(reader);
+ }
+ return attributesNode.getRules().isEmpty() ? null : attributesNode;
+ }
+
+ private AttributesNode findAttributes(ObjectReader reader)
+ throws IOException {
+ CanonicalTreeParser itr = new CanonicalTreeParser();
+ itr.reset(raw);
+ if (itr.findFile(ATTRS)) {
+ return loadAttributes(reader, itr.getEntryObjectId());
+ }
+ return noAttributes();
+ }
+
+ private static AttributesNode loadAttributes(ObjectReader reader,
+ AnyObjectId id) throws IOException {
+ AttributesNode r = new AttributesNode();
+ try (InputStream in = reader.open(id, OBJ_BLOB).openStream()) {
+ r.parse(in);
+ }
+ return r.getRules().isEmpty() ? noAttributes() : r;
+ }
+
+ private static AttributesNode noAttributes() {
+ return new AttributesNode(Collections.<AttributesRule> emptyList());
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
index 8d2cb1d8cd..accf4956f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
@@ -67,8 +67,8 @@ import org.eclipse.jgit.util.FS;
*/
public class FileTreeIterator extends WorkingTreeIterator {
/**
- * the starting directory. This directory should correspond to the root of
- * the repository.
+ * the starting directory of this Iterator. All entries are located directly
+ * in this directory.
*/
protected final File directory;
@@ -238,8 +238,6 @@ public class FileTreeIterator extends WorkingTreeIterator {
@Override
protected byte[] idSubmodule(final Entry e) {
- if (repository == null)
- return idSubmodule(getDirectory(), e);
- return super.idSubmodule(e);
+ return idSubmodule(getDirectory(), e);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
index 2d6acbddf0..350f563964 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
@@ -96,7 +96,7 @@ public class NameConflictTreeWalk extends TreeWalk {
* the repository the walker will obtain data from.
*/
public NameConflictTreeWalk(final Repository repo) {
- this(repo.newObjectReader());
+ super(repo);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
index 06e828419d..06dc0bf6d0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
@@ -45,12 +45,25 @@
package org.eclipse.jgit.treewalk;
import java.io.IOException;
-
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.attributes.Attribute;
+import org.eclipse.jgit.attributes.Attribute.State;
+import org.eclipse.jgit.attributes.Attributes;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
+import org.eclipse.jgit.attributes.AttributesProvider;
+import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StopWalkException;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
@@ -60,6 +73,7 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.eclipse.jgit.util.QuotedString;
import org.eclipse.jgit.util.RawParseUtils;
/**
@@ -82,10 +96,45 @@ import org.eclipse.jgit.util.RawParseUtils;
* Multiple simultaneous TreeWalk instances per {@link Repository} are
* permitted, even from concurrent threads.
*/
-public class TreeWalk implements AutoCloseable {
+public class TreeWalk implements AutoCloseable, AttributesProvider {
private static final AbstractTreeIterator[] NO_TREES = {};
/**
+ * @since 4.2
+ */
+ public static enum OperationType {
+ /**
+ * Represents a checkout operation (for example a checkout or reset
+ * operation).
+ */
+ CHECKOUT_OP,
+
+ /**
+ * Represents a checkin operation (for example an add operation)
+ */
+ CHECKIN_OP
+ }
+
+ /**
+ * Type of operation you want to retrieve the git attributes for.
+ */
+ private OperationType operationType = OperationType.CHECKOUT_OP;
+
+ /**
+ * The filter command as defined in gitattributes. The keys are
+ * filterName+"."+filterCommandType. E.g. "lfs.clean"
+ */
+ private Map<String, String> filterCommandsByNameDotType = new HashMap<String, String>();
+
+ /**
+ * @param operationType
+ * @since 4.2
+ */
+ public void setOperationType(OperationType operationType) {
+ this.operationType = operationType;
+ }
+
+ /**
* Open a tree walk and filter to exactly one path.
* <p>
* The returned tree walk is already positioned on the requested path, so
@@ -213,8 +262,15 @@ public class TreeWalk implements AutoCloseable {
private boolean postChildren;
+ private AttributesNodeProvider attributesNodeProvider;
+
AbstractTreeIterator currentHead;
+ /** Cached attribute for the current entry */
+ private Attributes attrs = null;
+
+ private Config config;
+
/**
* Create a new tree walker for a given repository.
*
@@ -225,6 +281,8 @@ public class TreeWalk implements AutoCloseable {
*/
public TreeWalk(final Repository repo) {
this(repo.newObjectReader(), true);
+ config = repo.getConfig();
+ attributesNodeProvider = repo.createAttributesNodeProvider();
}
/**
@@ -356,8 +414,29 @@ public class TreeWalk implements AutoCloseable {
postOrderTraversal = b;
}
+ /**
+ * Sets the {@link AttributesNodeProvider} for this {@link TreeWalk}.
+ * <p>
+ * This is a requirement for a correct computation of the git attributes.
+ * If this {@link TreeWalk} has been built using
+ * {@link #TreeWalk(Repository)} constructor, the
+ * {@link AttributesNodeProvider} has already been set. Indeed,the
+ * {@link Repository} can provide an {@link AttributesNodeProvider} using
+ * {@link Repository#createAttributesNodeProvider()} method. Otherwise you
+ * should provide one.
+ * </p>
+ *
+ * @see Repository#createAttributesNodeProvider()
+ * @param provider
+ * @since 4.2
+ */
+ public void setAttributesNodeProvider(AttributesNodeProvider provider) {
+ attributesNodeProvider = provider;
+ }
+
/** Reset this walker so new tree iterators can be added to it. */
public void reset() {
+ attrs = null;
trees = NO_TREES;
advance = false;
depth = 0;
@@ -401,6 +480,7 @@ public class TreeWalk implements AutoCloseable {
advance = false;
depth = 0;
+ attrs = null;
}
/**
@@ -450,6 +530,7 @@ public class TreeWalk implements AutoCloseable {
trees = r;
advance = false;
depth = 0;
+ attrs = null;
}
/**
@@ -546,6 +627,7 @@ public class TreeWalk implements AutoCloseable {
public boolean next() throws MissingObjectException,
IncorrectObjectTypeException, CorruptObjectException, IOException {
try {
+ attrs = null;
if (advance) {
advance = false;
postChildren = false;
@@ -915,6 +997,7 @@ public class TreeWalk implements AutoCloseable {
*/
public void enterSubtree() throws MissingObjectException,
IncorrectObjectTypeException, CorruptObjectException, IOException {
+ attrs = null;
final AbstractTreeIterator ch = currentHead;
final AbstractTreeIterator[] tmp = new AbstractTreeIterator[trees.length];
for (int i = 0; i < trees.length; i++) {
@@ -1008,4 +1091,296 @@ public class TreeWalk implements AutoCloseable {
static String pathOf(final byte[] buf, int pos, int end) {
return RawParseUtils.decode(Constants.CHARSET, buf, pos, end);
}
+
+ /**
+ * Retrieve the git attributes for the current entry.
+ *
+ * <h4>Git attribute computation</h4>
+ *
+ * <ul>
+ * <li>Get the attributes matching the current path entry from the info file
+ * (see {@link AttributesNodeProvider#getInfoAttributesNode()}).</li>
+ * <li>Completes the list of attributes using the .gitattributes files
+ * located on the current path (the further the directory that contains
+ * .gitattributes is from the path in question, the lower its precedence).
+ * For a checkin operation, it will look first on the working tree (if any).
+ * If there is no attributes file, it will fallback on the index. For a
+ * checkout operation, it will first use the index entry and then fallback
+ * on the working tree if none.</li>
+ * <li>In the end, completes the list of matching attributes using the
+ * global attribute file define in the configuration (see
+ * {@link AttributesNodeProvider#getGlobalAttributesNode()})</li>
+ *
+ * </ul>
+ *
+ *
+ * <h4>Iterator constraints</h4>
+ *
+ * <p>
+ * In order to have a correct list of attributes for the current entry, this
+ * {@link TreeWalk} requires to have at least one
+ * {@link AttributesNodeProvider} and a {@link DirCacheIterator} set up. An
+ * {@link AttributesNodeProvider} is used to retrieve the attributes from
+ * the info attributes file and the global attributes file. The
+ * {@link DirCacheIterator} is used to retrieve the .gitattributes files
+ * stored in the index. A {@link WorkingTreeIterator} can also be provided
+ * to access the local version of the .gitattributes files. If none is
+ * provided it will fallback on the {@link DirCacheIterator}.
+ * </p>
+ *
+ * @return a {@link Set} of {@link Attribute}s that match the current entry.
+ * @since 4.2
+ */
+ public Attributes getAttributes() {
+ if (attrs != null)
+ return attrs;
+
+ if (attributesNodeProvider == null) {
+ // The work tree should have a AttributesNodeProvider to be able to
+ // retrieve the info and global attributes node
+ throw new IllegalStateException(
+ "The tree walk should have one AttributesNodeProvider set in order to compute the git attributes."); //$NON-NLS-1$
+ }
+
+ WorkingTreeIterator workingTreeIterator = getTree(WorkingTreeIterator.class);
+ DirCacheIterator dirCacheIterator = getTree(DirCacheIterator.class);
+ CanonicalTreeParser other = getTree(CanonicalTreeParser.class);
+
+ if (workingTreeIterator == null && dirCacheIterator == null
+ && other == null) {
+ // Can not retrieve the attributes without at least one of the above
+ // iterators.
+ return new Attributes();
+ }
+
+ String path = currentHead.getEntryPathString();
+ final boolean isDir = FileMode.TREE.equals(currentHead.mode);
+ Attributes attributes = new Attributes();
+ try {
+ // Gets the global attributes node
+ AttributesNode globalNodeAttr = attributesNodeProvider
+ .getGlobalAttributesNode();
+ // Gets the info attributes node
+ AttributesNode infoNodeAttr = attributesNodeProvider
+ .getInfoAttributesNode();
+
+ // Gets the info attributes
+ if (infoNodeAttr != null) {
+ infoNodeAttr.getAttributes(path, isDir, attributes);
+ }
+
+ // Gets the attributes located on the current entry path
+ getPerDirectoryEntryAttributes(path, isDir, operationType,
+ workingTreeIterator, dirCacheIterator, other, attributes);
+
+ // Gets the attributes located in the global attribute file
+ if (globalNodeAttr != null) {
+ globalNodeAttr.getAttributes(path, isDir, attributes);
+ }
+ } catch (IOException e) {
+ throw new JGitInternalException("Error while parsing attributes", e); //$NON-NLS-1$
+ }
+ // now after all attributes are collected - in the correct hierarchy
+ // order - remove all unspecified entries (the ! marker)
+ for (Attribute a : attributes.getAll()) {
+ if (a.getState() == State.UNSPECIFIED)
+ attributes.remove(a.getKey());
+ }
+ return attributes;
+ }
+
+ /**
+ * Get the attributes located on the current entry path.
+ *
+ * @param path
+ * current entry path
+ * @param isDir
+ * holds true if the current entry is a directory
+ * @param opType
+ * type of operation
+ * @param workingTreeIterator
+ * a {@link WorkingTreeIterator} matching the current entry
+ * @param dirCacheIterator
+ * a {@link DirCacheIterator} matching the current entry
+ * @param other
+ * a {@link CanonicalTreeParser} matching the current entry
+ * @param attributes
+ * Non null map holding the existing attributes. This map will be
+ * augmented with new entry. None entry will be overrided.
+ * @throws IOException
+ * It raises an {@link IOException} if a problem appears while
+ * parsing one on the attributes file.
+ */
+ private void getPerDirectoryEntryAttributes(String path, boolean isDir,
+ OperationType opType, WorkingTreeIterator workingTreeIterator,
+ DirCacheIterator dirCacheIterator, CanonicalTreeParser other,
+ Attributes attributes)
+ throws IOException {
+ // Prevents infinite recurrence
+ if (workingTreeIterator != null || dirCacheIterator != null
+ || other != null) {
+ AttributesNode currentAttributesNode = getCurrentAttributesNode(
+ opType, workingTreeIterator, dirCacheIterator, other);
+ if (currentAttributesNode != null) {
+ currentAttributesNode.getAttributes(path, isDir, attributes);
+ }
+ getPerDirectoryEntryAttributes(path, isDir, opType,
+ getParent(workingTreeIterator, WorkingTreeIterator.class),
+ getParent(dirCacheIterator, DirCacheIterator.class),
+ getParent(other, CanonicalTreeParser.class), attributes);
+ }
+ }
+
+ private static <T extends AbstractTreeIterator> T getParent(T current,
+ Class<T> type) {
+ if (current != null) {
+ AbstractTreeIterator parent = current.parent;
+ if (type.isInstance(parent)) {
+ return type.cast(parent);
+ }
+ }
+ return null;
+ }
+
+ private <T extends AbstractTreeIterator> T getTree(Class<T> type) {
+ for (int i = 0; i < trees.length; i++) {
+ AbstractTreeIterator tree = trees[i];
+ if (type.isInstance(tree)) {
+ return type.cast(tree);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the {@link AttributesNode} for the current entry.
+ * <p>
+ * This method implements the fallback mechanism between the index and the
+ * working tree depending on the operation type
+ * </p>
+ *
+ * @param opType
+ * @param workingTreeIterator
+ * @param dirCacheIterator
+ * @param other
+ * @return a {@link AttributesNode} of the current entry,
+ * {@link NullPointerException} otherwise.
+ * @throws IOException
+ * It raises an {@link IOException} if a problem appears while
+ * parsing one on the attributes file.
+ */
+ private AttributesNode getCurrentAttributesNode(OperationType opType,
+ @Nullable WorkingTreeIterator workingTreeIterator,
+ @Nullable DirCacheIterator dirCacheIterator,
+ @Nullable CanonicalTreeParser other)
+ throws IOException {
+ AttributesNode attributesNode = null;
+ switch (opType) {
+ case CHECKIN_OP:
+ if (workingTreeIterator != null) {
+ attributesNode = workingTreeIterator.getEntryAttributesNode();
+ }
+ if (attributesNode == null && dirCacheIterator != null) {
+ attributesNode = getAttributesNode(dirCacheIterator
+ .getEntryAttributesNode(getObjectReader()),
+ attributesNode);
+ }
+ if (attributesNode == null && other != null) {
+ attributesNode = getAttributesNode(
+ other.getEntryAttributesNode(getObjectReader()),
+ attributesNode);
+ }
+ break;
+ case CHECKOUT_OP:
+ if (other != null) {
+ attributesNode = other
+ .getEntryAttributesNode(getObjectReader());
+ }
+ if (dirCacheIterator != null) {
+ attributesNode = getAttributesNode(dirCacheIterator
+ .getEntryAttributesNode(getObjectReader()),
+ attributesNode);
+ }
+ if (attributesNode == null && workingTreeIterator != null) {
+ attributesNode = getAttributesNode(
+ workingTreeIterator.getEntryAttributesNode(),
+ attributesNode);
+ }
+ break;
+ default:
+ throw new IllegalStateException(
+ "The only supported operation types are:" //$NON-NLS-1$
+ + OperationType.CHECKIN_OP + "," //$NON-NLS-1$
+ + OperationType.CHECKOUT_OP);
+ }
+
+ return attributesNode;
+ }
+
+ private static AttributesNode getAttributesNode(AttributesNode value,
+ AttributesNode defaultValue) {
+ return (value == null) ? defaultValue : value;
+ }
+
+ /**
+ * Inspect config and attributes to return a filtercommand applicable for
+ * the current path
+ *
+ * @param filterCommandType
+ * which type of filterCommand should be executed. E.g. "clean",
+ * "smudge"
+ * @return a filter command
+ * @throws IOException
+ * @since 4.2
+ */
+ public String getFilterCommand(String filterCommandType)
+ throws IOException {
+ Attributes attributes = getAttributes();
+
+ Attribute f = attributes.get(Constants.ATTR_FILTER);
+ if (f == null) {
+ return null;
+ }
+ String filterValue = f.getValue();
+ if (filterValue == null) {
+ return null;
+ }
+
+ String filterCommand = getFilterCommandDefinition(filterValue,
+ filterCommandType);
+ if (filterCommand == null) {
+ return null;
+ }
+ return filterCommand.replaceAll("%f", //$NON-NLS-1$
+ QuotedString.BOURNE.quote((getPathString())));
+ }
+
+ /**
+ * Get the filter command how it is defined in gitconfig. The returned
+ * string may contain "%f" which needs to be replaced by the current path
+ * before executing the filter command. These filter definitions are cached
+ * for better performance.
+ *
+ * @param filterDriverName
+ * The name of the filter driver as it is referenced in the
+ * gitattributes file. E.g. "lfs". For each filter driver there
+ * may be many commands defined in the .gitconfig
+ * @param filterCommandType
+ * The type of the filter command for a specific filter driver.
+ * May be "clean" or "smudge".
+ * @return the definition of the command to be executed for this filter
+ * driver and filter command
+ */
+ private String getFilterCommandDefinition(String filterDriverName,
+ String filterCommandType) {
+ String key = filterDriverName + "." + filterCommandType; //$NON-NLS-1$
+ String filterCommand = filterCommandsByNameDotType.get(key);
+ if (filterCommand != null)
+ return filterCommand;
+ filterCommand = config.getString(Constants.ATTR_FILTER,
+ filterDriverName, filterCommandType);
+ if (filterCommand != null)
+ filterCommandsByNameDotType.put(key, filterCommand);
+ return filterCommand;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index 73ab04f9cb..94beeeb56f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -62,6 +62,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import org.eclipse.jgit.api.errors.FilterFailedException;
import org.eclipse.jgit.attributes.AttributesNode;
import org.eclipse.jgit.attributes.AttributesRule;
import org.eclipse.jgit.diff.RawText;
@@ -76,6 +77,7 @@ import org.eclipse.jgit.ignore.IgnoreNode;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
import org.eclipse.jgit.lib.CoreConfig.CheckStat;
import org.eclipse.jgit.lib.CoreConfig.SymLinks;
import org.eclipse.jgit.lib.FileMode;
@@ -85,6 +87,7 @@ import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.submodule.SubmoduleWalk;
import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.ExecutionResult;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.io.EolCanonicalizingInputStream;
@@ -101,6 +104,8 @@ import org.eclipse.jgit.util.io.EolCanonicalizingInputStream;
* @see FileTreeIterator
*/
public abstract class WorkingTreeIterator extends AbstractTreeIterator {
+ private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024;
+
/** An empty entry array, suitable for {@link #init(Entry[])}. */
protected static final Entry[] EOF = {};
@@ -134,8 +139,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
/** If there is a .gitignore file present, the parsed rules from it. */
private IgnoreNode ignoreNode;
- /** If there is a .gitattributes file present, the parsed rules from it. */
- private AttributesNode attributesNode;
+ private String cleanFilterCommand;
/** Repository that is the root level being iterated over */
protected Repository repository;
@@ -147,19 +151,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
private int contentIdOffset;
/**
- * Holds the {@link AttributesNode} that is stored in
- * $GIT_DIR/info/attributes file.
- */
- private AttributesNode infoAttributeNode;
-
- /**
- * Holds the {@link AttributesNode} that is stored in global attribute file.
- *
- * @see CoreConfig#getAttributesFile()
- */
- private AttributesNode globalAttributeNode;
-
- /**
* Create a new iterator with no parent.
*
* @param options
@@ -202,8 +193,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
protected WorkingTreeIterator(final WorkingTreeIterator p) {
super(p);
state = p.state;
- infoAttributeNode = p.infoAttributeNode;
- globalAttributeNode = p.globalAttributeNode;
+ repository = p.repository;
}
/**
@@ -223,10 +213,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
else
entry = null;
ignoreNode = new RootIgnoreNode(entry, repo);
-
- infoAttributeNode = new InfoAttributesNode(repo);
-
- globalAttributeNode = new GlobalAttributesNode(repo);
}
/**
@@ -370,7 +356,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
private InputStream possiblyFilteredInputStream(final Entry e,
final InputStream is, final long len) throws IOException {
- if (!mightNeedCleaning()) {
+ boolean mightNeedCleaning = mightNeedCleaning();
+ if (!mightNeedCleaning) {
canonLen = len;
return is;
}
@@ -388,7 +375,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
return new ByteArrayInputStream(raw, 0, n);
}
- if (isBinary(e)) {
+ // TODO: fix autocrlf causing mightneedcleaning
+ if (!mightNeedCleaning && isBinary(e)) {
canonLen = len;
return is;
}
@@ -412,10 +400,12 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
- private boolean mightNeedCleaning() {
+ private boolean mightNeedCleaning() throws IOException {
switch (getOptions().getAutoCRLF()) {
case FALSE:
default:
+ if (getCleanFilterCommand() != null)
+ return true;
return false;
case TRUE:
@@ -437,8 +427,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
- private static ByteBuffer filterClean(byte[] src, int n)
- throws IOException {
+ private ByteBuffer filterClean(byte[] src, int n) throws IOException {
InputStream in = new ByteArrayInputStream(src);
try {
return IO.readWholeStream(filterClean(in), n);
@@ -447,8 +436,42 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
- private static InputStream filterClean(InputStream in) {
- return new EolCanonicalizingInputStream(in, true);
+ private InputStream filterClean(InputStream in) throws IOException {
+ in = handleAutoCRLF(in);
+ String filterCommand = getCleanFilterCommand();
+ if (filterCommand != null) {
+ FS fs = repository.getFS();
+ ProcessBuilder filterProcessBuilder = fs.runInShell(filterCommand,
+ new String[0]);
+ filterProcessBuilder.directory(repository.getWorkTree());
+ filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
+ repository.getDirectory().getAbsolutePath());
+ ExecutionResult result;
+ try {
+ result = fs.execute(filterProcessBuilder, in);
+ } catch (IOException | InterruptedException e) {
+ throw new IOException(new FilterFailedException(e,
+ filterCommand, getEntryPathString()));
+ }
+ int rc = result.getRc();
+ if (rc != 0) {
+ throw new IOException(new FilterFailedException(rc,
+ filterCommand, getEntryPathString(),
+ result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE),
+ RawParseUtils.decode(result.getStderr()
+ .toByteArray(MAX_EXCEPTION_TEXT_SIZE))));
+ }
+ return result.getStdout().openInputStream();
+ }
+ return in;
+ }
+
+ private InputStream handleAutoCRLF(InputStream in) {
+ AutoCRLF autoCRLF = getOptions().getAutoCRLF();
+ if (autoCRLF == AutoCRLF.TRUE || autoCRLF == AutoCRLF.INPUT) {
+ in = new EolCanonicalizingInputStream(in, true);
+ }
+ return in;
}
/**
@@ -507,6 +530,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
System.arraycopy(e.encodedName, 0, path, pathOffset, nameLen);
pathLen = pathOffset + nameLen;
canonLen = -1;
+ cleanFilterCommand = null;
}
/**
@@ -667,41 +691,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
return attributesNode;
}
- /**
- * Retrieves the {@link AttributesNode} that holds the information located
- * in $GIT_DIR/info/attributes file.
- *
- * @return the {@link AttributesNode} that holds the information located in
- * $GIT_DIR/info/attributes file.
- * @throws IOException
- * if an error is raised while parsing the attributes file
- * @since 3.7
- */
- public AttributesNode getInfoAttributesNode() throws IOException {
- if (infoAttributeNode instanceof InfoAttributesNode)
- infoAttributeNode = ((InfoAttributesNode) infoAttributeNode).load();
- return infoAttributeNode;
- }
-
- /**
- * Retrieves the {@link AttributesNode} that holds the information located
- * in system-wide file.
- *
- * @return the {@link AttributesNode} that holds the information located in
- * system-wide file.
- * @throws IOException
- * IOException if an error is raised while parsing the
- * attributes file
- * @see CoreConfig#getAttributesFile()
- * @since 3.7
- */
- public AttributesNode getGlobalAttributesNode() throws IOException {
- if (globalAttributeNode instanceof GlobalAttributesNode)
- globalAttributeNode = ((GlobalAttributesNode) globalAttributeNode)
- .load();
- return globalAttributeNode;
- }
-
private static final Comparator<Entry> ENTRY_CMP = new Comparator<Entry>() {
public int compare(final Entry o1, final Entry o2) {
final byte[] a = o1.encodedName;
@@ -1296,68 +1285,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
- /**
- * Attributes node loaded from global system-wide file.
- */
- private static class GlobalAttributesNode extends AttributesNode {
- final Repository repository;
-
- GlobalAttributesNode(Repository repository) {
- this.repository = repository;
- }
-
- AttributesNode load() throws IOException {
- AttributesNode r = new AttributesNode();
-
- FS fs = repository.getFS();
- String path = repository.getConfig().get(CoreConfig.KEY)
- .getAttributesFile();
- if (path != null) {
- File attributesFile;
- if (path.startsWith("~/")) //$NON-NLS-1$
- attributesFile = fs.resolve(fs.userHome(),
- path.substring(2));
- else
- attributesFile = fs.resolve(null, path);
- loadRulesFromFile(r, attributesFile);
- }
- return r.getRules().isEmpty() ? null : r;
- }
- }
-
- /** Magic type indicating there may be rules for the top level. */
- private static class InfoAttributesNode extends AttributesNode {
- final Repository repository;
-
- InfoAttributesNode(Repository repository) {
- this.repository = repository;
- }
-
- AttributesNode load() throws IOException {
- AttributesNode r = new AttributesNode();
-
- FS fs = repository.getFS();
-
- File attributes = fs.resolve(repository.getDirectory(),
- "info/attributes"); //$NON-NLS-1$
- loadRulesFromFile(r, attributes);
-
- return r.getRules().isEmpty() ? null : r;
- }
-
- }
-
- private static void loadRulesFromFile(AttributesNode r, File attrs)
- throws FileNotFoundException, IOException {
- if (attrs.exists()) {
- FileInputStream in = new FileInputStream(attrs);
- try {
- r.parse(in);
- } finally {
- in.close();
- }
- }
- }
private static final class IteratorState {
/** Options used to process the working tree. */
@@ -1390,4 +1317,18 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
}
+
+ /**
+ * @return the clean filter command for the current entry or
+ * <code>null</code> if no such command is defined
+ * @throws IOException
+ * @since 4.2
+ */
+ public String getCleanFilterCommand() throws IOException {
+ if (cleanFilterCommand == null && state.walk != null) {
+ cleanFilterCommand = state.walk
+ .getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN);
+ }
+ return cleanFilterCommand;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index e407dd8be9..253acbb76a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -110,7 +110,54 @@ public abstract class FS {
}
}
- final static Logger LOG = LoggerFactory.getLogger(FS.class);
+ /**
+ * Result of an executed process. The caller is responsible to close the
+ * contained {@link TemporaryBuffer}s
+ *
+ * @since 4.2
+ */
+ public static class ExecutionResult {
+ private TemporaryBuffer stdout;
+
+ private TemporaryBuffer stderr;
+
+ private int rc;
+
+ /**
+ * @param stdout
+ * @param stderr
+ * @param rc
+ */
+ public ExecutionResult(TemporaryBuffer stdout, TemporaryBuffer stderr,
+ int rc) {
+ this.stdout = stdout;
+ this.stderr = stderr;
+ this.rc = rc;
+ }
+
+ /**
+ * @return buffered standard output stream
+ */
+ public TemporaryBuffer getStdout() {
+ return stdout;
+ }
+
+ /**
+ * @return buffered standard error stream
+ */
+ public TemporaryBuffer getStderr() {
+ return stderr;
+ }
+
+ /**
+ * @return the return code of the process
+ */
+ public int getRc() {
+ return rc;
+ }
+ }
+
+ private final static Logger LOG = LoggerFactory.getLogger(FS.class);
/** The auto-detected implementation selected for this operating system and JRE. */
public static final FS DETECTED = detect();
@@ -902,9 +949,7 @@ public abstract class FS {
* @param outRedirect
* An OutputStream on which to redirect the processes stdout. Can
* be <code>null</code>, in which case the processes standard
- * output will be lost. If binary is set to <code>false</code>
- * then it is expected that the process emits text data which
- * should be processed line by line.
+ * output will be lost.
* @param errRedirect
* An OutputStream on which to redirect the processes stderr. Can
* be <code>null</code>, in which case the processes standard
@@ -912,9 +957,9 @@ public abstract class FS {
* @param inRedirect
* An InputStream from which to redirect the processes stdin. Can
* be <code>null</code>, in which case the process doesn't get
- * any data over stdin. If binary is set to
- * <code>false</code> then it is expected that the process
- * expects text data which should be processed line by line.
+ * any data over stdin. It is assumed that the whole InputStream
+ * will be consumed by the process. The method will close the
+ * inputstream after all bytes are read.
* @return the return code of this process.
* @throws IOException
* if an I/O error occurs while executing this process.
@@ -964,6 +1009,9 @@ public abstract class FS {
// A process doesn't clean its own resources even when destroyed
// Explicitly try and close all three streams, preserving the
// outer I/O exception if any.
+ if (inRedirect != null) {
+ inRedirect.close();
+ }
try {
process.getErrorStream().close();
} catch (IOException e) {
@@ -1004,10 +1052,10 @@ public abstract class FS {
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
- if (!pool.awaitTermination(5, TimeUnit.SECONDS)) {
+ if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being canceled
- if (!pool.awaitTermination(5, TimeUnit.SECONDS))
+ if (!pool.awaitTermination(60, TimeUnit.SECONDS))
hasShutdown = false;
}
} catch (InterruptedException ie) {
@@ -1034,6 +1082,31 @@ public abstract class FS {
*/
public abstract ProcessBuilder runInShell(String cmd, String[] args);
+ /**
+ * Execute a command defined by a {@link ProcessBuilder}.
+ *
+ * @param pb
+ * The command to be executed
+ * @param in
+ * The standard input stream passed to the process
+ * @return The result of the executed command
+ * @throws InterruptedException
+ * @throws IOException
+ * @since 4.2
+ */
+ public ExecutionResult execute(ProcessBuilder pb, InputStream in)
+ throws IOException, InterruptedException {
+ TemporaryBuffer stdout = new TemporaryBuffer.LocalFile(null);
+ TemporaryBuffer stderr = new TemporaryBuffer.Heap(1024, 1024 * 1024);
+ try {
+ int rc = runProcess(pb, stdout, stderr, in);
+ return new ExecutionResult(stdout, stderr, rc);
+ } finally {
+ stdout.close();
+ stderr.close();
+ }
+ }
+
private static class Holder<V> {
final V value;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
index f0a2e721a6..defe14f0ed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
@@ -171,7 +171,8 @@ public class FS_Win32 extends FS {
createSymLink(linkName, tempFile.getPath());
supportSymlinks = Boolean.TRUE;
linkName.delete();
- } catch (IOException | UnsupportedOperationException e) {
+ } catch (IOException | UnsupportedOperationException
+ | InternalError e) {
supportSymlinks = Boolean.FALSE;
} finally {
if (tempFile != null)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
index 2450be4c17..ec581b397a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
@@ -162,17 +162,15 @@ public class FS_Win32_Cygwin extends FS_Win32 {
errRedirect, stdinArgs);
}
- @Override
- public boolean supportsSymlinks() {
- return true;
- }
-
/**
* @since 3.7
*/
@Override
public File findHook(Repository repository, String hookName) {
final File gitdir = repository.getDirectory();
+ if (gitdir == null) {
+ return null;
+ }
final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS)
.resolve(hookName);
if (Files.isExecutable(hookPath))
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
index 6d0318c029..727ea79cc9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -398,8 +398,10 @@ public class FileUtils {
* Create a symbolic link
*
* @param path
+ * the path of the symbolic link to create
* @param target
- * @return path to the created link
+ * the target of the symbolic link
+ * @return the path to the symbolic link
* @throws IOException
* @since 4.2
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index 4795c89e73..9860ef070f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -339,4 +339,19 @@ public abstract class SystemReader {
public void checkPath(String path) throws CorruptObjectException {
platformChecker.checkPath(path);
}
+
+ /**
+ * Check tree path entry for validity.
+ * <p>
+ * Scans a multi-directory path string such as {@code "src/main.c"}.
+ *
+ * @param path
+ * path string to scan.
+ * @throws CorruptObjectException
+ * path is invalid.
+ * @since 4.2
+ */
+ public void checkPath(byte[] path) throws CorruptObjectException {
+ platformChecker.checkPath(path, 0, path.length);
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
index ca47f50fd9..3cd5929c7f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
@@ -247,6 +247,37 @@ public abstract class TemporaryBuffer extends OutputStream {
}
/**
+ * Convert this buffer's contents into a contiguous byte array. If this size
+ * of the buffer exceeds the limit only return the first {@code limit} bytes
+ * <p>
+ * The buffer is only complete after {@link #close()} has been invoked.
+ *
+ * @param limit
+ * the maximum number of bytes to be returned
+ *
+ * @return the byte array limited to {@code limit} bytes.
+ * @throws IOException
+ * an error occurred reading from a local temporary file
+ * @throws OutOfMemoryError
+ * the buffer cannot fit in memory
+ *
+ * @since 4.2
+ */
+ public byte[] toByteArray(int limit) throws IOException {
+ final long len = Math.min(length(), limit);
+ if (Integer.MAX_VALUE < len)
+ throw new OutOfMemoryError(
+ JGitText.get().lengthExceedsMaximumArraySize);
+ final byte[] out = new byte[(int) len];
+ int outPtr = 0;
+ for (final Block b : blocks) {
+ System.arraycopy(b.buffer, 0, out, outPtr, b.count);
+ outPtr += b.count;
+ }
+ return out;
+ }
+
+ /**
* Send this buffer to an output stream.
* <p>
* This method may only be invoked after {@link #close()} has completed