Merge branch 'stable-4.6'

* stable-4.6:
  Update Orbit to S20170120205402 and com.jcraft.jsch to 0.1.54
  Fix preparation of 4.6.1-SNAPSHOT builds

Change-Id: Ifd1f81cb199a0b5bd35e8652cac116e377136b2d
Signed-off-by: David Pursehouse <david.pursehouse@gmail.com>
diff --git a/.gitignore b/.gitignore
index e78f8fd..b52fac6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,9 @@
 /buck-out
 /target
 infer-out
+bazel-bin
+bazel-genfiles
+bazel-jgit
+bazel-out
+bazel-testlogs
+*~
diff --git a/BUILD b/BUILD
new file mode 100644
index 0000000..f94a952
--- /dev/null
+++ b/BUILD
@@ -0,0 +1,22 @@
+package(default_visibility = ["//visibility:public"])
+
+genrule(
+    name = "all",
+    srcs = [
+        "//org.eclipse.jgit:jgit",
+        "//org.eclipse.jgit.archive:jgit-archive",
+        "//org.eclipse.jgit.http.apache:http-apache",
+        "//org.eclipse.jgit.http.server:jgit-servlet",
+        "//org.eclipse.jgit.lfs:jgit-lfs",
+        "//org.eclipse.jgit.lfs.server:jgit-lfs-server",
+        "//org.eclipse.jgit.junit:junit",
+    ],
+    outs = ["all.zip"],
+    cmd = " && ".join([
+        "p=$$PWD",
+        "t=$$(mktemp -d || mktemp -d -t bazel-tmp)",
+        "cp $(SRCS) $$t",
+        "cd $$t",
+        "zip -qr $$p/$@ .",
+    ]),
+)
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..3551c1a
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,100 @@
+workspace(name = "jgit")
+
+load("//tools:bazlets.bzl", "load_bazlets")
+
+load_bazlets(commit = "3afbeab55ece585dbfc7a980bf7214b24ddbbe86")
+
+load(
+    "@com_googlesource_gerrit_bazlets//tools:maven_jar.bzl",
+    "maven_jar",
+)
+
+maven_jar(
+    name = "jsch",
+    artifact = "com.jcraft:jsch:0.1.53",
+    sha1 = "658b682d5c817b27ae795637dfec047c63d29935",
+)
+
+maven_jar(
+    name = "javaewah",
+    artifact = "com.googlecode.javaewah:JavaEWAH:1.1.6",
+    sha1 = "94ad16d728b374d65bd897625f3fbb3da223a2b6",
+)
+
+maven_jar(
+    name = "httpclient",
+    artifact = "org.apache.httpcomponents:httpclient:4.3.6",
+    sha1 = "4c47155e3e6c9a41a28db36680b828ced53b8af4",
+)
+
+maven_jar(
+    name = "httpcore",
+    artifact = "org.apache.httpcomponents:httpcore:4.3.3",
+    sha1 = "f91b7a4aadc5cf486df6e4634748d7dd7a73f06d",
+)
+
+maven_jar(
+    name = "commons_logging",
+    artifact = "commons-logging:commons-logging:1.1.3",
+    sha1 = "f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f",
+)
+
+maven_jar(
+    name = "log_api",
+    artifact = "org.slf4j:slf4j-api:1.7.2",
+    sha1 = "0081d61b7f33ebeab314e07de0cc596f8e858d97",
+)
+
+maven_jar(
+    name = "slf4j_simple",
+    artifact = "org.slf4j:slf4j-simple:1.7.2",
+    sha1 = "760055906d7353ba4f7ce1b8908bc6b2e91f39fa",
+)
+
+maven_jar(
+    name = "servlet_api_3_1",
+    artifact = "javax.servlet:javax.servlet-api:3.1.0",
+    sha1 = "3cd63d075497751784b2fa84be59432f4905bf7c",
+)
+
+maven_jar(
+    name = "commons_compress",
+    artifact = "org.apache.commons:commons-compress:1.6",
+    sha1 = "c7d9b580aff9e9f1998361f16578e63e5c064699",
+)
+
+maven_jar(
+    name = "tukaani_xz",
+    artifact = "org.tukaani:xz:1.3",
+    sha1 = "66db21c8484120cb6a51b5b3ea47b6f383942bec",
+)
+
+maven_jar(
+    name = "args4j",
+    artifact = "args4j:args4j:2.0.15",
+    sha1 = "139441471327b9cc6d56436cb2a31e60eb6ed2ba",
+)
+
+maven_jar(
+    name = "junit",
+    artifact = "junit:junit:4.11",
+    sha1 = "4e031bb61df09069aeb2bffb4019e7a5034a4ee0",
+)
+
+maven_jar(
+    name = "hamcrest_library",
+    artifact = "org.hamcrest:hamcrest-library:1.3",
+    sha1 = "4785a3c21320980282f9f33d0d1264a69040538f",
+)
+
+maven_jar(
+    name = "hamcrest_core",
+    artifact = "org.hamcrest:hamcrest-core:1.3",
+    sha1 = "42a25dc3219429f0e5d060061f71acb49bf010a0",
+)
+
+maven_jar(
+    name = "gson",
+    artifact = "com.google.code.gson:gson:2.2.4",
+    sha1 = "a60a5e993c98c864010053cb901b7eab25306568",
+)
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index 5262d9c..2ac80dc 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -3,14 +3,14 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.ant.test
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.junit;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)",
+ org.eclipse.jgit.ant.tasks;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.junit;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)",
  org.hamcrest;version="[1.1.0,2.0.0)",
  org.junit;version="[4.0.0,5.0.0)"
diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml
index 31d7f9f..31714c1 100644
--- a/org.eclipse.jgit.ant.test/pom.xml
+++ b/org.eclipse.jgit.ant.test/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ant.test</artifactId>
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index 1525b72..f583eec 100644
--- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
@@ -2,11 +2,11 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name
 Bundle-SymbolicName: org.eclipse.jgit.ant
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
-  org.eclipse.jgit.storage.file;version="[4.6.1,4.7.0)"
+  org.eclipse.jgit.storage.file;version="[4.7.0,4.8.0)"
 Bundle-Localization: plugin
 Bundle-Vendor: %Provider-Name
-Export-Package: org.eclipse.jgit.ant.tasks;version="4.6.1";
+Export-Package: org.eclipse.jgit.ant.tasks;version="4.7.0";
  uses:="org.apache.tools.ant.types,org.apache.tools.ant"
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index 78c0e1f..ee0cefc 100644
--- a/org.eclipse.jgit.ant/pom.xml
+++ b/org.eclipse.jgit.ant/pom.xml
@@ -48,7 +48,7 @@
 	<parent>
 		<groupId>org.eclipse.jgit</groupId>
 		<artifactId>org.eclipse.jgit-parent</artifactId>
-		<version>4.6.1-SNAPSHOT</version>
+		<version>4.7.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>org.eclipse.jgit.ant</artifactId>
diff --git a/org.eclipse.jgit.archive/BUILD b/org.eclipse.jgit.archive/BUILD
new file mode 100644
index 0000000..e15fcfa
--- /dev/null
+++ b/org.eclipse.jgit.archive/BUILD
@@ -0,0 +1,16 @@
+package(default_visibility = ["//visibility:public"])
+
+java_library(
+    name = "jgit-archive",
+    srcs = glob(
+        ["src/**"],
+        exclude = ["src/org/eclipse/jgit/archive/FormatActivator.java"],
+    ),
+    resource_strip_prefix = "org.eclipse.jgit.archive/resources",
+    resources = glob(["resources/**"]),
+    deps = [
+        # We want these deps to be provided_deps
+        "//org.eclipse.jgit:jgit",
+        "@commons_compress//jar",
+    ],
+)
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index f3369da..b477de5 100644
--- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.archive
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -12,14 +12,14 @@
  org.apache.commons.compress.compressors.bzip2;version="[1.4,2.0)",
  org.apache.commons.compress.compressors.gzip;version="[1.4,2.0)",
  org.apache.commons.compress.compressors.xz;version="[1.4,2.0)",
- org.eclipse.jgit.api;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)",
+ org.eclipse.jgit.api;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)",
  org.osgi.framework;version="[1.3.0,2.0.0)"
 Bundle-ActivationPolicy: lazy
 Bundle-Activator: org.eclipse.jgit.archive.FormatActivator
-Export-Package: org.eclipse.jgit.archive;version="4.6.1";
+Export-Package: org.eclipse.jgit.archive;version="4.7.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.api,
    org.apache.commons.compress.archivers,
diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
index ae331c0..b1bcdbb 100644
--- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.archive - Sources
 Bundle-SymbolicName: org.eclipse.jgit.archive.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 4.6.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.6.1.qualifier";roots="."
+Bundle-Version: 4.7.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.7.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index ab64576..36aa1ba 100644
--- a/org.eclipse.jgit.archive/pom.xml
+++ b/org.eclipse.jgit.archive/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.archive</artifactId>
diff --git a/org.eclipse.jgit.http.apache/BUILD b/org.eclipse.jgit.http.apache/BUILD
new file mode 100644
index 0000000..0814537
--- /dev/null
+++ b/org.eclipse.jgit.http.apache/BUILD
@@ -0,0 +1,13 @@
+package(default_visibility = ["//visibility:public"])
+
+java_library(
+    name = "http-apache",
+    srcs = glob(["src/**"]),
+    resource_strip_prefix = "org.eclipse.jgit.http.apache/resources",
+    resources = glob(["resources/**"]),
+    deps = [
+        "//org.eclipse.jgit:jgit",
+        "@httpclient//jar",
+        "@httpcore//jar",
+    ],
+)
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index badb920..fc38413 100644
--- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %Bundle-Name
 Bundle-SymbolicName: org.eclipse.jgit.http.apache
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-Localization: plugin
 Bundle-Vendor: %Provider-Name
@@ -23,10 +23,10 @@
  org.apache.http.impl.client.cache;version="[4.3.0,5.0.0)",
  org.apache.http.impl.conn;version="[4.3.0,5.0.0)",
  org.apache.http.params;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.nls;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport.http;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="4.6.1";
+ org.eclipse.jgit.nls;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport.http;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="4.7.0";
   uses:="org.apache.http.client,
    org.eclipse.jgit.transport.http,
    org.apache.http.entity,
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml
index 6c42599..524547d 100644
--- a/org.eclipse.jgit.http.apache/pom.xml
+++ b/org.eclipse.jgit.http.apache/pom.xml
@@ -48,7 +48,7 @@
 	<parent>
 		<groupId>org.eclipse.jgit</groupId>
 		<artifactId>org.eclipse.jgit-parent</artifactId>
-		<version>4.6.1-SNAPSHOT</version>
+		<version>4.7.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>org.eclipse.jgit.http.apache</artifactId>
diff --git a/org.eclipse.jgit.http.server/BUILD b/org.eclipse.jgit.http.server/BUILD
new file mode 100644
index 0000000..1999208
--- /dev/null
+++ b/org.eclipse.jgit.http.server/BUILD
@@ -0,0 +1,13 @@
+package(default_visibility = ["//visibility:public"])
+
+java_library(
+    name = "jgit-servlet",
+    srcs = glob(["src/**"]),
+    resource_strip_prefix = "org.eclipse.jgit.http.server/resources",
+    resources = glob(["resources/**"]),
+    deps = [
+        # We want these deps to be provided_deps
+        "//org.eclipse.jgit:jgit",
+        "@servlet_api_3_1//jar",
+    ],
+)
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index a5accce..30e4b57 100644
--- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
@@ -2,13 +2,13 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.http.server
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.http.server;version="4.6.1",
- org.eclipse.jgit.http.server.glue;version="4.6.1";
+Export-Package: org.eclipse.jgit.http.server;version="4.7.0",
+ org.eclipse.jgit.http.server.glue;version="4.7.0";
   uses:="javax.servlet,javax.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="4.6.1";
+ org.eclipse.jgit.http.server.resolver;version="4.7.0";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.transport,
@@ -17,12 +17,12 @@
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
  javax.servlet.http;version="[2.5.0,3.2.0)",
- org.eclipse.jgit.errors;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport.resolver;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)"
+ org.eclipse.jgit.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)"
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index c80bc4e..29ba777 100644
--- a/org.eclipse.jgit.http.server/pom.xml
+++ b/org.eclipse.jgit.http.server/pom.xml
@@ -52,7 +52,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.server</artifactId>
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index f7bb403..7534000 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.http.test
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -22,24 +22,24 @@
  org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)",
  org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)",
  org.eclipse.jetty.util.thread;version="[9.0.0,10.0.0)",
- org.eclipse.jgit.errors;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.http.server;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.http.server.glue;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.http.server.resolver;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.junit;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.junit.http;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport.http;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport.resolver;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)",
+ org.eclipse.jgit.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.http.server;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.http.server.glue;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.http.server.resolver;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.junit;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.junit.http;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport.http;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.0.0,5.0.0)",
  org.junit.runner;version="[4.0.0,5.0.0)",
diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml
index 34509bd..3702fbd 100644
--- a/org.eclipse.jgit.http.test/pom.xml
+++ b/org.eclipse.jgit.http.test/pom.xml
@@ -51,7 +51,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index 144466a..9a0a6c8 100644
--- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.junit.http
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
@@ -20,16 +20,16 @@
  org.eclipse.jetty.util.component;version="[9.0.0,10.0.0)",
  org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)",
  org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)",
- org.eclipse.jgit.errors;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.http.server;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.junit;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport.resolver;version="[4.6.1,4.7.0)",
+ org.eclipse.jgit.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.http.server;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.junit;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.7.0,4.8.0)",
  org.junit;version="[4.0.0,5.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="4.6.1";
