summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.lfs
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit.lfs')
-rw-r--r--org.eclipse.jgit.lfs/.classpath2
-rw-r--r--org.eclipse.jgit.lfs/.settings/.api_filters17
-rw-r--r--org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs6
-rw-r--r--org.eclipse.jgit.lfs/META-INF/MANIFEST.MF21
-rw-r--r--org.eclipse.jgit.lfs/pom.xml2
-rw-r--r--org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties4
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java171
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Lfs.java124
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java176
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java125
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/CorruptMediaFile.java100
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java149
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java2
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java19
14 files changed, 899 insertions, 19 deletions
diff --git a/org.eclipse.jgit.lfs/.classpath b/org.eclipse.jgit.lfs/.classpath
index 04a2be7bdb..cfcf24a51e 100644
--- a/org.eclipse.jgit.lfs/.classpath
+++ b/org.eclipse.jgit.lfs/.classpath
@@ -2,7 +2,7 @@
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit.lfs/.settings/.api_filters b/org.eclipse.jgit.lfs/.settings/.api_filters
new file mode 100644
index 0000000000..be675b1ee1
--- /dev/null
+++ b/org.eclipse.jgit.lfs/.settings/.api_filters
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.jgit.lfs" version="2">
+ <resource path="src/org/eclipse/jgit/lfs/lib/Constants.java" type="org.eclipse.jgit.lfs.lib.Constants">
+ <filter id="388100214">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lfs.lib.Constants"/>
+ <message_argument value="CONTENT_TYPE_GIT_LFS_JSON"/>
+ </message_arguments>
+ </filter>
+ <filter id="388100214">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lfs.lib.Constants"/>
+ <message_argument value="HDR_APPLICATION_OCTET_STREAM"/>
+ </message_arguments>
+ </filter>
+ </resource>
+</component>
diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
index 1ce7cd0219..808ec3a2c6 100644
--- a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
@@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -112,7 +112,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.7
+org.eclipse.jdt.core.compiler.source=1.8
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
index 854ca1d0d8..d5d1f7cbdd 100644
--- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
@@ -2,14 +2,17 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit.lfs
-Bundle-Version: 4.5.3.qualifier
+Bundle-Version: 4.6.2.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.lfs.errors;version="4.5.3",
- org.eclipse.jgit.lfs.internal;version="4.5.3";x-friends:="org.eclipse.jgit.lfs.test",
- org.eclipse.jgit.lfs.lib;version="4.5.3"
-Bundle-RequiredExecutionEnvironment: JavaSE-1.7
-Import-Package: org.eclipse.jgit.internal.storage.file;version="[4.5.3,4.6.0)",
- org.eclipse.jgit.lib;version="[4.5.3,4.6.0)",
- org.eclipse.jgit.nls;version="[4.5.3,4.6.0)",
- org.eclipse.jgit.util;version="[4.5.3,4.6.0)"
+Export-Package: org.eclipse.jgit.lfs;version="4.6.2",
+ org.eclipse.jgit.lfs.errors;version="4.6.2",
+ org.eclipse.jgit.lfs.internal;version="4.6.2";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
+ org.eclipse.jgit.lfs.lib;version="4.6.2"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Import-Package: org.eclipse.jgit.annotations;version="[4.6.2,4.7.0)";resolution:=optional,
+ org.eclipse.jgit.attributes;version="[4.6.2,4.7.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.6.2,4.7.0)",
+ org.eclipse.jgit.lib;version="[4.6.2,4.7.0)",
+ org.eclipse.jgit.nls;version="[4.6.2,4.7.0)",
+ org.eclipse.jgit.util;version="[4.6.2,4.7.0)"
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index 179479e7d2..049b3601a7 100644
--- a/org.eclipse.jgit.lfs/pom.xml
+++ b/org.eclipse.jgit.lfs/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>4.5.3-SNAPSHOT</version>
+ <version>4.6.2-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit.lfs</artifactId>
diff --git a/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties b/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties
index 7c3aea2261..5e52a782f0 100644
--- a/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties
+++ b/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties
@@ -1,7 +1,9 @@
+corruptLongObject=The content hash ''{0}'' of the long object ''{1}'' doesn''t match its id, the corrupt object will be deleted.
incorrectLONG_OBJECT_ID_LENGTH=Incorrect LONG_OBJECT_ID_LENGTH.
+inconsistentMediafileLength=mediafile {0} has unexpected length; expected {1} but found {2}.
invalidLongId=Invalid id: {0}
invalidLongIdLength=Invalid id length {0}; should be {1}
requiredHashFunctionNotAvailable=Required hash function {0} not available.
repositoryNotFound=Repository {0} not found
repositoryReadOnly=Repository {0} is read-only
-lfsUnavailable=LFS is not available for repository {0} \ No newline at end of file
+lfsUnavailable=LFS is not available for repository {0}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java
new file mode 100644
index 0000000000..66feca7518
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2016, 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.lfs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+
+import org.eclipse.jgit.attributes.FilterCommand;
+import org.eclipse.jgit.attributes.FilterCommandFactory;
+import org.eclipse.jgit.attributes.FilterCommandRegistry;
+import org.eclipse.jgit.lfs.errors.CorruptMediaFile;
+import org.eclipse.jgit.lfs.internal.AtomicObjectOutputStream;
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.FileUtils;
+
+/**
+ * Built-in LFS clean filter
+ *
+ * When new content is about to be added to the git repository and this filter
+ * is configured for that content, then this filter will replace the original
+ * content with content of a so-called LFS pointer file. The pointer file
+ * content will then be added to the git repository. Additionally this filter
+ * writes the original content in a so-called 'media file' to '.git/lfs/objects/
+ * <first-two-characters-of-contentid>/<rest-of-contentid>'
+ *
+ * @see <a href="https://github.com/github/git-lfs/blob/master/docs/spec.md">Git
+ * LFS Specification</a>
+ * @since 4.6
+ */
+public class CleanFilter extends FilterCommand {
+ /**
+ * The factory is responsible for creating instances of {@link CleanFilter}
+ */
+ public final static FilterCommandFactory FACTORY = new FilterCommandFactory() {
+
+ @Override
+ public FilterCommand create(Repository db, InputStream in,
+ OutputStream out) throws IOException {
+ return new CleanFilter(db, in, out);
+ }
+ };
+
+ /**
+ * Registers this filter by calling
+ * {@link FilterCommandRegistry#register(String, FilterCommandFactory)}
+ */
+ public final static void register() {
+ FilterCommandRegistry.register(
+ org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX
+ + "lfs/clean", //$NON-NLS-1$
+ FACTORY);
+ }
+
+ // Used to compute the hash for the original content
+ private AtomicObjectOutputStream aOut;
+
+ private Lfs lfsUtil;
+
+ // the size of the original content
+ private long size;
+
+ // a temporary file into which the original content is written. When no
+ // errors occur this file will be renamed to the mediafile
+ private Path tmpFile;
+
+ /**
+ * @param db
+ * the repository
+ * @param in
+ * an {@link InputStream} providing the original content
+ * @param out
+ * the {@link OutputStream} into which the content of the pointer
+ * file should be written. That's the content which will be added
+ * to the git repository
+ * @throws IOException
+ * when the creation of the temporary file fails or when no
+ * {@link OutputStream} for this file can be created
+ */
+ public CleanFilter(Repository db, InputStream in, OutputStream out)
+ throws IOException {
+ super(in, out);
+ lfsUtil = new Lfs(db.getDirectory().toPath().resolve("lfs")); //$NON-NLS-1$
+ Files.createDirectories(lfsUtil.getLfsTmpDir());
+ tmpFile = lfsUtil.createTmpFile();
+ this.aOut = new AtomicObjectOutputStream(tmpFile.toAbsolutePath());
+ }
+
+ public int run() throws IOException {
+ try {
+ byte[] buf = new byte[8192];
+ int length = in.read(buf);
+ if (length != -1) {
+ aOut.write(buf, 0, length);
+ size += length;
+ return length;
+ } else {
+ aOut.close();
+ AnyLongObjectId loid = aOut.getId();
+ aOut = null;
+ Path mediaFile = lfsUtil.getMediaFile(loid);
+ if (Files.isRegularFile(mediaFile)) {
+ long fsSize = Files.size(mediaFile);
+ if (fsSize != size) {
+ throw new CorruptMediaFile(mediaFile, size, fsSize);
+ } else {
+ FileUtils.delete(tmpFile.toFile());
+ }
+ } else {
+ FileUtils.mkdirs(mediaFile.getParent().toFile(), true);
+ FileUtils.rename(tmpFile.toFile(), mediaFile.toFile(),
+ StandardCopyOption.ATOMIC_MOVE);
+ }
+ LfsPointer lfsPointer = new LfsPointer(loid, size);
+ lfsPointer.encode(out);
+ out.close();
+ return -1;
+ }
+ } catch (IOException e) {
+ if (aOut != null) {
+ aOut.abort();
+ }
+ out.close();
+ throw e;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Lfs.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Lfs.java
new file mode 100644
index 0000000000..75e34e0f63
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/Lfs.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016, 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.lfs;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+
+/**
+ * Class which represents the lfs folder hierarchy inside a .git folder
+ *
+ * @since 4.6
+ */
+public class Lfs {
+ private Path root;
+
+ private Path objDir;
+
+ private Path tmpDir;
+
+ /**
+ * @param root
+ * the path to the LFS media directory. Will be "<repo>/.git/lfs"
+ */
+ public Lfs(Path root) {
+ this.root = root;
+ }
+
+ /**
+ * @return the path to the LFS directory
+ */
+ public Path getLfsRoot() {
+ return root;
+ }
+
+ /**
+ * @return the path to the temp directory used by LFS. Will be
+ * "<repo>/.git/lfs/tmp"
+ */
+ public Path getLfsTmpDir() {
+ if (tmpDir == null) {
+ tmpDir = root.resolve("tmp"); //$NON-NLS-1$
+ }
+ return tmpDir;
+ }
+
+ /**
+ * @return the path to the object directory used by LFS. Will be
+ * "<repo>/.git/lfs/objects"
+ */
+ public Path getLfsObjDir() {
+ if (objDir == null) {
+ objDir = root.resolve("objects"); //$NON-NLS-1$
+ }
+ return objDir;
+ }
+
+ /**
+ * @param id
+ * the id of the mediafile
+ * @return the file which stores the original content. This will be files
+ * underneath
+ * "<repo>/.git/lfs/objects/<firstTwoLettersOfID>/<remainingLettersOfID>"
+ */
+ public Path getMediaFile(AnyLongObjectId id) {
+ String idStr = id.name();
+ return getLfsObjDir().resolve(idStr.substring(0, 2))
+ .resolve(idStr.substring(2));
+ }
+
+ /**
+ * Create a new temp file in the LFS directory
+ *
+ * @return a new temporary file in the LFS directory
+ * @throws IOException
+ * when the temp file could not be created
+ */
+ public Path createTmpFile() throws IOException {
+ return Files.createTempFile(getLfsTmpDir(), null, null);
+ }
+
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java
new file mode 100644
index 0000000000..bbea53567f
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016, 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.lfs;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.nio.charset.UnsupportedCharsetException;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+import org.eclipse.jgit.lfs.lib.Constants;
+import org.eclipse.jgit.lfs.lib.LongObjectId;
+
+/**
+ * Represents an LFS pointer file
+ *
+ * @since 4.6
+ */
+public class LfsPointer {
+ /**
+ * The version of the LfsPointer file format
+ */
+ public static final String VERSION = "https://git-lfs.github.com/spec/v1"; //$NON-NLS-1$
+
+ /**
+ * The name of the hash function as used in the pointer files. This will
+ * evaluate to "sha256"
+ */
+ public static final String HASH_FUNCTION_NAME = Constants.LONG_HASH_FUNCTION
+ .toLowerCase().replace("-", ""); //$NON-NLS-1$ //$NON-NLS-2$
+
+ private AnyLongObjectId oid;
+
+ private long size;
+
+ /**
+ * @param oid
+ * the id of the content
+ * @param size
+ * the size of the content
+ */
+ public LfsPointer(AnyLongObjectId oid, long size) {
+ this.oid = oid;
+ this.size = size;
+ }
+
+ /**
+ * @return the id of the content
+ */
+ public AnyLongObjectId getOid() {
+ return oid;
+ }
+
+ /**
+ * @return the size of the content
+ */
+ public long getSize() {
+ return size;
+ }
+
+ /**
+ * Encode this object into the LFS format defined by {@link #VERSION}
+ *
+ * @param out
+ * the {@link OutputStream} into which the encoded data should be
+ * written
+ */
+ public void encode(OutputStream out) {
+ try (PrintStream ps = new PrintStream(out, false,
+ StandardCharsets.UTF_8.name())) {
+ ps.print("version "); //$NON-NLS-1$
+ ps.print(VERSION + "\n"); //$NON-NLS-1$
+ ps.print("oid " + HASH_FUNCTION_NAME + ":"); //$NON-NLS-1$ //$NON-NLS-2$
+ ps.print(oid.name() + "\n"); //$NON-NLS-1$
+ ps.print("size "); //$NON-NLS-1$
+ ps.print(size + "\n"); //$NON-NLS-1$
+ } catch (UnsupportedEncodingException e) {
+ // should not happen, we are using a standard charset
+ throw new UnsupportedCharsetException(
+ StandardCharsets.UTF_8.name());
+ }
+ }
+
+ /**
+ * Try to parse the data provided by an InputStream to the format defined by
+ * {@link #VERSION}
+ *
+ * @param in
+ * the {@link InputStream} from where to read the data
+ * @return an {@link LfsPointer} or <code>null</code> if the stream was not
+ * parseable as LfsPointer
+ * @throws IOException
+ */
+ @Nullable
+ public static LfsPointer parseLfsPointer(InputStream in)
+ throws IOException {
+ boolean versionLine = false;
+ LongObjectId id = null;
+ long sz = -1;
+
+ try (BufferedReader br = new BufferedReader(
+ new InputStreamReader(in, StandardCharsets.UTF_8.name()))) {
+ for (String s = br.readLine(); s != null; s = br.readLine()) {
+ if (s.startsWith("#") || s.length() == 0) { //$NON-NLS-1$
+ continue;
+ } else if (s.startsWith("version") && s.length() > 8 //$NON-NLS-1$
+ && s.substring(8).trim().equals(VERSION)) {
+ versionLine = true;
+ } else if (s.startsWith("oid sha256:")) { //$NON-NLS-1$
+ id = LongObjectId.fromString(s.substring(11).trim());
+ } else if (s.startsWith("size") && s.length() > 5) { //$NON-NLS-1$
+ sz = Long.parseLong(s.substring(5).trim());
+ } else {
+ return null;
+ }
+ }
+ if (versionLine && id != null && sz > -1) {
+ return new LfsPointer(id, sz);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "LfsPointer: oid=" + oid.name() + ", size=" //$NON-NLS-1$ //$NON-NLS-2$
+ + size;
+ }
+}
+
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java
new file mode 100644
index 0000000000..233247779d
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/SmudgeFilter.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016, 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.lfs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.eclipse.jgit.attributes.FilterCommand;
+import org.eclipse.jgit.attributes.FilterCommandFactory;
+import org.eclipse.jgit.attributes.FilterCommandRegistry;
+import org.eclipse.jgit.lfs.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Built-in LFS smudge filter
+ *
+ * When content is read from git's object-database and written to the filesystem
+ * and this filter is configured for that content, then this filter will replace
+ * the content of LFS pointer files with the original content. This happens e.g.
+ * when a checkout needs to update a working tree file which is under LFS
+ * control. This implementation expects that the origin content is already
+ * available in the .git/lfs/objects folder. This implementation will not
+ * contact any LFS servers in order to get the missing content.
+ *
+ * @since 4.6
+ */
+public class SmudgeFilter extends FilterCommand {
+ /**
+ * The factory is responsible for creating instances of {@link SmudgeFilter}
+ */
+ public final static FilterCommandFactory FACTORY = new FilterCommandFactory() {
+ @Override
+ public FilterCommand create(Repository db, InputStream in,
+ OutputStream out) throws IOException {
+ return new SmudgeFilter(db, in, out);
+ }
+ };
+
+ /**
+ * Registers this filter in JGit by calling
+ */
+ public final static void register() {
+ FilterCommandRegistry.register(
+ org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX
+ + "lfs/smudge", //$NON-NLS-1$
+ FACTORY);
+ }
+
+ private Lfs lfs;
+
+ /**
+ * @param db
+ * @param in
+ * @param out
+ * @throws IOException
+ */
+ public SmudgeFilter(Repository db, InputStream in, OutputStream out)
+ throws IOException {
+ super(in, out);
+ lfs = new Lfs(db.getDirectory().toPath().resolve(Constants.LFS));
+ LfsPointer res = LfsPointer.parseLfsPointer(in);
+ if (res != null) {
+ Path mediaFile = lfs.getMediaFile(res.getOid());
+ if (Files.exists(mediaFile)) {
+ this.in = Files.newInputStream(mediaFile);
+ }
+ }
+ }
+
+ @Override
+ public int run() throws IOException {
+ int b;
+ if (in != null) {
+ while ((b = in.read()) != -1) {
+ out.write(b);
+ }
+ in.close();
+ }
+ out.close();
+ return -1;
+ }
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/CorruptMediaFile.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/CorruptMediaFile.java
new file mode 100644
index 0000000000..f2b51c0442
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/CorruptMediaFile.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016, 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.lfs.errors;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.lfs.internal.LfsText;
+
+/**
+ * Thrown when a LFS mediafile is found which doesn't have the expected size
+ *
+ * @since 4.6
+ */
+public class CorruptMediaFile extends IOException {
+ private static final long serialVersionUID = 1L;
+
+ private Path mediaFile;
+
+ private long expectedSize;
+
+ private long size;
+
+ /**
+ * @param mediaFile
+ * @param expectedSize
+ * @param size
+ */
+ @SuppressWarnings("boxing")
+ public CorruptMediaFile(Path mediaFile, long expectedSize,
+ long size) {
+ super(MessageFormat.format(LfsText.get().inconsistentMediafileLength,
+ mediaFile, expectedSize, size));
+ this.mediaFile = mediaFile;
+ this.expectedSize = expectedSize;
+ this.size = size;
+ }
+
+ /**
+ * @return the media file which seems to be corrupt
+ */
+ public Path getMediaFile() {
+ return mediaFile;
+ }
+
+ /**
+ * @return the expected size of the media file
+ */
+ public long getExpectedSize() {
+ return expectedSize;
+ }
+
+ /**
+ * @return the actual size of the media file in the file system
+ */
+ public long getSize() {
+ return size;
+ }
+}
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java
new file mode 100644
index 0000000000..867cca5056
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@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.lfs.internal;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Path;
+import java.security.DigestOutputStream;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.storage.file.LockFile;
+import org.eclipse.jgit.lfs.errors.CorruptLongObjectException;
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+import org.eclipse.jgit.lfs.lib.Constants;
+import org.eclipse.jgit.lfs.lib.LongObjectId;
+
+/**
+ * Output stream writing content to a {@link LockFile} which is committed on
+ * close(). The stream checks if the hash of the stream content matches the
+ * id.
+ */
+public class AtomicObjectOutputStream extends OutputStream {
+
+ private LockFile locked;
+
+ private DigestOutputStream out;
+
+ private boolean aborted;
+
+ private AnyLongObjectId id;
+
+ /**
+ * @param path
+ * @param id
+ * @throws IOException
+ */
+ public AtomicObjectOutputStream(Path path, AnyLongObjectId id)
+ throws IOException {
+ locked = new LockFile(path.toFile());
+ locked.lock();
+ this.id = id;
+ out = new DigestOutputStream(locked.getOutputStream(),
+ Constants.newMessageDigest());
+ }
+
+ /**
+ * @param path
+ * @throws IOException
+ */
+ public AtomicObjectOutputStream(Path path) throws IOException {
+ this(path, null);
+ }
+
+ /**
+ * @return content hash of the object which was streamed through this
+ * stream. May return {@code null} if called before closing this stream.
+ */
+ public @Nullable AnyLongObjectId getId() {
+ return id;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ out.write(b);
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException {
+ out.write(b);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ out.write(b, off, len);
+ }
+
+ @Override
+ public void close() throws IOException {
+ out.close();
+ if (!aborted) {
+ if (id != null) {
+ verifyHash();
+ } else {
+ id = LongObjectId.fromRaw(out.getMessageDigest().digest());
+ }
+ locked.commit();
+ }
+ }
+
+ private void verifyHash() {
+ AnyLongObjectId contentHash = LongObjectId
+ .fromRaw(out.getMessageDigest().digest());
+ if (!contentHash.equals(id)) {
+ abort();
+ throw new CorruptLongObjectException(id, contentHash,
+ MessageFormat.format(LfsText.get().corruptLongObject,
+ contentHash, id));
+ }
+ }
+
+ /**
+ * Aborts the stream. Temporary file will be deleted
+ */
+ public void abort() {
+ locked.unlock();
+ aborted = true;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java
index 365eaa1727..c76df39354 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/LfsText.java
@@ -58,6 +58,8 @@ public class LfsText extends TranslationBundle {
}
// @formatter:off
+ /***/ public String corruptLongObject;
+ /***/ public String inconsistentMediafileLength;
/***/ public String incorrectLONG_OBJECT_ID_LENGTH;
/***/ public String invalidLongId;
/***/ public String invalidLongIdLength;
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java
index d2464126cc..a88057afce 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/Constants.java
@@ -55,8 +55,19 @@ import org.eclipse.jgit.lfs.internal.LfsText;
**/
@SuppressWarnings("nls")
public final class Constants {
- /** Hash function used natively by Git LFS extension for large objects. */
- private static final String LONG_HASH_FUNCTION = "SHA-256";
+ /**
+ * lfs folder
+ *
+ * @since 4.6
+ */
+ public static final String LFS = "lfs";
+
+ /**
+ * Hash function used natively by Git LFS extension for large objects.
+ *
+ * @since 4.6
+ */
+ public static final String LONG_HASH_FUNCTION = "SHA-256";
/**
* A Git LFS large object hash is 256 bits, i.e. 32 bytes.
@@ -104,11 +115,11 @@ public final class Constants {
* Content type used by LFS REST API as defined in
* {@link "https://github.com/github/git-lfs/blob/master/docs/api/v1/http-v1-batch.md"}
*/
- public static String CONTENT_TYPE_GIT_LFS_JSON = "application/vnd.git-lfs+json";
+ public static final String CONTENT_TYPE_GIT_LFS_JSON = "application/vnd.git-lfs+json";
/**
* "arbitrary binary data" as defined in RFC 2046
* {@link "https://www.ietf.org/rfc/rfc2046.txt"}
*/
- public static String HDR_APPLICATION_OCTET_STREAM = "application/octet-stream";
+ public static final String HDR_APPLICATION_OCTET_STREAM = "application/octet-stream";
}