+Export-Package: org.eclipse.jgit.junit.http;version="4.7.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.junit,
    javax.servlet.http,
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index d99e68c..6a94644 100644
--- a/org.eclipse.jgit.junit.http/pom.xml
+++ b/org.eclipse.jgit.junit.http/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit/BUILD b/org.eclipse.jgit.junit/BUILD
new file mode 100644
index 0000000..6d6462d
--- /dev/null
+++ b/org.eclipse.jgit.junit/BUILD
@@ -0,0 +1,13 @@
+package(default_visibility = ["//visibility:public"])
+
+java_library(
+    name = "junit",
+    srcs = glob(["src/**"]),
+    resource_strip_prefix = "org.eclipse.jgit.junit/resources",
+    resources = glob(["resources/**"]),
+    deps = [
+        # We want these deps to be provided_deps
+        "//org.eclipse.jgit:jgit",
+        "@junit//jar",
+    ],
+)
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index cde23b2..6cbb54e 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -2,31 +2,31 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.junit
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.api;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.api.errors;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.dircache;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.errors;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.merge;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.treewalk;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util.io;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util.time;version="[4.6.1,4.7.0)",
+Import-Package: org.eclipse.jgit.api;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.api.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.dircache;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.merge;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.treewalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util.io;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util.time;version="[4.7.0,4.8.0)",
  org.junit;version="[4.0.0,5.0.0)",
  org.junit.rules;version="[4.9.0,5.0.0)",
  org.junit.runner;version="[4.0.0,5.0.0)",
  org.junit.runners.model;version="[4.5.0,5.0.0)"
-Export-Package: org.eclipse.jgit.junit;version="4.6.1";
+Export-Package: org.eclipse.jgit.junit;version="4.7.0";
   uses:="org.eclipse.jgit.dircache,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
@@ -35,4 +35,4 @@
    org.eclipse.jgit.util,
    org.eclipse.jgit.storage.file,
    org.eclipse.jgit.api",
- org.eclipse.jgit.junit.time;version="4.6.1"
+ org.eclipse.jgit.junit.time;version="4.7.0"
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index 199d257..e4918c8 100644
--- a/org.eclipse.jgit.junit/pom.xml
+++ b/org.eclipse.jgit.junit/pom.xml
@@ -52,7 +52,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
index aae1e03..3208223 100644
--- a/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server.test/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.lfs.server.test
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -27,11 +27,11 @@
  org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)",
  org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)",
  org.eclipse.jetty.util.thread;version="[9.0.0,10.0.0)",
- org.eclipse.jgit.junit.http;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs.server.fs;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs.test;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)",
+ org.eclipse.jgit.junit.http;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs.test;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.0.0,5.0.0)",
  org.junit.runner;version="[4.0.0,5.0.0)",
diff --git a/org.eclipse.jgit.lfs.server.test/pom.xml b/org.eclipse.jgit.lfs.server.test/pom.xml
index 24b0371..c962f12 100644
--- a/org.eclipse.jgit.lfs.server.test/pom.xml
+++ b/org.eclipse.jgit.lfs.server.test/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server.test</artifactId>
diff --git a/org.eclipse.jgit.lfs.server/.settings/.api_filters b/org.eclipse.jgit.lfs.server/.settings/.api_filters
deleted file mode 100644
index e662937..0000000
--- a/org.eclipse.jgit.lfs.server/.settings/.api_filters
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<component id="org.eclipse.jgit.lfs.server" version="2">
-    <resource path="src/org/eclipse/jgit/lfs/server/LfsProtocolServlet.java" type="org.eclipse.jgit.lfs.server.LfsProtocolServlet">
-        <filter comment="breaking implementors only which is ok under OSGi semver rules" id="336695337">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lfs.server.LfsProtocolServlet"/>
-                <message_argument value="getLargeFileRepository(LfsProtocolServlet.LfsRequest, String)"/>
-            </message_arguments>
-        </filter>
-        <filter comment="breaking implementors only which is ok under OSGi semver rules" id="338792546">
-            <message_arguments>
-                <message_argument value="org.eclipse.jgit.lfs.server.LfsProtocolServlet"/>
-                <message_argument value="getLargeFileRepository()"/>
-            </message_arguments>
-        </filter>
-    </resource>
-</component>
diff --git a/org.eclipse.jgit.lfs.server/BUCK b/org.eclipse.jgit.lfs.server/BUCK
index 6b40b7c..6494ff4 100644
--- a/org.eclipse.jgit.lfs.server/BUCK
+++ b/org.eclipse.jgit.lfs.server/BUCK
@@ -11,7 +11,8 @@
     '//org.eclipse.jgit.lfs:jgit-lfs',
     '//lib:gson',
     '//lib:httpcore',
-    '//lib:servlet-api'
+    '//lib:servlet-api',
+    '//lib:slf4j-api'
   ],
   visibility = ['PUBLIC'],
 )
diff --git a/org.eclipse.jgit.lfs.server/BUILD b/org.eclipse.jgit.lfs.server/BUILD
new file mode 100644
index 0000000..5f1e12a
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server/BUILD
@@ -0,0 +1,17 @@
+package(default_visibility = ["//visibility:public"])
+
+java_library(
+    name = "jgit-lfs-server",
+    srcs = glob(["src/**"]),
+    resource_strip_prefix = "org.eclipse.jgit.lfs.server/resources",
+    resources = glob(["resources/**"]),
+    deps = [
+        "//org.eclipse.jgit.http.apache:http-apache",
+        "//org.eclipse.jgit:jgit",
+        "//org.eclipse.jgit.lfs:jgit-lfs",
+        "@gson//jar",
+        "@httpcore//jar",
+        "@log_api//jar",
+        "@servlet_api_3_1//jar",
+    ],
+)
diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
index f6aae18..08cc189 100644
--- a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
@@ -2,19 +2,19 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.lfs.server
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.lfs.server;version="4.6.1";
+Export-Package: org.eclipse.jgit.lfs.server;version="4.7.0";
   uses:="javax.servlet.http,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.fs;version="4.6.1";
+ org.eclipse.jgit.lfs.server.fs;version="4.7.0";
   uses:="javax.servlet,
    javax.servlet.http,
    org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.internal;version="4.6.1";x-internal:=true,
- org.eclipse.jgit.lfs.server.s3;version="4.6.1";
+ org.eclipse.jgit.lfs.server.internal;version="4.7.0";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="4.7.0";
   uses:="org.eclipse.jgit.lfs.server,
    org.eclipse.jgit.lfs.lib"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -24,13 +24,14 @@
  javax.servlet.http;version="[3.1.0,4.0.0)",
  org.apache.http;version="[4.3.0,5.0.0)",
  org.apache.http.client;version="[4.3.0,5.0.0)",
- org.eclipse.jgit.annotations;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs.errors;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs.internal;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport.http;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)"
+ org.eclipse.jgit.annotations;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs.internal;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport.http;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)",
+ org.slf4j;version="[1.7.0,2.0.0)"
diff --git a/org.eclipse.jgit.lfs.server/pom.xml b/org.eclipse.jgit.lfs.server/pom.xml
index 61281aa..8d5030a 100644
--- a/org.eclipse.jgit.lfs.server/pom.xml
+++ b/org.eclipse.jgit.lfs.server/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.server</artifactId>
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsProtocolServlet.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsProtocolServlet.java
index 841074b..e774eba 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsProtocolServlet.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/LfsProtocolServlet.java
@@ -49,7 +49,11 @@
 import static org.apache.http.HttpStatus.SC_NOT_FOUND;
 import static org.apache.http.HttpStatus.SC_OK;
 import static org.apache.http.HttpStatus.SC_SERVICE_UNAVAILABLE;
+import static org.apache.http.HttpStatus.SC_UNAUTHORIZED;
 import static org.apache.http.HttpStatus.SC_UNPROCESSABLE_ENTITY;
+import static org.eclipse.jgit.lfs.lib.Constants.DOWNLOAD;
+import static org.eclipse.jgit.lfs.lib.Constants.UPLOAD;
+import static org.eclipse.jgit.lfs.lib.Constants.VERIFY;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -58,6 +62,7 @@
 import java.io.OutputStreamWriter;
 import java.io.Reader;
 import java.io.Writer;
+import java.text.MessageFormat;
 import java.util.List;
 
 import javax.servlet.ServletException;
@@ -71,8 +76,12 @@
 import org.eclipse.jgit.lfs.errors.LfsRateLimitExceeded;
 import org.eclipse.jgit.lfs.errors.LfsRepositoryNotFound;
 import org.eclipse.jgit.lfs.errors.LfsRepositoryReadOnly;
+import org.eclipse.jgit.lfs.errors.LfsUnauthorized;
 import org.eclipse.jgit.lfs.errors.LfsUnavailable;
 import org.eclipse.jgit.lfs.errors.LfsValidationError;
+import org.eclipse.jgit.lfs.internal.LfsText;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.gson.FieldNamingPolicy;
 import com.google.gson.Gson;
@@ -86,6 +95,8 @@
  * @since 4.3
  */
 public abstract class LfsProtocolServlet extends HttpServlet {
+	private static Logger LOG = LoggerFactory
+			.getLogger(LfsProtocolServlet.class);
 
 	private static final long serialVersionUID = 1L;
 
@@ -163,6 +174,30 @@ public String getOperation() {
 		public List<LfsObject> getObjects() {
 			return objects;
 		}
+
+		/**
+		 * @return true if the operation is upload.
+		 * @since 4.7
+		 */
+		public boolean isUpload() {
+			return operation.equals(UPLOAD);
+		}
+
+		/**
+		 * @return true if the operation is download.
+		 * @since 4.7
+		 */
+		public boolean isDownload() {
+			return operation.equals(DOWNLOAD);
+		}
+
+		/**
+		 * @return true if the operation is verify.
+		 * @since 4.7
+		 */
+		public boolean isVerify() {
+			return operation.equals(VERIFY);
+		}
 	}
 
 	@Override
@@ -181,7 +216,10 @@ protected void doPost(HttpServletRequest req, HttpServletResponse res)
 		try {
 			repo = getLargeFileRepository(request, path);
 			if (repo == null) {
-				throw new LfsException("unexpected error"); //$NON-NLS-1$
+				String error = MessageFormat
+						.format(LfsText.get().lfsFailedToGetRepository, path);
+				LOG.error(error);
+				throw new LfsException(error);
 			}
 			res.setStatus(SC_OK);
 			TransferHandler handler = TransferHandler
@@ -201,6 +239,8 @@ protected void doPost(HttpServletRequest req, HttpServletResponse res)
 			sendError(res, w, SC_INSUFFICIENT_STORAGE, e.getMessage());
 		} catch (LfsUnavailable e) {
 			sendError(res, w, SC_SERVICE_UNAVAILABLE, e.getMessage());
+		} catch (LfsUnauthorized e) {
+			sendError(res, w, SC_UNAUTHORIZED, e.getMessage());
 		} catch (LfsException e) {
 			sendError(res, w, SC_INTERNAL_SERVER_ERROR, e.getMessage());
 		} finally {
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/TransferHandler.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/TransferHandler.java
index bf5b61c..86ca2d3 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/TransferHandler.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/TransferHandler.java
@@ -44,6 +44,9 @@
 package org.eclipse.jgit.lfs.server;
 
 import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+import static org.eclipse.jgit.lfs.lib.Constants.DOWNLOAD;
+import static org.eclipse.jgit.lfs.lib.Constants.UPLOAD;
+import static org.eclipse.jgit.lfs.lib.Constants.VERIFY;
 
 import java.io.IOException;
 import java.text.MessageFormat;
@@ -58,18 +61,14 @@
 
 abstract class TransferHandler {
 
-	private static final String DOWNLOAD = "download"; //$NON-NLS-1$
-	private static final String UPLOAD = "upload"; //$NON-NLS-1$
-	private static final String VERIFY = "verify"; //$NON-NLS-1$
-
 	static TransferHandler forOperation(String operation,
 			LargeFileRepository repository, List<LfsObject> objects) {
 		switch (operation) {
-		case TransferHandler.UPLOAD:
+		case UPLOAD:
 			return new Upload(repository, objects);
-		case TransferHandler.DOWNLOAD:
+		case DOWNLOAD:
 			return new Download(repository, objects);
-		case TransferHandler.VERIFY:
+		case VERIFY:
 		default:
 			throw new UnsupportedOperationException(MessageFormat.format(
 					LfsServerText.get().unsupportedOperation, operation));
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectDownloadListener.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectDownloadListener.java
index f179b6c..cc43500 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectDownloadListener.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/fs/ObjectDownloadListener.java
@@ -61,9 +61,11 @@
 import org.eclipse.jgit.util.HttpSupport;
 
 /**
- * Handle asynchronous large object download
+ * Handle asynchronous large object download.
+ *
+ * @since 4.7
  */
-class ObjectDownloadListener implements WriteListener {
+public class ObjectDownloadListener implements WriteListener {
 
 	private static Logger LOG = Logger
 			.getLogger(ObjectDownloadListener.class.getName());
diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
index babae33..b5418cd 100644
--- a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
@@ -2,19 +2,23 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.lfs.test
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.junit;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs.errors;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)",
+Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.junit;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.treewalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)",
  org.hamcrest.core;version="[1.1.0,2.0.0)",
  org.junit;version="[4.0.0,5.0.0)",
  org.junit.runner;version="[4.0.0,5.0.0)",
  org.junit.runners;version="[4.0.0,5.0.0)"
-Export-Package: org.eclipse.jgit.lfs.test;version="4.6.1";x-friends:="org.eclipse.jgit.lfs.server.test"
+Export-Package: org.eclipse.jgit.lfs.test;version="4.7.0";x-friends:="org.eclipse.jgit.lfs.server.test"
 
diff --git a/org.eclipse.jgit.lfs.test/pom.xml b/org.eclipse.jgit.lfs.test/pom.xml
index 30c621a..0e84d79 100644
--- a/org.eclipse.jgit.lfs.test/pom.xml
+++ b/org.eclipse.jgit.lfs.test/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs.test</artifactId>
diff --git a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LfsPointerFilterTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LfsPointerFilterTest.java
new file mode 100644
index 0000000..a56f0df
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LfsPointerFilterTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2015, Dariusz Luksza <dariusz@luksza.org>
+ * 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.lib;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.junit.Test;
+
+public class LfsPointerFilterTest {
+
+	private static final int SIZE = 12345;
+
+	private static final String OID = "4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393";
+
+	private static final String[] NOT_VALID_LFS_FILES = { "", // empty file
+			// simulate java file
+			"package org.eclipse.jgit;",
+			// invalid LFS pointer, no oid and version
+			"version https://hawser.github.com/spec/v1\n",
+			// invalid LFS pointer, no version
+			"version https://hawser.github.com/spec/v1\n"
+					+ "oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393\n",
+			// invalid LFS pointer, no id
+			"version https://hawser.github.com/spec/v1\n" + "size 12345\n",
+			// invalid LFS pointer, wrong order of oid and size
+			"version https://hawser.github.com/spec/v1\n" + "size 12345\n"
+					+ "oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393\n" };
+
+	private static final String[] LFS_VERSION_DOMAINS = {
+			"hawser", "git-lfs"
+	};
+
+	private static final String[] VALID_LFS_FILES = {
+			// valid LFS pointer
+			"version https://%s.github.com/spec/v1\n"
+					+ "oid sha256:" + OID + "\n"
+					+ "size " + SIZE + "\n",
+			// valid LFS pointer with "custom" key
+			"version https://%s.github.com/spec/v1\n"
+					+ "custom key with value\n"
+					+ "oid sha256:" + OID + "\n"
+					+ "size " + SIZE + "\n",
+			// valid LFS pointer with key with "."
+			"version https://%s.github.com/spec/v1\n"
+					+ "oid sha256:" + OID + "\n"
+					+ "r.key key with .\n"
+					+ "size " + SIZE + "\n",
+			// valid LFS pointer with key with "-"
+			"version https://%s.github.com/spec/v1\n"
+					+ "oid sha256:" + OID + "\n"
+					+ "size " + SIZE + "\n"
+					+ "valid-name another valid key\n" };
+
+	@Test
+	public void testRegularFilesInRepositoryRoot() throws Exception {
+		for (String file : NOT_VALID_LFS_FILES) {
+			assertLfs("file.bin", file).withRecursive(false).shouldBe(false);
+		}
+	}
+
+	@Test
+	public void testNestedRegularFiles() throws Exception {
+		for (String file : NOT_VALID_LFS_FILES) {
+			assertLfs("a/file.bin", file).withRecursive(true).shouldBe(false);
+		}
+	}
+
+	@Test
+	public void testValidPointersInRepositoryRoot() throws Exception {
+		for (String domain : LFS_VERSION_DOMAINS) {
+			for (String file : VALID_LFS_FILES) {
+				assertLfs("file.bin", String.format(file, domain))
+						.withRecursive(true).shouldBe(true)
+					.check();
+			}
+		}
+	}
+
+	@Test
+	public void testValidNestedPointers() throws Exception {
+		for (String domain : LFS_VERSION_DOMAINS) {
+			for (String file : VALID_LFS_FILES) {
+				assertLfs("a/file.bin", String.format(file, domain))
+						.withRecursive(true).shouldBe(true).check();
+			}
+		}
+	}
+
+	@Test
+	public void testValidNestedPointersWithoutRecurrence() throws Exception {
+		for (String domain : LFS_VERSION_DOMAINS) {
+			for (String file : VALID_LFS_FILES) {
+				assertLfs("file.bin", String.format(file, domain))
+						.withRecursive(false).shouldBe(true).check();
+				assertLfs("a/file.bin", String.format(file, domain))
+						.withRecursive(false).shouldBe(false).check();
+			}
+		}
+	}
+
+	private static LfsTreeWalk assertLfs(String path, String content) {
+		return new LfsTreeWalk(path, content);
+	}
+
+	private static class LfsTreeWalk {
+		private final String path;
+
+		private final String content;
+
+		private boolean state;
+
+		private boolean recursive;
+
+		private TestRepository<InMemoryRepository> tr;
+
+		LfsTreeWalk(String path, String content) {
+			this.path = path;
+			this.content = content;
+		}
+
+		LfsTreeWalk withRecursive(boolean shouldBeRecursive) {
+			this.recursive = shouldBeRecursive;
+			return this;
+		}
+
+		LfsTreeWalk shouldBe(boolean shouldBeValid) {
+			this.state = shouldBeValid;
+			return this;
+		}
+
+		void check() throws Exception {
+			tr = new TestRepository<>(new InMemoryRepository(
+					new DfsRepositoryDescription("test")));
+			RevCommit commit = tr.branch("master").commit().add(path, content)
+					.message("initial commit").create();
+			RevTree tree = parseCommit(commit);
+			LfsPointerFilter filter = new LfsPointerFilter();
+			try (TreeWalk treeWalk = new TreeWalk(tr.getRepository())) {
+				treeWalk.addTree(tree);
+				treeWalk.setRecursive(recursive);
+				treeWalk.setFilter(filter);
+
+				if (state) {
+					assertTrue(treeWalk.next());
+					assertEquals(path, treeWalk.getPathString());
+					assertNotNull(filter.getPointer());
+					assertEquals(SIZE, filter.getPointer().getSize());
+					assertEquals(OID, filter.getPointer().getOid().name());
+				} else {
+					assertFalse(treeWalk.next());
+					assertNull(filter.getPointer());
+				}
+			}
+		}
+
+		private RevTree parseCommit(RevCommit commit) throws Exception {
+			try (ObjectWalk ow = new ObjectWalk(tr.getRepository())) {
+				return ow.parseCommit(commit).getTree();
+			}
+		}
+	}
+}
diff --git a/org.eclipse.jgit.lfs/.settings/.api_filters b/org.eclipse.jgit.lfs/.settings/.api_filters
deleted file mode 100644
index be675b1..0000000
--- a/org.eclipse.jgit.lfs/.settings/.api_filters
+++ /dev/null
@@ -1,17 +0,0 @@
-<?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/BUILD b/org.eclipse.jgit.lfs/BUILD
new file mode 100644
index 0000000..c4c9f8a
--- /dev/null
+++ b/org.eclipse.jgit.lfs/BUILD
@@ -0,0 +1,11 @@
+package(default_visibility = ["//visibility:public"])
+
+java_library(
+    name = "jgit-lfs",
+    srcs = glob(["src/**"]),
+    resource_strip_prefix = "org.eclipse.jgit.lfs/resources",
+    resources = glob(["resources/**"]),
+    deps = [
+        "//org.eclipse.jgit:jgit",
+    ],
+)
diff --git a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
index 7516915..c8e3ec8 100644
--- a/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.lfs/META-INF/MANIFEST.MF
@@ -2,17 +2,20 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.lfs
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.lfs;version="4.6.1",
- org.eclipse.jgit.lfs.errors;version="4.6.1",
- org.eclipse.jgit.lfs.internal;version="4.6.1";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.1"
+Export-Package: org.eclipse.jgit.lfs;version="4.7.0",
+ org.eclipse.jgit.lfs.errors;version="4.7.0",
+ org.eclipse.jgit.lfs.internal;version="4.7.0";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.7.0"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.annotations;version="[4.6.1,4.7.0)";resolution:=optional,
- org.eclipse.jgit.attributes;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)"
+Import-Package: org.eclipse.jgit.annotations;version="[4.7.0,4.8.0)";resolution:=optional,
+ org.eclipse.jgit.attributes;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.treewalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)"
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index 79dff99..aa316fd 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.6.1-SNAPSHOT</version>
+    <version>4.7.0-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 5e52a78..e08e28c 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
@@ -7,3 +7,5 @@
 repositoryNotFound=Repository {0} not found
 repositoryReadOnly=Repository {0} is read-only
 lfsUnavailable=LFS is not available for repository {0}
+lfsUnathorized=Not authorized to perform operation {0} on repository {1}
+lfsFailedToGetRepository=failed to get 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
index 66feca7..c4e34ee 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/CleanFilter.java
@@ -151,7 +151,10 @@ public int run() throws IOException {
 						FileUtils.delete(tmpFile.toFile());
 					}
 				} else {
-					FileUtils.mkdirs(mediaFile.getParent().toFile(), true);
+					Path parent = mediaFile.getParent();
+					if (parent != null) {
+						FileUtils.mkdirs(parent.toFile(), true);
+					}
 					FileUtils.rename(tmpFile.toFile(), mediaFile.toFile(),
 							StandardCopyOption.ATOMIC_MOVE);
 				}
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
index bbea535..0aee1aa 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/LfsPointer.java
@@ -69,6 +69,12 @@ public class LfsPointer {
 	public static final String VERSION = "https://git-lfs.github.com/spec/v1"; //$NON-NLS-1$
 
 	/**
+	 * The version of the LfsPointer file format using legacy URL
+	 * @since 4.7
+	 */
+	public static final String VERSION_LEGACY = "https://hawser.github.com/spec/v1"; //$NON-NLS-1$
+
+	/**
 	 * The name of the hash function as used in the pointer files. This will
 	 * evaluate to "sha256"
 	 */
@@ -150,14 +156,13 @@ public static LfsPointer parseLfsPointer(InputStream in)
 				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)) {
+						&& (s.substring(8).trim().equals(VERSION) ||
+								s.substring(8).trim().equals(VERSION_LEGACY))) {
 					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) {
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/LfsUnauthorized.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/LfsUnauthorized.java
new file mode 100644
index 0000000..62b0cde
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/errors/LfsUnauthorized.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017, David Pursehouse <david.pursehouse@gmail.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.text.MessageFormat;
+
+import org.eclipse.jgit.lfs.internal.LfsText;
+
+/**
+ * Thrown when authorization was refused for an LFS operation.
+ *
+ * @since 4.7
+ */
+public class LfsUnauthorized extends LfsException {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * @param operation
+	 *            the operation that was attempted.
+	 * @param name
+	 *            the repository name.
+	 */
+	public LfsUnauthorized(String operation, String name) {
+		super(MessageFormat.format(LfsText.get().lfsUnathorized, operation,
+				name));
+	}
+}
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 c76df39..4459588 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
@@ -67,4 +67,6 @@ public static LfsText get() {
 	/***/ public String repositoryNotFound;
 	/***/ public String repositoryReadOnly;
 	/***/ public String lfsUnavailable;
+	/***/ public String lfsUnathorized;
+	/***/ public String lfsFailedToGetRepository;
 }
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 a88057a..0220743 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
@@ -87,6 +87,27 @@ public final class Constants {
 			* 2;
 
 	/**
+	 * LFS upload operation.
+	 *
+	 * @since 4.7
+	 */
+	public static final String UPLOAD = "upload";
+
+	/**
+	 * LFS download operation.
+	 *
+	 * @since 4.7
+	 */
+	public static final String DOWNLOAD = "download";
+
+	/**
+	 * LFS verify operation.
+	 *
+	 * @since 4.7
+	 */
+	public static final String VERIFY = "verify";
+
+	/**
 	 * Create a new digest function for objects.
 	 *
 	 * @return a new digest object.
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/LfsPointerFilter.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/LfsPointerFilter.java
new file mode 100644
index 0000000..6f672b8
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/LfsPointerFilter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015, 2017, Dariusz Luksza <dariusz@luksza.org>
+ * 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.lib;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lfs.LfsPointer;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectStream;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
+
+/**
+ * Detects Large File pointers, as described in [1] in Git repository.
+ *
+ * [1] https://github.com/github/git-lfs/blob/master/docs/spec.md
+ *
+ * @since 4.7
+ */
+public class LfsPointerFilter extends TreeFilter {
+
+	private LfsPointer pointer;
+
+	/**
+	 * @return {@link LfsPointer} or {@code null}
+	 */
+	public LfsPointer getPointer() {
+		return pointer;
+	}
+
+	@Override
+	public boolean include(TreeWalk walk) throws MissingObjectException,
+			IncorrectObjectTypeException, IOException {
+		pointer = null;
+		if (walk.isSubtree()) {
+			return walk.isRecursive();
+		}
+		ObjectId objectId = walk.getObjectId(0);
+		ObjectLoader object = walk.getObjectReader().open(objectId);
+		if (object.getSize() > 1024) {
+			return false;
+		}
+
+		try (ObjectStream stream = object.openStream()) {
+			pointer = LfsPointer.parseLfsPointer(stream);
+			return pointer != null;
+		}
+	}
+
+	@Override
+	public boolean shouldBeRecursive() {
+		return false;
+	}
+
+	@Override
+	public TreeFilter clone() {
+		return new LfsPointerFilter();
+	}
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
index 43a3900..77cb9b4 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit"
       label="%featureName"
-      version="4.6.1.qualifier"
+      version="4.7.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
index 43db9fd..21e45db 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
index 61f9b51..9c1b2ad 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.http.apache"
       label="%featureName"
-      version="4.6.1.qualifier"
+      version="4.7.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
index 10b5d4a..603de95 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
index 73a49aa..f2f97ff 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.junit"
       label="%featureName"
-      version="4.6.1.qualifier"
+      version="4.7.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
index 22aaac4..d90825c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
index 21aac0e..5ccb544 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.lfs"
       label="%featureName"
-      version="4.6.1.qualifier"
+      version="4.7.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
index 598402f..52f3a75 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.lfs.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
index eedde4f..25bdd49 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.pgm"
       label="%featureName"
-      version="4.6.1.qualifier"
+      version="4.7.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
@@ -31,8 +31,8 @@
          version="0.0.0"/>
 
    <requires>
-      <import feature="org.eclipse.jgit" version="4.6.1" match="equivalent"/>
-      <import feature="org.eclipse.jgit.lfs" version="4.6.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit" version="4.7.0" match="equivalent"/>
+      <import feature="org.eclipse.jgit.lfs" version="4.7.0" match="equivalent"/>
    </requires>
 
    <plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
index a579f51..dc56eca 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
index 4491184..c83014d 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.pgm.source"
       label="%featureName"
-      version="4.6.1.qualifier"
+      version="4.7.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
index 5e92866..b3b1371 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
index 2aba530..4b5d64f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.repository</artifactId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
index cf8e2b5..02da081 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
@@ -2,7 +2,7 @@
 <feature
       id="org.eclipse.jgit.source"
       label="%featureName"
-      version="4.6.1.qualifier"
+      version="4.7.0.qualifier"
       provider-name="%providerName">
 
    <description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
index beeab3b..85bce96 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
index 7cf2cfe..e2e87a9 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
@@ -2,4 +2,4 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: JGit Target Platform Bundle
 Bundle-SymbolicName: org.eclipse.jgit.target
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
index 350541a..5a8dad7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
@@ -49,7 +49,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>jgit.tycho.parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.target</artifactId>
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 4d09dee..0e8d9c7 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -53,7 +53,7 @@
 
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>jgit.tycho.parent</artifactId>
-  <version>4.6.1-SNAPSHOT</version>
+  <version>4.7.0-SNAPSHOT</version>
   <packaging>pom</packaging>
 
   <name>JGit Tycho Parent</name>
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index 90d6f4c..1e5f76d 100644
--- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
@@ -2,28 +2,28 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.pgm.test
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.api;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.api.errors;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.diff;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.dircache;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="4.6.1",
- org.eclipse.jgit.junit;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.merge;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.pgm;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.pgm.internal;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.pgm.opt;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.treewalk;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util.io;version="[4.6.1,4.7.0)",
+Import-Package: org.eclipse.jgit.api;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.api.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.diff;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.dircache;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="4.7.0",
+ org.eclipse.jgit.junit;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.merge;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.pgm;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.pgm.opt;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.treewalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util.io;version="[4.7.0,4.8.0)",
  org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
  org.junit;version="[4.11.0,5.0.0)",
  org.junit.rules;version="[4.11.0,5.0.0)",
diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml
index 57593cd..12dcb59 100644
--- a/org.eclipse.jgit.pgm.test/pom.xml
+++ b/org.eclipse.jgit.pgm.test/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm.test</artifactId>
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index 503f498..7571cec 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.pgm
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-Localization: plugin
@@ -27,46 +27,46 @@
  org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)",
  org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)",
  org.eclipse.jetty.util.thread;version="[9.0.0,10.0.0)",
- org.eclipse.jgit.api;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.api.errors;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.archive;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.awtui;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.blame;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.diff;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.dircache;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.errors;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.gitrepo;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.ketch;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs.server;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs.server.fs;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs.server.s3;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.merge;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.notes;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.revplot;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.revwalk.filter;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.storage.pack;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport.resolver;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.treewalk;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util.io;version="[4.6.1,4.7.0)",
+ org.eclipse.jgit.api;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.api.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.archive;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.awtui;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.blame;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.diff;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.dircache;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.gitrepo;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.ketch;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs.server;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.merge;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.notes;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revplot;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.storage.pack;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.treewalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util.io;version="[4.7.0,4.8.0)",
  org.kohsuke.args4j;version="[2.0.12,2.1.0)",
  org.kohsuke.args4j.spi;version="[2.0.15,2.1.0)"
-Export-Package: org.eclipse.jgit.console;version="4.6.1";
+Export-Package: org.eclipse.jgit.console;version="4.7.0";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="4.6.1";
+ org.eclipse.jgit.pgm;version="4.7.0";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.pgm.opt,
@@ -77,11 +77,11 @@
    org.eclipse.jgit.treewalk,
    javax.swing,
    org.eclipse.jgit.transport",
- org.eclipse.jgit.pgm.debug;version="4.6.1";
+ org.eclipse.jgit.pgm.debug;version="4.7.0";
   uses:="org.eclipse.jgit.util.io,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.pgm.internal;version="4.6.1";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="4.6.1";
+ org.eclipse.jgit.pgm.internal;version="4.7.0";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
+ org.eclipse.jgit.pgm.opt;version="4.7.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.kohsuke.args4j.spi,
diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
index 904bd19..4df0eaf 100644
--- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit.pgm - Sources
 Bundle-SymbolicName: org.eclipse.jgit.pgm.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 4.6.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.6.1.qualifier";roots="."
+Bundle-Version: 4.7.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.7.0.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index 19e09df..3b66793 100644
--- a/org.eclipse.jgit.pgm/pom.xml
+++ b/org.eclipse.jgit.pgm/pom.xml
@@ -50,7 +50,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm</artifactId>
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index 1d4bf76..06e4d94 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -246,6 +246,8 @@
 usage_MakeCacheTree=Show the current cache tree structure
 usage_MergeBase=Find as good common ancestors as possible for a merge
 usage_MergesTwoDevelopmentHistories=Merges two development histories
+usage_PreserveOldPacks=Preserve old pack files by moving them into the preserved subdirectory instead of deleting them after repacking
+usage_PrunePreserved=Remove the preserved subdirectory containing previously preserved old pack files before repacking, and before preserving more old pack files
 usage_ReadDirCache= Read the DirCache 100 times
 usage_RebuildCommitGraph=Recreate a repository from another one's commit graph
 usage_RebuildRefTree=Copy references into a RefTree
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Gc.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Gc.java
index bf45476..7289abb 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Gc.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Gc.java
@@ -52,10 +52,18 @@ class Gc extends TextBuiltin {
 	@Option(name = "--aggressive", usage = "usage_Aggressive")
 	private boolean aggressive;
 
+	@Option(name = "--preserve-oldpacks", usage = "usage_PreserveOldPacks")
+	private boolean preserveOldPacks;
+
+	@Option(name = "--prune-preserved", usage = "usage_PrunePreserved")
+	private boolean prunePreserved;
+
 	@Override
 	protected void run() throws Exception {
 		Git git = Git.wrap(db);
 		git.gc().setAggressive(aggressive)
+				.setPreserveOldPacks(preserveOldPacks)
+				.setPrunePreserved(prunePreserved)
 				.setProgressMonitor(new TextProgressMonitor(errw)).call();
 	}
 }
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
index 90c03e9..4842b98 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
@@ -68,7 +68,6 @@ public static CLIText get() {
 	 * @param line
 	 *            the line to format
 	 * @return the formatted line
-	 * @since 2.2
 	 */
 	public static String formatLine(String line) {
 		return MessageFormat.format(get().lineFormat, line);
@@ -81,7 +80,6 @@ public static String formatLine(String line) {
 	 * @param message
 	 *            the message to format
 	 * @return the formatted line
-	 * @since 4.2
 	 */
 	public static String fatalError(String message) {
 		return MessageFormat.format(get().fatalError, message);
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 601ed0f..a8a70df 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -2,53 +2,53 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.test
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
- org.eclipse.jgit.api;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.api.errors;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.attributes;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.awtui;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.blame;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.diff;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.dircache;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.errors;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.events;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.fnmatch;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.gitrepo;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.hooks;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.ignore;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.ignore.internal;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.junit;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lfs;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.merge;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.notes;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.patch;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.pgm;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.pgm.internal;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.revplot;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.revwalk.filter;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.storage.file;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.storage.pack;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.submodule;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport.http;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport.resolver;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.treewalk;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util.io;version="[4.6.1,4.7.0)",
+ org.eclipse.jgit.api;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.api.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.attributes;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.awtui;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.blame;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.diff;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.dircache;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.events;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.fnmatch;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.gitrepo;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.hooks;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.ignore;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.ignore.internal;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.junit;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lfs;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.merge;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.notes;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.patch;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.pgm;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revplot;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.storage.file;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.storage.pack;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.submodule;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport.http;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.treewalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util.io;version="[4.7.0,4.8.0)",
  org.junit;version="[4.4.0,5.0.0)",
  org.junit.experimental.theories;version="[4.4.0,5.0.0)",
  org.junit.rules;version="[4.11.0,5.0.0)",
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index bad592a..266cc53 100644
--- a/org.eclipse.jgit.test/pom.xml
+++ b/org.eclipse.jgit.test/pom.xml
@@ -52,7 +52,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.test</artifactId>
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
index ce235a7..8627115 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
@@ -736,4 +736,21 @@ public void untrackedFileConflictsWithWorkingDirectory()
 		}
 		assertEquals("working-directory", read(path));
 	}
+
+	@Test
+	public void untrackedAndTrackedChanges() throws Exception {
+		writeTrashFile(PATH, "changed");
+		String path = "untracked.txt";
+		writeTrashFile(path, "untracked");
+		git.stashCreate().setIncludeUntracked(true).call();
+		assertTrue(PATH + " should exist", check(PATH));
+		assertEquals(PATH + " should have been reset", "content", read(PATH));
+		assertFalse(path + " should not exist", check(path));
+		git.stashApply().setStashRef("stash@{0}").call();
+		assertTrue(PATH + " should exist", check(PATH));
+		assertEquals(PATH + " should have new content", "changed", read(PATH));
+		assertTrue(path + " should exist", check(path));
+		assertEquals(path + " should have new content", "untracked",
+				read(path));
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
index 41a1a5d..c7e5973 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
@@ -44,14 +44,18 @@
 package org.eclipse.jgit.internal.storage.file;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
-import java.util.Iterator;
+import java.util.List;
 
 import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
+import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.storage.pack.PackConfig;
 import org.junit.Test;
@@ -175,14 +179,9 @@ public void testNotPackTwice(boolean aggressive) throws Exception {
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
 
-		Iterator<PackFile> pIt = repo.getObjectDatabase().getPacks().iterator();
-		long c = pIt.next().getObjectCount();
-		if (c == 9)
-			assertEquals(2, pIt.next().getObjectCount());
-		else {
-			assertEquals(2, c);
-			assertEquals(9, pIt.next().getObjectCount());
-		}
+		List<PackFile> packs = new ArrayList<>(
+				repo.getObjectDatabase().getPacks());
+		assertEquals(11, packs.get(0).getObjectCount());
 	}
 
 	@Test
@@ -190,16 +189,26 @@ public void testDonePruneTooYoungPacks() throws Exception {
 		BranchBuilder bb = tr.branch("refs/heads/master");
 		bb.commit().message("M").add("M", "M").create();
 
+		String tempRef = "refs/heads/soon-to-be-unreferenced";
+		BranchBuilder bb2 = tr.branch(tempRef);
+		bb2.commit().message("M").add("M", "M").create();
+
 		gc.setExpireAgeMillis(0);
 		gc.gc();
 		stats = gc.getStatistics();
 		assertEquals(0, stats.numberOfLooseObjects);
-		assertEquals(3, stats.numberOfPackedObjects);
+		assertEquals(4, stats.numberOfPackedObjects);
 		assertEquals(1, stats.numberOfPackFiles);
 		File oldPackfile = tr.getRepository().getObjectDatabase().getPacks()
 				.iterator().next().getPackFile();
 
 		fsTick();
+
+		// delete the temp ref, orphaning its commit
+		RefUpdate update = tr.getRepository().getRefDatabase().newUpdate(tempRef, false);
+		update.setForceUpdate(true);
+		update.delete();
+
 		bb.commit().message("B").add("B", "Q").create();
 
 		// The old packfile is too young to be deleted. We should end up with
@@ -210,7 +219,7 @@ public void testDonePruneTooYoungPacks() throws Exception {
 		assertEquals(0, stats.numberOfLooseObjects);
 		// if objects exist in multiple packFiles then they are counted multiple
 		// times
-		assertEquals(9, stats.numberOfPackedObjects);
+		assertEquals(10, stats.numberOfPackedObjects);
 		assertEquals(2, stats.numberOfPackFiles);
 
 		// repack again but now without a grace period for loose objects. Since
@@ -221,15 +230,19 @@ public void testDonePruneTooYoungPacks() throws Exception {
 		assertEquals(0, stats.numberOfLooseObjects);
 		// if objects exist in multiple packFiles then they are counted multiple
 		// times
-		assertEquals(9, stats.numberOfPackedObjects);
+		assertEquals(10, stats.numberOfPackedObjects);
 		assertEquals(2, stats.numberOfPackFiles);
 
 		// repack again but now without a grace period for packfiles. We should
 		// end up with one packfile
 		gc.setPackExpireAgeMillis(0);
+
+		// we want to keep newly-loosened objects though
+		gc.setExpireAgeMillis(-1);
+
 		gc.gc();
 		stats = gc.getStatistics();
-		assertEquals(0, stats.numberOfLooseObjects);
+		assertEquals(1, stats.numberOfLooseObjects);
 		// if objects exist in multiple packFiles then they are counted multiple
 		// times
 		assertEquals(6, stats.numberOfPackedObjects);
@@ -237,7 +250,45 @@ public void testDonePruneTooYoungPacks() throws Exception {
 
 	}
 
-	private void configureGc(GC myGc, boolean aggressive) {
+	@Test
+	public void testPreserveAndPruneOldPacks() throws Exception {
+		testPreserveOldPacks();
+		configureGc(gc, false).setPrunePreserved(true);
+		gc.gc();
+
+		assertFalse(repo.getObjectDatabase().getPreservedDirectory().exists());
+	}
+
+	private void testPreserveOldPacks() throws Exception {
+		BranchBuilder bb = tr.branch("refs/heads/master");
+		bb.commit().message("P").add("P", "P").create();
+
+		// pack loose object into packfile
+		gc.setExpireAgeMillis(0);
+		gc.gc();
+		File oldPackfile = tr.getRepository().getObjectDatabase().getPacks()
+				.iterator().next().getPackFile();
+		assertTrue(oldPackfile.exists());
+
+		fsTick();
+		bb.commit().message("B").add("B", "Q").create();
+
+		// repack again but now without a grace period for packfiles. We should
+		// end up with a new packfile and the old one should be placed in the
+		// preserved directory
+		gc.setPackExpireAgeMillis(0);
+		configureGc(gc, false).setPreserveOldPacks(true);
+		gc.gc();
+
+		File oldPackDir = repo.getObjectDatabase().getPreservedDirectory();
+		String oldPackFileName = oldPackfile.getName();
+		String oldPackName = oldPackFileName.substring(0,
+				oldPackFileName.lastIndexOf('.')) + ".old-pack";  //$NON-NLS-1$
+		File preservePackFile = new File(oldPackDir, oldPackName);
+		assertTrue(preservePackFile.exists());
+	}
+
+	private PackConfig configureGc(GC myGc, boolean aggressive) {
 		PackConfig pconfig = new PackConfig(repo);
 		if (aggressive) {
 			pconfig.setDeltaSearchWindowSize(250);
@@ -246,5 +297,6 @@ private void configureGc(GC myGc, boolean aggressive) {
 		} else
 			pconfig = new PackConfig(repo);
 		myGc.setPackConfig(pconfig);
+		return pconfig;
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java
index 9e28298..1a50844 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcKeepFilesTest.java
@@ -73,7 +73,10 @@ public void testKeepFiles() throws Exception {
 				.iterator();
 		PackFile singlePack = packIt.next();
 		assertFalse(packIt.hasNext());
-		File keepFile = new File(singlePack.getPackFile().getPath() + ".keep");
+		String packFileName = singlePack.getPackFile().getPath();
+		String keepFileName = packFileName.substring(0,
+				packFileName.lastIndexOf('.')) + ".keep";
+		File keepFile = new File(keepFileName);
 		assertFalse(keepFile.exists());
 		assertTrue(keepFile.createNewFile());
 		bb.commit().add("A", "A2").add("B", "B2").create();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
index ea8dfa2..22bec37 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
@@ -46,6 +46,7 @@
 import static java.lang.Integer.valueOf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 
@@ -89,7 +90,7 @@ public void emptyRefDirectoryDeleted() throws Exception {
 		tr.branch(ref).commit().create();
 		String name = repo.findRef(ref).getName();
 		Path dir = repo.getDirectory().toPath().resolve(name).getParent();
-
+		assertNotNull(dir);
 		gc.packRefs();
 		assertFalse(Files.exists(dir));
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
index 87554ae..1293bef 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ObjectDirectoryTest.java
@@ -47,13 +47,18 @@
 
 import java.io.File;
 import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.MessageFormat;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 
+import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
@@ -61,10 +66,15 @@
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.junit.Assume;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 public class ObjectDirectoryTest extends RepositoryTestCase {
 
+	@Rule
+	public ExpectedException expectedEx = ExpectedException.none();
+
 	@Test
 	public void testConcurrentInsertionOfBlobsToTheSameNewFanOutDirectory()
 			throws Exception {
@@ -162,7 +172,7 @@ public boolean accept(File dir, String name) {
 					return name.endsWith(".pack");
 				}
 			});
-			assertTrue(ret.length == 1);
+			assertTrue(ret != null && ret.length == 1);
 			Assume.assumeTrue(tmpFile.lastModified() == ret[0].lastModified());
 
 			// all objects are in a new packfile but we will not detect it
@@ -171,6 +181,40 @@ public boolean accept(File dir, String name) {
 		}
 	}
 
+	@Test
+	public void testShallowFile()
+			throws Exception {
+		FileRepository repository = createBareRepository();
+		ObjectDirectory dir = repository.getObjectDatabase();
+
+		String commit = "d3148f9410b071edd4a4c85d2a43d1fa2574b0d2";
+		try (PrintWriter writer = new PrintWriter(
+				new File(repository.getDirectory(), Constants.SHALLOW))) {
+			writer.println(commit);
+		}
+		Set<ObjectId> shallowCommits = dir.getShallowCommits();
+		assertTrue(shallowCommits.remove(ObjectId.fromString(commit)));
+		assertTrue(shallowCommits.isEmpty());
+	}
+
+	@Test
+	public void testShallowFileCorrupt()
+			throws Exception {
+		FileRepository repository = createBareRepository();
+		ObjectDirectory dir = repository.getObjectDatabase();
+
+		String commit = "X3148f9410b071edd4a4c85d2a43d1fa2574b0d2";
+		try (PrintWriter writer = new PrintWriter(
+				new File(repository.getDirectory(), Constants.SHALLOW))) {
+			writer.println(commit);
+		}
+
+		expectedEx.expect(IOException.class);
+		expectedEx.expectMessage(MessageFormat
+				.format(JGitText.get().badShallowLine, commit));
+		dir.getShallowCommits();
+	}
+
 	private Collection<Callable<ObjectId>> blobInsertersForTheSameFanOutDir(
 			final ObjectDirectory dir) {
 		Callable<ObjectId> callable = new Callable<ObjectId>() {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
index bb553a4..388456f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
@@ -1705,7 +1705,8 @@ public void assertWorkDir(Map<String, String> i)
 							+ " in workDir. ", buffer, i.get(path).getBytes());
 					nrFiles++;
 				} else if (file.isDirectory()) {
-					if (file.list().length == 0) {
+					String[] files = file.list();
+					if (files != null && files.length == 0) {
 						assertEquals("found unexpected empty folder for path "
 								+ path + " in workDir. ", "/", i.get(path));
 						nrFiles++;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java
index e9d46bb..9c85fbe 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ValidRefNameTest.java
@@ -247,4 +247,86 @@ public void testWindowsReservedNames() {
 		assertValid(true, "refs/heads/conx");
 		assertValid(true, "refs/heads/xcon");
 	}
+
+	@Test
+	public void testNormalizeBranchName() {
+
+		assertEquals(true, Repository.normalizeBranchName("").equals(""));
+
+		assertEquals(true,
+				Repository.normalizeBranchName("__@#$@#$@$____   _")
+						.equals(""));
+
+		assertEquals(true,
+				Repository.normalizeBranchName("~`!@#$%^&*()_+}]{[|\\\";?>.<,/")
+						.equals(""));
+
+		assertEquals(true,
+				Repository.normalizeBranchName("Bug 12345 :::: Hello World")
+						.equals("Bug_12345-Hello_World"));
+
+		assertEquals(true,
+				Repository.normalizeBranchName("Bug 12345 :::: Hello::: World")
+						.equals("Bug_12345-Hello-_World"));
+
+		assertEquals(true,
+				Repository.normalizeBranchName(":::Bug 12345 - Hello World")
+						.equals("Bug_12345-Hello_World"));
+
+		assertEquals(true,
+				Repository.normalizeBranchName("---Bug 12345 - Hello World")
+						.equals("Bug_12345-Hello_World"));
+
+		assertEquals(true,
+				Repository.normalizeBranchName("Bug 12345 ---- Hello --- World")
+						.equals("Bug_12345-Hello-World"));
+
+		assertEquals(true, Repository.normalizeBranchName(null) == null);
+
+		assertEquals(true,
+				Repository.normalizeBranchName("Bug 12345 - Hello World!")
+						.equals("Bug_12345-Hello_World"));
+
+		assertEquals(true,
+				Repository.normalizeBranchName("Bug 12345 : Hello World!")
+						.equals("Bug_12345-Hello_World"));
+
+		assertEquals(true,
+				Repository.normalizeBranchName("Bug 12345 _ Hello World!")
+						.equals("Bug_12345_Hello_World"));
+
+		assertEquals(true,
+				Repository
+						.normalizeBranchName("Bug 12345   -       Hello World!")
+						.equals("Bug_12345-Hello_World"));
+
+		assertEquals(true,
+				Repository.normalizeBranchName(" Bug 12345   -   Hello World! ")
+						.equals("Bug_12345-Hello_World"));
+
+		assertEquals(true,
+				Repository
+						.normalizeBranchName(" Bug 12345   -   Hello World!   ")
+						.equals("Bug_12345-Hello_World"));
+
+		assertEquals(true,
+				Repository
+						.normalizeBranchName(
+								"Bug 12345   -   Hello______ World!")
+						.equals("Bug_12345-Hello_World"));
+
+		assertEquals(true,
+				Repository.normalizeBranchName("_Bug 12345 - Hello World!")
+						.equals("Bug_12345-Hello_World"));
+
+		assertEquals(true,
+				Repository
+						.normalizeBranchName(
+								"Bug 12345 - Hello Wo!@#$%^&*(rld {@")
+						.equals("Bug_12345-Hello_World_"));
+
+		assertEquals(true,
+				Repository.normalizeBranchName("Bug 1#$  2345 - Hello World")
+						.equals("Bug_12345-Hello_World"));
+	}
 }
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index 070de39..0f85601 100644
--- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
@@ -3,14 +3,14 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.ui
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Vendor: %provider_name
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Export-Package: org.eclipse.jgit.awtui;version="4.6.1"
-Import-Package: org.eclipse.jgit.errors;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.revplot;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.1,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.1,4.7.0)"
+Export-Package: org.eclipse.jgit.awtui;version="4.7.0"
+Import-Package: org.eclipse.jgit.errors;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revplot;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.0,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.0,4.8.0)"
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index 4fc6485..58935d4 100644
--- a/org.eclipse.jgit.ui/pom.xml
+++ b/org.eclipse.jgit.ui/pom.xml
@@ -52,7 +52,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ui</artifactId>
diff --git a/org.eclipse.jgit/BUCK b/org.eclipse.jgit/BUCK
index 2bae6dc..73e2080 100644
--- a/org.eclipse.jgit/BUCK
+++ b/org.eclipse.jgit/BUCK
@@ -9,7 +9,6 @@
     '//lib:javaewah',
     '//lib:jsch',
     '//lib:httpcomponents',
-    '//lib:servlet-api',
     '//lib:slf4j-api',
   ],
   visibility = ['PUBLIC'],
diff --git a/org.eclipse.jgit/BUILD b/org.eclipse.jgit/BUILD
new file mode 100644
index 0000000..a9b01e9
--- /dev/null
+++ b/org.eclipse.jgit/BUILD
@@ -0,0 +1,33 @@
+package(default_visibility = ["//visibility:public"])
+
+INSECURE_CIPHER_FACTORY = [
+    "src/org/eclipse/jgit/transport/InsecureCipherFactory.java",
+]
+
+SRCS = glob(
+    ["src/**"],
+    exclude = INSECURE_CIPHER_FACTORY,
+)
+
+RESOURCES = glob(["resources/**"])
+
+java_library(
+    name = "jgit",
+    srcs = SRCS,
+    resource_strip_prefix = "org.eclipse.jgit/resources",
+    resources = RESOURCES,
+    deps = [
+        ":insecure_cipher_factory",
+        "@httpclient//jar",
+        "@javaewah//jar",
+        "@jsch//jar",
+        "@log_api//jar",
+        "@servlet_api_3_1//jar",
+    ],
+)
+
+java_library(
+    name = "insecure_cipher_factory",
+    srcs = INSECURE_CIPHER_FACTORY,
+    javacopts = ["-Xep:InsecureCipherMode:OFF"],
+)
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 22d4275..8b78628 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -2,12 +2,12 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 4.6.1.qualifier
+Bundle-Version: 4.7.0.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.annotations;version="4.6.1",
- org.eclipse.jgit.api;version="4.6.1";
+Export-Package: org.eclipse.jgit.annotations;version="4.7.0",
+ org.eclipse.jgit.api;version="4.7.0";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff,
@@ -21,47 +21,51 @@
    org.eclipse.jgit.submodule,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="4.6.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="4.6.1",
- org.eclipse.jgit.blame;version="4.6.1";
+ org.eclipse.jgit.api.errors;version="4.7.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
+ org.eclipse.jgit.attributes;version="4.7.0",
+ org.eclipse.jgit.blame;version="4.7.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="4.6.1";
+ org.eclipse.jgit.diff;version="4.7.0";
   uses:="org.eclipse.jgit.patch,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="4.6.1";
+ org.eclipse.jgit.dircache;version="4.7.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.util,
    org.eclipse.jgit.events,
    org.eclipse.jgit.attributes",
- org.eclipse.jgit.errors;version="4.6.1";
+ org.eclipse.jgit.errors;version="4.7.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.internal.storage.pack,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.dircache",
- org.eclipse.jgit.events;version="4.6.1";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="4.6.1",
- org.eclipse.jgit.gitrepo;version="4.6.1";
+ org.eclipse.jgit.events;version="4.7.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.fnmatch;version="4.7.0",
+ org.eclipse.jgit.gitrepo;version="4.7.0";
   uses:="org.eclipse.jgit.api,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.xml.sax.helpers,
    org.xml.sax",
- org.eclipse.jgit.gitrepo.internal;version="4.6.1";x-internal:=true,
- org.eclipse.jgit.hooks;version="4.6.1";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="4.6.1",
- org.eclipse.jgit.ignore.internal;version="4.6.1";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="4.6.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.ketch;version="4.6.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.dfs;version="4.6.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.server,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.storage.file;version="4.6.1";
+ org.eclipse.jgit.gitrepo.internal;version="4.7.0";x-internal:=true,
+ org.eclipse.jgit.hooks;version="4.7.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="4.7.0",
+ org.eclipse.jgit.ignore.internal;version="4.7.0";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal;version="4.7.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.ketch;version="4.7.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.dfs;version="4.7.0";
+  x-friends:="org.eclipse.jgit.test,
+   org.eclipse.jgit.http.server,
+   org.eclipse.jgit.http.test,
+   org.eclipse.jgit.lfs.test",
+ org.eclipse.jgit.internal.storage.file;version="4.7.0";
   x-friends:="org.eclipse.jgit.test,
    org.eclipse.jgit.junit,
    org.eclipse.jgit.junit.http,
@@ -69,9 +73,9 @@
    org.eclipse.jgit.lfs,
    org.eclipse.jgit.pgm,
    org.eclipse.jgit.pgm.test",
- org.eclipse.jgit.internal.storage.pack;version="4.6.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftree;version="4.6.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.lib;version="4.6.1";
+ org.eclipse.jgit.internal.storage.pack;version="4.7.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftree;version="4.7.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.lib;version="4.7.0";
   uses:="org.eclipse.jgit.revwalk,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util,
@@ -81,32 +85,32 @@
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.transport,
    org.eclipse.jgit.submodule",
- org.eclipse.jgit.merge;version="4.6.1";
+ org.eclipse.jgit.merge;version="4.7.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.diff,
    org.eclipse.jgit.dircache,
    org.eclipse.jgit.api",
- org.eclipse.jgit.nls;version="4.6.1",
- org.eclipse.jgit.notes;version="4.6.1";
+ org.eclipse.jgit.nls;version="4.7.0",
+ org.eclipse.jgit.notes;version="4.7.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="4.6.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="4.6.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="4.6.1";
+ org.eclipse.jgit.patch;version="4.7.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="4.7.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.revwalk;version="4.7.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.6.1";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="4.6.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="4.6.1";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="4.6.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
- org.eclipse.jgit.transport;version="4.6.1";
+ org.eclipse.jgit.revwalk.filter;version="4.7.0";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="4.7.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="4.7.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="4.7.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.transport;version="4.7.0";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.internal.storage.pack,
@@ -118,29 +122,28 @@
    org.eclipse.jgit.transport.http,
    org.eclipse.jgit.errors,
    org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="4.6.1";uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="4.6.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
- org.eclipse.jgit.treewalk;version="4.6.1";
+ org.eclipse.jgit.transport.http;version="4.7.0";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="4.7.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
+ org.eclipse.jgit.treewalk;version="4.7.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.attributes,
    org.eclipse.jgit.treewalk.filter,
    org.eclipse.jgit.util,
    org.eclipse.jgit.dircache",
- org.eclipse.jgit.treewalk.filter;version="4.6.1";uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="4.6.1";
+ org.eclipse.jgit.treewalk.filter;version="4.7.0";uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.util;version="4.7.0";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.transport.http,
    org.eclipse.jgit.storage.file,
    org.ietf.jgss",
- org.eclipse.jgit.util.io;version="4.6.1",
- org.eclipse.jgit.util.time;version="4.6.1"
+ org.eclipse.jgit.util.io;version="4.7.0",
+ org.eclipse.jgit.util.time;version="4.7.0"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
  com.jcraft.jsch;version="[0.1.37,0.2.0)",
  javax.crypto,
  javax.net.ssl,
- javax.servlet.http;version="[2.5.0,3.2.0)",
  org.slf4j;version="[1.7.0,2.0.0)",
  org.xml.sax,
  org.xml.sax.helpers
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index e310d9b..9287b6f 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@
 Bundle-Name: org.eclipse.jgit - Sources
 Bundle-SymbolicName: org.eclipse.jgit.source
 Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 4.6.1.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="4.6.1.qualifier";roots="."
+Bundle-Version: 4.7.0.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="4.7.0.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index 1aee726..6465a00 100644
--- a/org.eclipse.jgit/pom.xml
+++ b/org.eclipse.jgit/pom.xml
@@ -53,7 +53,7 @@
   <parent>
     <groupId>org.eclipse.jgit</groupId>
     <artifactId>org.eclipse.jgit-parent</artifactId>
-    <version>4.6.1-SNAPSHOT</version>
+    <version>4.7.0-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit</artifactId>
@@ -89,11 +89,6 @@
       <artifactId>slf4j-api</artifactId>
     </dependency>
 
-    <dependency>
-      <groupId>javax.servlet</groupId>
-      <artifactId>javax.servlet-api</artifactId>
-      <scope>provided</scope>
-    </dependency>
   </dependencies>
 
   <build>
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 b9b74bf..eb6f3a0 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -31,6 +31,7 @@
 badObjectType=Bad object type: {0}
 badRef=Bad ref: {0}: {1}
 badSectionEntry=Bad section entry: {0}
+badShallowLine=Bad shallow line: {0}
 bareRepositoryNoWorkdirAndIndex=Bare Repository has neither a working tree, nor an index
 base64InputNotProperlyPadded=Base64 input not properly padded.
 baseLengthIncorrect=base length incorrect
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
index dd5da15..b065b94 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -151,23 +151,35 @@ public Git call() throws GitAPIException, InvalidRemoteException,
 		}
 	}
 
+	private static boolean isNonEmptyDirectory(File dir) {
+		if (dir != null && dir.exists()) {
+			File[] files = dir.listFiles();
+			return files != null && files.length != 0;
+		}
+		return false;
+	}
+
 	private Repository init(URIish u) throws GitAPIException {
 		InitCommand command = Git.init();
 		command.setBare(bare);
-		if (directory == null && gitDir == null)
+		if (directory == null && gitDir == null) {
 			directory = new File(u.getHumanishName(), Constants.DOT_GIT);
+		}
 		validateDirs(directory, gitDir, bare);
-		if (directory != null && directory.exists()
-				&& directory.listFiles().length != 0)
+		if (isNonEmptyDirectory(directory)) {
 			throw new JGitInternalException(MessageFormat.format(
 					JGitText.get().cloneNonEmptyDirectory, directory.getName()));
-		if (gitDir != null && gitDir.exists() && gitDir.listFiles().length != 0)
+		}
+		if (isNonEmptyDirectory(gitDir)) {
 			throw new JGitInternalException(MessageFormat.format(
 					JGitText.get().cloneNonEmptyDirectory, gitDir.getName()));
-		if (directory != null)
+		}
+		if (directory != null) {
 			command.setDirectory(directory);
-		if (gitDir != null)
+		}
+		if (gitDir != null) {
 			command.setGitDir(gitDir);
+		}
 		return command.call().getRepository();
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
index d0f729c..0f38db5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/GarbageCollectCommand.java
@@ -159,6 +159,38 @@ public GarbageCollectCommand setAggressive(boolean aggressive) {
 		return this;
 	}
 
+	/**
+	 * Whether to preserve old pack files instead of deleting them.
+	 *
+	 * @since 4.7
+	 * @param preserveOldPacks
+	 *            whether to preserve old pack files
+	 * @return this instance
+	 */
+	public GarbageCollectCommand setPreserveOldPacks(boolean preserveOldPacks) {
+		if (pconfig == null)
+			pconfig = new PackConfig(repo);
+
+		pconfig.setPreserveOldPacks(preserveOldPacks);
+		return this;
+	}
+
+	/**
+	 * Whether to prune preserved pack files in the preserved directory.
+	 *
+	 * @since 4.7
+	 * @param prunePreserved
+	 *            whether to prune preserved pack files
+	 * @return this instance
+	 */
+	public GarbageCollectCommand setPrunePreserved(boolean prunePreserved) {
+		if (pconfig == null)
+			pconfig = new PackConfig(repo);
+
+		pconfig.setPrunePreserved(prunePreserved);
+		return this;
+	}
+
 	@Override
 	public Properties call() throws GitAPIException {
 		checkCallable();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
index b8ee1ec..fc8bb87 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
@@ -232,19 +232,19 @@ public ObjectId call() throws GitAPIException,
 					untrackedMerger.setBase(null);
 					boolean ok = untrackedMerger.merge(headCommit,
 							untrackedCommit);
-					if (ok)
+					if (ok) {
 						try {
 							RevTree untrackedTree = revWalk
-									.parseTree(untrackedMerger
-											.getResultTreeId());
+									.parseTree(untrackedCommit);
 							resetUntracked(untrackedTree);
 						} catch (CheckoutConflictException e) {
 							throw new StashApplyFailureException(
-									JGitText.get().stashApplyConflict);
+									JGitText.get().stashApplyConflict, e);
 						}
-					else
+					} else {
 						throw new StashApplyFailureException(
 								JGitText.get().stashApplyConflict);
+					}
 				}
 			} else {
 				throw new StashApplyFailureException(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
index 819442c..fa7cc0d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
@@ -682,7 +682,7 @@ public void format(DiffEntry ent) throws IOException {
 	}
 
 	private static byte[] writeGitLinkText(AbbreviatedObjectId id) {
-		if (id.toObjectId().equals(ObjectId.zeroId())) {
+		if (ObjectId.zeroId().equals(id.toObjectId())) {
 			return EMPTY;
 		}
 		return encodeASCII("Subproject commit " + id.name() //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
index 8a35d35..55ded92 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
@@ -57,6 +57,7 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.gitrepo.RepoProject.CopyFile;
 import org.eclipse.jgit.gitrepo.internal.RepoText;
@@ -324,7 +325,7 @@ public List<RepoProject> getProjects() {
 	 *
 	 * @return filtered projects list reference, never null
 	 */
-	public List<RepoProject> getFilteredProjects() {
+	public @NonNull List<RepoProject> getFilteredProjects() {
 		return filteredProjects;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/AbstractMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/AbstractMatcher.java
index 4e90d8c..64c2a74 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/AbstractMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/AbstractMatcher.java
@@ -46,8 +46,6 @@
  * Base class for default methods as {@link #toString()} and such.
  * <p>
  * This class is immutable and thread safe.
- *
- * @since 3.6
  */
 public abstract class AbstractMatcher implements IMatcher {
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java
index 8bb4dfb..4c13e2f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java
@@ -44,8 +44,6 @@
 
 /**
  * Generic string matcher
- *
- * @since 3.6
  */
 public interface IMatcher {
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java
index 3d0ad09..3ebc6afb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java
@@ -44,8 +44,6 @@
 
 /**
  * Matcher for simple regex patterns starting with an asterisk, e.g. "*.tmp"
- *
- * @since 3.6
  */
 public class LeadingAsteriskMatcher extends NameMatcher {
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java
index 8beae83..888b989 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java
@@ -47,8 +47,6 @@
 /**
  * Matcher built from patterns for file names (single path segments). This class
  * is immutable and thread safe.
- *
- * @since 3.6
  */
 public class NameMatcher extends AbstractMatcher {
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
index c3f6694..adf1329 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
@@ -59,8 +59,6 @@
  * Matcher built by patterns consists of multiple path segments.
  * <p>
  * This class is immutable and thread safe.
- *
- * @since 3.6
  */
 public class PathMatcher extends AbstractMatcher {
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
index 70c5199..1c467fe 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
@@ -58,8 +58,6 @@
 /**
  * Various {@link String} related utility methods, written mostly to avoid
  * generation of new String objects (e.g. via splitting Strings etc).
- *
- * @since 3.6
  */
 public class Strings {
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java
index b927d27..c7064ac 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java
@@ -44,8 +44,6 @@
 
 /**
  * Matcher for simple patterns ending with an asterisk, e.g. "Makefile.*"
- *
- * @since 3.6
  */
 public class TrailingAsteriskMatcher extends NameMatcher {
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java
index 8f98152..f64050f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java
@@ -53,8 +53,6 @@
  * glob wildcards to Java {@link Pattern}'s.
  * <p>
  * This class is immutable and thread safe.
- *
- * @since 3.6
  */
 public class WildCardMatcher extends NameMatcher {
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java
index d578654..2f24e02 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java
@@ -47,8 +47,6 @@
  * matcher matches any path.
  * <p>
  * This class is immutable and thread safe.
- *
- * @since 3.6
  */
 public final class WildMatcher extends AbstractMatcher {
 
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 ada5bf7..244356c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -90,6 +90,7 @@ public static JGitText get() {
 	/***/ public String badObjectType;
 	/***/ public String badRef;
 	/***/ public String badSectionEntry;
+	/***/ public String badShallowLine;
 	/***/ public String bareRepositoryNoWorkdirAndIndex;
 	/***/ public String base64InputNotProperlyPadded;
 	/***/ public String baseLengthIncorrect;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BeforeDfsPackIndexLoadedListener.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BeforeDfsPackIndexLoadedListener.java
index 047c86f..9f7f350 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BeforeDfsPackIndexLoadedListener.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/BeforeDfsPackIndexLoadedListener.java
@@ -47,8 +47,6 @@
 
 /**
  * Receives {@link BeforeDfsPackIndexLoadedEvent}s.
- *
- * @since 2.2
  */
 public interface BeforeDfsPackIndexLoadedListener extends RepositoryListener {
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
index 089bfa4..feadedb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
@@ -122,7 +122,6 @@ public DfsBlockCacheConfig setBlockSize(final int newSize) {
 	/**
 	 * @return the estimated number of threads concurrently accessing the cache.
 	 *         <b>Default is 32.</b>
-	 * @since 4.6
 	 */
 	public int getConcurrencyLevel() {
 		return concurrencyLevel;
@@ -133,7 +132,6 @@ public int getConcurrencyLevel() {
 	 *            the estimated number of threads concurrently accessing the
 	 *            cache.
 	 * @return {@code this}
-	 * @since 4.6
 	 */
 	public DfsBlockCacheConfig setConcurrencyLevel(
 			final int newConcurrencyLevel) {
@@ -145,7 +143,6 @@ public DfsBlockCacheConfig setConcurrencyLevel(
 	 * @return highest percentage of {@link #getBlockLimit()} a single pack can
 	 *         occupy while being copied by the pack reuse strategy. <b>Default
 	 *         is 0.30, or 30%</b>.
-	 * @since 4.0
 	 */
 	public double getStreamRatio() {
 		return streamRatio;
@@ -155,7 +152,6 @@ public double getStreamRatio() {
 	 * @param ratio
 	 *            percentage of cache to occupy with a copied pack.
 	 * @return {@code this}
-	 * @since 4.0
 	 */
 	public DfsBlockCacheConfig setStreamRatio(double ratio) {
 		streamRatio = Math.max(0, Math.min(ratio, 1.0));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
index 6f760ca..f7d078f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
@@ -253,7 +253,7 @@ public boolean pack(ProgressMonitor pm) throws IOException {
 			for (Ref ref : refsBefore) {
 				if (ref.isSymbolic() || ref.getObjectId() == null)
 					continue;
-				if (isHead(ref))
+				if (isHead(ref) || isTag(ref))
 					allHeads.add(ref.getObjectId());
 				else if (RefTreeNames.isRefTree(refdb, ref.getName()))
 					txnHeads.add(ref.getObjectId());
@@ -461,6 +461,10 @@ private static boolean isHead(Ref ref) {
 		return ref.getName().startsWith(Constants.R_HEADS);
 	}
 
+	private static boolean isTag(Ref ref) {
+		return ref.getName().startsWith(Constants.R_TAGS);
+	}
+
 	private int objectsBefore() {
 		int cnt = 0;
 		for (DfsPackFile p : packsBefore)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
index 4eabb03..f15d427 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
@@ -187,7 +187,6 @@ public DfsPackDescription getPackDescription() {
 
 	/**
 	 * @return whether the pack index file is loaded and cached in memory.
-	 * @since 2.2
 	 */
 	public boolean isIndexLoaded() {
 		DfsBlockCache.Ref<PackIndex> idxref = index;
@@ -499,6 +498,7 @@ private long copyPackBypassCache(PackOutputStream out, DfsReader ctx)
 				rc.setReadAheadBytes(ctx.getOptions().getStreamPackBufferSize());
 			long position = 12;
 			long remaining = length - (12 + 20);
+			boolean packHeadSkipped = false;
 			while (0 < remaining) {
 				DfsBlock b = cache.get(key, alignToBlock(position));
 				if (b != null) {
@@ -508,6 +508,7 @@ private long copyPackBypassCache(PackOutputStream out, DfsReader ctx)
 					position += n;
 					remaining -= n;
 					rc.position(position);
+					packHeadSkipped = true;
 					continue;
 				}
 
@@ -517,7 +518,14 @@ private long copyPackBypassCache(PackOutputStream out, DfsReader ctx)
 					throw packfileIsTruncated();
 				else if (n > remaining)
 					n = (int) remaining;
-				out.write(buf.array(), 0, n);
+
+				if (!packHeadSkipped) {
+					// Need skip the 'PACK' header for the first read
+					out.write(buf.array(), 12, n - 12);
+					packHeadSkipped = true;
+				} else {
+					out.write(buf.array(), 0, n);
+				}
 				position += n;
 				remaining -= n;
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
index 8419807..d07c13d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
@@ -109,7 +109,6 @@ public DfsReaderOptions setStreamFileThreshold(final int newLimit) {
 	/**
 	 * @return number of bytes to use for buffering when streaming a pack file
 	 *         during copying. If 0 the block size of the pack is used.
-	 * @since 4.0
 	 */
 	public int getStreamPackBufferSize() {
 		return streamPackBufferSize;
@@ -120,7 +119,6 @@ public int getStreamPackBufferSize() {
 	 *            new buffer size in bytes for buffers used when streaming pack
 	 *            files during copying.
 	 * @return {@code this}
-	 * @since 4.0
 	 */
 	public DfsReaderOptions setStreamPackBufferSize(int bufsz) {
 		streamPackBufferSize = Math.max(0, bufsz);
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 fd21397..bdcf9f9 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
@@ -63,7 +63,6 @@ public InMemoryRepository build() throws IOException {
 	 *
 	 * @param repoDesc
 	 *            description of the repository.
-	 * @since 2.0
 	 */
 	public InMemoryRepository(DfsRepositoryDescription repoDesc) {
 		this(new Builder().setRepositoryDescription(repoDesc));
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 147e54d..04973f1 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
@@ -81,6 +81,8 @@
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.NoWorkTreeException;
 import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
+import org.eclipse.jgit.internal.storage.file.ObjectDirectoryInserter;
 import org.eclipse.jgit.internal.storage.pack.PackExt;
 import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.internal.storage.reftree.RefTreeNames;
@@ -90,6 +92,8 @@
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdSet;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Ref.Storage;
@@ -209,11 +213,41 @@ public Collection<PackFile> gc() throws IOException, ParseException {
 	}
 
 	/**
+	 * Loosen objects in a pack file which are not also in the newly-created
+	 * pack files.
+	 *
+	 * @param inserter
+	 * @param reader
+	 * @param pack
+	 * @param existing
+	 * @throws IOException
+	 */
+	private void loosen(ObjectDirectoryInserter inserter, ObjectReader reader, PackFile pack, HashSet<ObjectId> existing)
+			throws IOException {
+		for (PackIndex.MutableEntry entry : pack) {
+			ObjectId oid = entry.toObjectId();
+			if (existing.contains(oid)) {
+				continue;
+			}
+			existing.add(oid);
+			ObjectLoader loader = reader.open(oid);
+			inserter.insert(loader.getType(),
+					loader.getSize(),
+					loader.openStream(),
+					true /* create this object even though it's a duplicate */);
+		}
+	}
+
+	/**
 	 * Delete old pack files. What is 'old' is defined by specifying a set of
 	 * old pack files and a set of new pack files. Each pack file contained in
-	 * old pack files but not contained in new pack files will be deleted. If an
-	 * expirationDate is set then pack files which are younger than the
-	 * expirationDate will not be deleted.
+	 * old pack files but not contained in new pack files will be deleted. If
+	 * preserveOldPacks is set, keep a copy of the pack file in the preserve
+	 * directory. If an expirationDate is set then pack files which are younger
+	 * than the expirationDate will not be deleted nor preserved.
+	 * <p>
+	 * If we're not immediately expiring loose objects, loosen any objects
+	 * in the old pack files which aren't in the new pack files.
 	 *
 	 * @param oldPacks
 	 * @param newPacks
@@ -222,6 +256,18 @@ public Collection<PackFile> gc() throws IOException, ParseException {
 	 */
 	private void deleteOldPacks(Collection<PackFile> oldPacks,
 			Collection<PackFile> newPacks) throws ParseException, IOException {
+		HashSet<ObjectId> ids = new HashSet<>();
+		for (PackFile pack : newPacks) {
+			for (PackIndex.MutableEntry entry : pack) {
+				ids.add(entry.toObjectId());
+			}
+		}
+		ObjectReader reader = repo.newObjectReader();
+		ObjectDirectory dir = repo.getObjectDatabase();
+		ObjectDirectoryInserter inserter = dir.newInserter();
+		boolean shouldLoosen = getExpireDate() < Long.MAX_VALUE;
+
+		prunePreserved();
 		long packExpireDate = getPackExpireDate();
 		oldPackLoop: for (PackFile oldPack : oldPacks) {
 			String oldName = oldPack.getPackName();
@@ -235,15 +281,56 @@ private void deleteOldPacks(Collection<PackFile> oldPacks,
 					&& repo.getFS().lastModified(
 							oldPack.getPackFile()) < packExpireDate) {
 				oldPack.close();
+				if (shouldLoosen) {
+					loosen(inserter, reader, oldPack, ids);
+				}
 				prunePack(oldName);
 			}
 		}
-		// close the complete object database. Thats my only chance to force
+		// close the complete object database. That's my only chance to force
 		// rescanning and to detect that certain pack files are now deleted.
 		repo.getObjectDatabase().close();
 	}
 
 	/**
+	 * Deletes old pack file, unless 'preserve-oldpacks' is set, in which case it
+	 * moves the pack file to the preserved directory
+	 *
+	 * @param packFile
+	 * @param packName
+	 * @param ext
+	 * @param deleteOptions
+	 * @throws IOException
+	 */
+	private void removeOldPack(File packFile, String packName, PackExt ext,
+			int deleteOptions) throws IOException {
+		if (pconfig != null && pconfig.isPreserveOldPacks()) {
+			File oldPackDir = repo.getObjectDatabase().getPreservedDirectory();
+			FileUtils.mkdir(oldPackDir, true);
+
+			String oldPackName = "pack-" + packName + ".old-" + ext.getExtension();  //$NON-NLS-1$ //$NON-NLS-2$
+			File oldPackFile = new File(oldPackDir, oldPackName);
+			FileUtils.rename(packFile, oldPackFile);
+		} else {
+			FileUtils.delete(packFile, deleteOptions);
+		}
+	}
+
+	/**
+	 * Delete the preserved directory including all pack files within
+	 */
+	private void prunePreserved() {
+		if (pconfig != null && pconfig.isPrunePreserved()) {
+			try {
+				FileUtils.delete(repo.getObjectDatabase().getPreservedDirectory(),
+						FileUtils.RECURSIVE | FileUtils.RETRY | FileUtils.SKIP_MISSING);
+			} catch (IOException e) {
+				// Deletion of the preserved pack files failed. Silently return.
+			}
+		}
+	}
+
+	/**
 	 * Delete files associated with a single pack file. First try to delete the
 	 * ".pack" file because on some platforms the ".pack" file may be locked and
 	 * can't be deleted. In such a case it is better to detect this early and
@@ -262,7 +349,7 @@ private void prunePack(String packName) {
 			for (PackExt ext : extensions)
 				if (PackExt.PACK.equals(ext)) {
 					File f = nameFor(packName, "." + ext.getExtension()); //$NON-NLS-1$
-					FileUtils.delete(f, deleteOptions);
+					removeOldPack(f, packName, ext, deleteOptions);
 					break;
 				}
 			// The .pack file has been deleted. Delete as many as the other
@@ -271,7 +358,7 @@ private void prunePack(String packName) {
 			for (PackExt ext : extensions) {
 				if (!PackExt.PACK.equals(ext)) {
 					File f = nameFor(packName, "." + ext.getExtension()); //$NON-NLS-1$
-					FileUtils.delete(f, deleteOptions);
+					removeOldPack(f, packName, ext, deleteOptions);
 				}
 			}
 		} catch (IOException e) {
@@ -607,7 +694,7 @@ public Collection<PackFile> repack() throws IOException {
 			nonHeads.addAll(listRefLogObjects(ref, 0));
 			if (ref.isSymbolic() || ref.getObjectId() == null)
 				continue;
-			if (ref.getName().startsWith(Constants.R_HEADS))
+			if (isHead(ref) || isTag(ref))
 				allHeads.add(ref.getObjectId());
 			else if (RefTreeNames.isRefTree(refdb, ref.getName()))
 				txnHeads.add(ref.getObjectId());
@@ -660,6 +747,14 @@ else if (RefTreeNames.isRefTree(refdb, ref.getName()))
 		return ret;
 	}
 
+	private static boolean isHead(Ref ref) {
+		return ref.getName().startsWith(Constants.R_HEADS);
+	}
+
+	private static boolean isTag(Ref ref) {
+		return ref.getName().startsWith(Constants.R_TAGS);
+	}
+
 	/**
 	 * @param ref
 	 *            the ref which log should be inspected
@@ -1072,7 +1167,6 @@ public void setPackExpireAgeMillis(long packExpireAgeMillis) {
 	 * influence how packs are written and to implement something similar to
 	 * "git gc --aggressive"
 	 *
-	 * @since 3.6
 	 * @param pconfig
 	 *            the {@link PackConfig} used when writing packs
 	 */
@@ -1145,7 +1239,6 @@ public void setPackExpire(Date packExpire) {
 	 *
 	 * @param auto
 	 *            defines whether gc should do automatic housekeeping
-	 * @since 4.5
 	 */
 	public void setAuto(boolean auto) {
 		this.automatic = auto;
@@ -1204,9 +1297,10 @@ boolean tooManyLooseObjects() {
 				new DirectoryStream.Filter<Path>() {
 
 					public boolean accept(Path file) throws IOException {
-						return Files.isRegularFile(file) && PATTERN_LOOSE_OBJECT
-								.matcher(file.getFileName().toString())
-								.matches();
+						Path fileName = file.getFileName();
+						return Files.isRegularFile(file) && fileName != null
+								&& PATTERN_LOOSE_OBJECT
+										.matcher(fileName.toString()).matches();
 					}
 				})) {
 			for (Iterator<Path> iter = stream.iterator(); iter.hasNext();
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 ce9677a..fcc8d71 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
@@ -150,7 +150,6 @@ public LockFile(final File f, final FS fs) {
 	 *
 	 * @param f
 	 *            the file that will be locked.
-	 * @since 4.2
 	 */
 	public LockFile(final File f) {
 		ref = f;
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 ea80528..eec7fb7 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
@@ -125,6 +125,8 @@ public class ObjectDirectory extends FileObjectDatabase {
 
 	private final File packDirectory;
 
+	private final File preservedDirectory;
+
 	private final File alternatesFile;
 
 	private final AtomicReference<PackList> packList;
@@ -165,6 +167,7 @@ public ObjectDirectory(final Config cfg, final File dir,
 		objects = dir;
 		infoDirectory = new File(objects, "info"); //$NON-NLS-1$
 		packDirectory = new File(objects, "pack"); //$NON-NLS-1$
+		preservedDirectory = new File(packDirectory, "preserved"); //$NON-NLS-1$
 		alternatesFile = new File(infoDirectory, "alternates"); //$NON-NLS-1$
 		packList = new AtomicReference<PackList>(NO_PACKS);
 		unpackedObjectCache = new UnpackedObjectCache();
@@ -189,6 +192,13 @@ public final File getDirectory() {
 		return objects;
 	}
 
+	/**
+	 * @return the location of the <code>preserved</code> directory.
+	 */
+	public final File getPreservedDirectory() {
+		return preservedDirectory;
+	}
+
 	@Override
 	public boolean exists() {
 		return fs.exists(objects);
@@ -690,8 +700,14 @@ Set<ObjectId> getShallowCommits() throws IOException {
 			final BufferedReader reader = open(shallowFile);
 			try {
 				String line;
-				while ((line = reader.readLine()) != null)
-					shallowCommitsIds.add(ObjectId.fromString(line));
+				while ((line = reader.readLine()) != null) {
+					try {
+						shallowCommitsIds.add(ObjectId.fromString(line));
+					} catch (IllegalArgumentException ex) {
+						throw new IOException(MessageFormat
+								.format(JGitText.get().badShallowLine, line));
+					}
+				}
 			} finally {
 				reader.close();
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
index 9820e0e..a510431 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
@@ -86,34 +86,71 @@ class ObjectDirectoryInserter extends ObjectInserter {
 	@Override
 	public ObjectId insert(int type, byte[] data, int off, int len)
 			throws IOException {
+		return insert(type, data, off, len, false);
+	}
+
+	/**
+	 * Insert a loose object into the database. If createDuplicate is true,
+	 * write the loose object even if we already have it in the loose or packed
+	 * ODB.
+	 *
+	 * @param type
+	 * @param data
+	 * @param off
+	 * @param len
+	 * @param createDuplicate
+	 * @return ObjectId
+	 * @throws IOException
+	 */
+	private ObjectId insert(
+			int type, byte[] data, int off, int len, boolean createDuplicate)
+			throws IOException {
 		ObjectId id = idFor(type, data, off, len);
-		if (db.has(id)) {
+		if (!createDuplicate && db.has(id)) {
 			return id;
 		} else {
 			File tmp = toTemp(type, data, off, len);
-			return insertOneObject(tmp, id);
+			return insertOneObject(tmp, id, createDuplicate);
 		}
 	}
 
 	@Override
 	public ObjectId insert(final int type, long len, final InputStream is)
 			throws IOException {
+		return insert(type, len, is, false);
+	}
+
+	/**
+	 * Insert a loose object into the database. If createDuplicate is true,
+	 * write the loose object even if we already have it in the loose or packed
+	 * ODB.
+	 *
+	 * @param type
+	 * @param len
+	 * @param is
+	 * @param createDuplicate
+	 * @return ObjectId
+	 * @throws IOException
+	 */
+	ObjectId insert(int type, long len, InputStream is, boolean createDuplicate)
+			throws IOException {
 		if (len <= buffer().length) {
 			byte[] buf = buffer();
 			int actLen = IO.readFully(is, buf, 0);
-			return insert(type, buf, 0, actLen);
+			return insert(type, buf, 0, actLen, createDuplicate);
 
 		} else {
 			MessageDigest md = digest();
 			File tmp = toTemp(md, type, len, is);
 			ObjectId id = ObjectId.fromRaw(md.digest());
-			return insertOneObject(tmp, id);
+			return insertOneObject(tmp, id, createDuplicate);
 		}
 	}
 
-	private ObjectId insertOneObject(final File tmp, final ObjectId id)
+	private ObjectId insertOneObject(
+			File tmp, ObjectId id, boolean createDuplicate)
 			throws IOException, ObjectWritingException {
-		switch (db.insertUnpackedObject(tmp, id, false /* no duplicate */)) {
+		switch (db.insertUnpackedObject(tmp, id, createDuplicate)) {
 		case INSERTED:
 		case EXISTS_PACKED:
 		case EXISTS_LOOSE:
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
index b385b8a..edf44fb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
@@ -46,6 +46,7 @@
 package org.eclipse.jgit.internal.storage.file;
 
 import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.KEEP;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
 
 import java.io.EOFException;
@@ -241,7 +242,7 @@ public boolean hasObject(final AnyObjectId id) throws IOException {
 	 */
 	public boolean shouldBeKept() {
 		if (keepFile == null)
-			keepFile = new File(packFile.getPath() + ".keep"); //$NON-NLS-1$
+			keepFile = extFile(KEEP);
 		return keepFile.exists();
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogWriter.java
index fc80266..24d2c79 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogWriter.java
@@ -74,8 +74,6 @@
 
 /**
  * Utility for writing reflog entries
- *
- * @since 2.0
  */
 public class ReflogWriter {
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java
index 4ee27cc..248692f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackExt.java
@@ -53,6 +53,9 @@ public class PackExt {
 	/** A pack index file extension. */
 	public static final PackExt INDEX = newPackExt("idx"); //$NON-NLS-1$
 
+	/** A keep pack file extension. */
+	public static final PackExt KEEP = newPackExt("keep"); //$NON-NLS-1$
+
 	/** A pack bitmap index file extension. */
 	public static final PackExt BITMAP_INDEX = newPackExt("bitmap"); //$NON-NLS-1$
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
index 8b4d2e6..ffab1a7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
@@ -164,7 +164,7 @@ public class PackWriter implements AutoCloseable {
 	private static final int PACK_VERSION_GENERATED = 2;
 
 	/** Empty set of objects for {@code preparePack()}. */
-	public static Set<ObjectId> NONE = Collections.emptySet();
+	public static final Set<ObjectId> NONE = Collections.emptySet();
 
 	private static final Map<WeakReference<PackWriter>, Boolean> instances =
 			new ConcurrentHashMap<WeakReference<PackWriter>, Boolean>();
@@ -369,7 +369,6 @@ public PackWriter(final PackConfig config, final ObjectReader reader) {
 	 *            the callback to set
 	 *
 	 * @return this object for chaining.
-	 * @since 4.1
 	 */
 	public PackWriter setObjectCountCallback(ObjectCountCallback callback) {
 		this.callback = callback;
@@ -381,7 +380,6 @@ public PackWriter setObjectCountCallback(ObjectCountCallback callback) {
 	 *
 	 * @param clientShallowCommits
 	 *            the shallow commits in the client
-	 * @since 4.1
 	 */
 	public void setClientShallowCommits(Set<ObjectId> clientShallowCommits) {
 		stats.clientShallowCommits = Collections
@@ -742,8 +740,6 @@ public void preparePack(ProgressMonitor countingMonitor,
 	 *            Must not be {@code null}.
 	 * @throws IOException
 	 *            an I/O problem occured while reading objects.
-	 *
-	 * @since 4.5
 	 */
 	public void preparePack(ProgressMonitor countingMonitor,
 			@NonNull Set<? extends ObjectId> want,
@@ -1089,8 +1085,6 @@ public State getState() {
 
 	/**
 	 * Release all resources used by this writer.
-	 *
-	 * @since 4.0
 	 */
 	@Override
 	public void close() {
@@ -2258,8 +2252,6 @@ public long getTotalObjects() {
 		 * @return the count of objects that needed to be discovered through an
 		 *         object walk because they were not found in bitmap indices.
 		 *         Returns -1 if no bitmap indices were found.
-		 *
-		 * @since 4.0
 		 */
 		public long getBitmapIndexMisses() {
 			return statistics.getBitmapIndexMisses();
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 c5b2ef8..641262c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -4,6 +4,7 @@
  * Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
  * Copyright (C) 2006-2012, Shawn O. Pearce <spearce@spearce.org>
  * Copyright (C) 2012, Daniel Megert <daniel_megert@ch.ibm.com>
+ * Copyright (C) 2017, Wim Jongman <wim.jongman@remainsoftware.com>
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -1887,4 +1888,40 @@ public Set<String> getRemoteNames() {
 	public void autoGC(ProgressMonitor monitor) {
 		// default does nothing
 	}
+
+	/**
+	 * Normalizes the passed branch name into a possible valid branch name. The
+	 * validity of the returned name should be checked by a subsequent call to
+	 * {@link #isValidRefName(String)}.
+	 * <p/>
+	 * Future implementations of this method could be more restrictive or more
+	 * lenient about the validity of specific characters in the returned name.
+	 * <p/>
+	 * The current implementation returns a trimmed string only containing word
+	 * characters ([a-zA-Z_0-9]) and hyphens ('-'). Colons are replaced by
+	 * hyphens. Repeating underscores and hyphens are replaced by a single
+	 * occurrence. Underscores and hyphens at the beginning of the string are
+	 * removed.
+	 *
+	 * @param name
+	 *            The name to normalize.
+	 *
+	 * @return The normalized String or null if null was passed.
+	 * @since 4.7
+	 * @see #isValidRefName(String)
+	 */
+	public static String normalizeBranchName(String name) {
+		if (name == null || name.length() == 0) {
+			return name;
+		}
+		String result = name.trim();
+		return result.replaceAll("\\s+([_:-])*?\\s+", "$1") //$NON-NLS-1$//$NON-NLS-2$
+				.replaceAll(":", "-") //$NON-NLS-1$//$NON-NLS-2$
+				.replaceAll("\\s+", "_") //$NON-NLS-1$//$NON-NLS-2$
+				.replaceAll("_{2,}", "_") //$NON-NLS-1$//$NON-NLS-2$
+				.replaceAll("-{2,}", "-") //$NON-NLS-1$//$NON-NLS-2$
+				.replaceAll("[^\\w-]", "") //$NON-NLS-1$ //$NON-NLS-2$
+				.replaceAll("^_+", "") //$NON-NLS-1$//$NON-NLS-2$
+				.replaceAll("^-+", ""); //$NON-NLS-1$//$NON-NLS-2$
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
index d594e97..3176cb3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
@@ -75,6 +75,20 @@ public class PackConfig {
 	public static final boolean DEFAULT_REUSE_OBJECTS = true;
 
 	/**
+	 * Default value of keep old packs option: {@value}
+	 * @see #setPreserveOldPacks(boolean)
+	 * @since 4.7
+	 */
+	public static final boolean DEFAULT_PRESERVE_OLD_PACKS = false;
+
+	/**
+	 * Default value of prune old packs option: {@value}
+	 * @see #setPrunePreserved(boolean)
+	 * @since 4.7
+	 */
+	public static final boolean DEFAULT_PRUNE_PRESERVED = false;
+
+	/**
 	 * Default value of delta compress option: {@value}
 	 *
 	 * @see #setDeltaCompress(boolean)
@@ -204,6 +218,10 @@ public class PackConfig {
 
 	private boolean reuseObjects = DEFAULT_REUSE_OBJECTS;
 
+	private boolean preserveOldPacks = DEFAULT_PRESERVE_OLD_PACKS;
+
+	private boolean prunePreserved = DEFAULT_PRUNE_PRESERVED;
+
 	private boolean deltaBaseAsOffset = DEFAULT_DELTA_BASE_AS_OFFSET;
 
 	private boolean deltaCompress = DEFAULT_DELTA_COMPRESS;
@@ -281,6 +299,8 @@ public PackConfig(PackConfig cfg) {
 		this.compressionLevel = cfg.compressionLevel;
 		this.reuseDeltas = cfg.reuseDeltas;
 		this.reuseObjects = cfg.reuseObjects;
+		this.preserveOldPacks = cfg.preserveOldPacks;
+		this.prunePreserved = cfg.prunePreserved;
 		this.deltaBaseAsOffset = cfg.deltaBaseAsOffset;
 		this.deltaCompress = cfg.deltaCompress;
 		this.maxDeltaDepth = cfg.maxDeltaDepth;
@@ -364,6 +384,61 @@ public void setReuseObjects(boolean reuseObjects) {
 	}
 
 	/**
+	 * Checks whether to preserve old packs in a preserved directory
+	 *
+	 * Default setting: {@value #DEFAULT_PRESERVE_OLD_PACKS}
+	 *
+	 * @return true if repacking will preserve old pack files.
+	 * @since 4.7
+	 */
+	public boolean isPreserveOldPacks() {
+		return preserveOldPacks;
+	}
+
+	/**
+	 * Set preserve old packs configuration option for repacking.
+	 *
+	 * If enabled, old pack files are moved into a preserved subdirectory instead
+	 * of being deleted
+	 *
+	 * Default setting: {@value #DEFAULT_PRESERVE_OLD_PACKS}
+	 *
+	 * @param preserveOldPacks
+	 *            boolean indicating whether or not preserve old pack files
+	 * @since 4.7
+	 */
+	public void setPreserveOldPacks(boolean preserveOldPacks) {
+		this.preserveOldPacks = preserveOldPacks;
+	}
+
+	/**
+	 * Checks whether to remove preserved pack files in a preserved directory
+	 *
+	 * Default setting: {@value #DEFAULT_PRUNE_PRESERVED}
+	 *
+	 * @return true if repacking will remove preserved pack files.
+	 * @since 4.7
+	 */
+	public boolean isPrunePreserved() {
+		return prunePreserved;
+	}
+
+	/**
+	 * Set prune preserved configuration option for repacking.
+	 *
+	 * If enabled, preserved pack files are removed from a preserved subdirectory
+	 *
+	 * Default setting: {@value #DEFAULT_PRESERVE_OLD_PACKS}
+	 *
+	 * @param prunePreserved
+	 *            boolean indicating whether or not preserve old pack files
+	 * @since 4.7
+	 */
+	public void setPrunePreserved(boolean prunePreserved) {
+		this.prunePreserved = prunePreserved;
+	}
+
+	/**
 	 * True if writer can use offsets to point to a delta base.
 	 *
 	 * If true the writer may choose to use an offset to point to a delta base
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ServiceMayNotContinueException.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ServiceMayNotContinueException.java
index 81c5da3..87c9a500 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ServiceMayNotContinueException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ServiceMayNotContinueException.java
@@ -45,8 +45,6 @@
 
 import java.io.IOException;
 
-import javax.servlet.http.HttpServletResponse;
-
 import org.eclipse.jgit.internal.JGitText;
 
 /**
@@ -55,6 +53,7 @@
  * @since 2.0
  */
 public class ServiceMayNotContinueException extends IOException {
+	private static final int FORBIDDEN = 403;
 	private static final long serialVersionUID = 1L;
 
 	private final int statusCode;
@@ -63,7 +62,7 @@ public class ServiceMayNotContinueException extends IOException {
 	/** Initialize with no message. */
 	public ServiceMayNotContinueException() {
 		// Do not set a message.
-		statusCode = HttpServletResponse.SC_FORBIDDEN;
+		statusCode = FORBIDDEN;
 	}
 
 	/**
@@ -73,7 +72,7 @@ public ServiceMayNotContinueException() {
 	 */
 	public ServiceMayNotContinueException(String msg) {
 		super(msg);
-		statusCode = HttpServletResponse.SC_FORBIDDEN;
+		statusCode = FORBIDDEN;
 	}
 
 	/**
@@ -99,7 +98,7 @@ public ServiceMayNotContinueException(String msg, int statusCode) {
 	 */
 	public ServiceMayNotContinueException(String msg, Throwable cause) {
 		super(msg, cause);
-		statusCode = HttpServletResponse.SC_FORBIDDEN;
+		statusCode = FORBIDDEN;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/Base64.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/Base64.java
index ed5838a..c05570b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/Base64.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/Base64.java
@@ -7,6 +7,7 @@
 package org.eclipse.jgit.util;
 
 import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
 import java.text.MessageFormat;
 import java.util.Arrays;
 
@@ -184,11 +185,7 @@ public static String encodeBytes(byte[] source, int off, int len) {
 			e += 4;
 		}
 
-		try {
-			return new String(outBuff, 0, e, UTF_8);
-		} catch (UnsupportedEncodingException uue) {
-			return new String(outBuff, 0, e);
-		}
+		return new String(outBuff, 0, e, StandardCharsets.UTF_8);
 	}
 
 	/**
@@ -304,12 +301,7 @@ else if (source[srcOffset + 3] == EQUALS_SIGN) {
 	 * @return the decoded data
 	 */
 	public static byte[] decode(String s) {
-		byte[] bytes;
-		try {
-			bytes = s.getBytes(UTF_8);
-		} catch (UnsupportedEncodingException uee) {
-			bytes = s.getBytes();
-		}
+		byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
 		return decode(bytes, 0, bytes.length);
 	}
 }
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 dcd7970..2f570ee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -59,7 +59,6 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
@@ -1011,16 +1010,13 @@ public int runProcess(ProcessBuilder processBuilder,
 		IOException ioException = null;
 		try {
 			process = processBuilder.start();
-			final Callable<Void> errorGobbler = new StreamGobbler(
-					process.getErrorStream(), errRedirect);
-			final Callable<Void> outputGobbler = new StreamGobbler(
-					process.getInputStream(), outRedirect);
-			executor.submit(errorGobbler);
-			executor.submit(outputGobbler);
+			executor.execute(
+					new StreamGobbler(process.getErrorStream(), errRedirect));
+			executor.execute(
+					new StreamGobbler(process.getInputStream(), outRedirect));
 			OutputStream outputStream = process.getOutputStream();
 			if (inRedirect != null) {
-				new StreamGobbler(inRedirect, outputStream)
-						.call();
+				new StreamGobbler(inRedirect, outputStream).copy();
 			}
 			try {
 				outputStream.close();
@@ -1336,7 +1332,7 @@ public String normalize(String name) {
 	 * streams.
 	 * </p>
 	 */
-	private static class StreamGobbler implements Callable<Void> {
+	private static class StreamGobbler implements Runnable {
 		private InputStream in;
 
 		private OutputStream out;
@@ -1346,7 +1342,15 @@ public StreamGobbler(InputStream stream, OutputStream output) {
 			this.out = output;
 		}
 
-		public Void call() throws IOException {
+		public void run() {
+			try {
+				copy();
+			} catch (IOException e) {
+				// Do nothing on read failure; leave streams open.
+			}
+		}
+
+		void copy() throws IOException {
 			boolean writeFailure = false;
 			byte buffer[] = new byte[4096];
 			int readBytes;
@@ -1363,7 +1367,6 @@ public Void call() throws IOException {
 					}
 				}
 			}
-			return null;
 		}
 	}
 }
diff --git a/pom.xml b/pom.xml
index 40ccb90..f0fd3aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,7 +51,7 @@
   <groupId>org.eclipse.jgit</groupId>
   <artifactId>org.eclipse.jgit-parent</artifactId>
   <packaging>pom</packaging>
-  <version>4.6.1-SNAPSHOT</version>
+  <version>4.7.0-SNAPSHOT</version>
 
   <name>JGit - Parent</name>
   <url>${jgit-url}</url>
diff --git a/tools/BUILD b/tools/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/BUILD
diff --git a/tools/bazlets.bzl b/tools/bazlets.bzl
new file mode 100644
index 0000000..e14e488
--- /dev/null
+++ b/tools/bazlets.bzl
@@ -0,0 +1,17 @@
+NAME = "com_googlesource_gerrit_bazlets"
+
+def load_bazlets(
+    commit,
+    local_path = None
+  ):
+  if not local_path:
+      native.git_repository(
+          name = NAME,
+          remote = "https://gerrit.googlesource.com/bazlets",
+          commit = commit,
+      )
+  else:
+      native.local_repository(
+          name = NAME,
+          path = local_path,
+      )