Merge branch 'stable-4.6' into stable-4.7

* stable-4.6:
  Update Oxygen Orbit p2 repository to R20170516192513
  Fix exception handling for opening bitmap index files

Change-Id: I669fe48ce0034f9ea1977d38ee39099497422c1c
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
diff --git a/.buckconfig b/.buckconfig
deleted file mode 100644
index 7986494..0000000
--- a/.buckconfig
+++ /dev/null
@@ -1,26 +0,0 @@
-[alias]
-  all = //:all
-  jgit = //org.eclipse.jgit:jgit
-  jgit-archive = //org.eclipse.jgit.archive:jgit-archive
-  jgit_bin = //:jgit_bin
-  jgit-lfs = //org.eclipse.jgit.lfs:jgit-lfs
-  jgit-lfs-server = //org.eclipse.jgit.lfs.server:jgit-lfs-server
-  jgit-servlet = //org.eclipse.jgit.http.server:jgit-servlet
-
-[buildfile]
-  includes = //tools/default.defs
-
-[java]
-  src_roots = src, resources, tst
-  source_level = 8
-  target_level = 8
-
-[project]
-  ignore = .git
-
-[cache]
-  mode = dir
-
-[download]
-  maven_repo = http://repo1.maven.org/maven2
-  in_build = true
diff --git a/.buckversion b/.buckversion
deleted file mode 100644
index 7eb591f..0000000
--- a/.buckversion
+++ /dev/null
@@ -1 +0,0 @@
-e27df656657f93f8d57a7aaac69a5ae0e298a292
diff --git a/.gitignore b/.gitignore
index e78f8fd..963b8a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
-/.buckd
 /.project
-/buck-cache
-/buck-out
 /target
 infer-out
+bazel-bin
+bazel-genfiles
+bazel-jgit
+bazel-out
+bazel-testlogs
+*~
diff --git a/.mailmap b/.mailmap
index ab4c963..560baaa 100644
--- a/.mailmap
+++ b/.mailmap
@@ -2,6 +2,7 @@
 Roberto Tyley <roberto.tyley@guardian.co.uk> roberto <roberto.tyley@guardian.co.uk>
 Saša Živkov <sasa.zivkov@sap.com>            Sasa Zivkov <sasa.zivkov@sap.com>
 Saša Živkov <sasa.zivkov@sap.com>            Saša Živkov <zivkov@gmail.com>
+Saša Živkov <sasa.zivkov@sap.com>            Sasa Zivkov <zivkov@gmail.com>
 Shawn Pearce <spearce@spearce.org>           Shawn O. Pearce <sop@google.com>
 Shawn Pearce <spearce@spearce.org>           Shawn Pearce <sop@google.com>
 Shawn Pearce <spearce@spearce.org>           Shawn O. Pearce <spearce@spearce.org>
diff --git a/BUCK b/BUCK
deleted file mode 100644
index 4578e17..0000000
--- a/BUCK
+++ /dev/null
@@ -1,18 +0,0 @@
-DEPS = [
-  '//org.eclipse.jgit:jgit',
-  '//org.eclipse.jgit.archive:jgit-archive',
-  '//org.eclipse.jgit.http.server:jgit-servlet',
-  '//org.eclipse.jgit.lfs:jgit-lfs',
-  '//org.eclipse.jgit.lfs.server:jgit-lfs-server',
-  '//org.eclipse.jgit.pgm:jgit',
-]
-
-zip_file(
-  name = 'all',
-  srcs = DEPS,
-)
-
-sh_binary(
-  name = 'jgit_bin',
-  main = '//org.eclipse.jgit.pgm:jgit',
-)
diff --git a/BUILD b/BUILD
new file mode 100644
index 0000000..be6dd76
--- /dev/null
+++ b/BUILD
@@ -0,0 +1,25 @@
+package(default_visibility = ["//visibility:public"])
+
+genrule(
+    name = "all",
+    testonly = 1,
+    srcs = [
+        "//org.eclipse.jgit:jgit",
+        "//org.eclipse.jgit.pgm:pgm",
+        "//org.eclipse.jgit.ui:ui",
+        "//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..efa161b
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,150 @@
+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_codec",
+    artifact = "commons-codec:commons-codec:1.4",
+    sha1 = "4216af16d38465bbab0f3dff8efa14204f7a399a",
+)
+
+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",
+)
+
+JETTY_VER = "9.3.17.v20170317"
+
+maven_jar(
+    name = "jetty_servlet",
+    artifact = "org.eclipse.jetty:jetty-servlet:" + JETTY_VER,
+    sha1 = "ed6986b0d0ca7b9b0f9015c9efb80442e3043a8e",
+    src_sha1 = "ee6b4784a00a92e5c1b6111033b7ae41ac6052a3",
+)
+
+maven_jar(
+    name = "jetty_security",
+    artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VER,
+    sha1 = "ca52535569445682d42aaa97c7039442719a0507",
+    src_sha1 = "2ff9f4fb18b320fd5a0272a427bacc4d5fe7bc86",
+)
+
+maven_jar(
+    name = "jetty_server",
+    artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VER,
+    sha1 = "194e9a02e6ba249ef4a3f4bd56b4993087992299",
+    src_sha1 = "0c9bd572f530c411592aefb71781ecca0b3719a9",
+)
+
+maven_jar(
+    name = "jetty_http",
+    artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VER,
+    sha1 = "6c02d728e15d4868486254039c867a1ac3e4a52e",
+    src_sha1 = "3c0a2a82792f268631b4fefd77be9f126ec974b1",
+)
+
+maven_jar(
+    name = "jetty_io",
+    artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VER,
+    sha1 = "756a8cd2a1cbfb84a94973b6332dd3eccd47c0cd",
+    src_sha1 = "a9afa99cccb19b441364fa805d027f457cbbb136",
+)
+
+maven_jar(
+    name = "jetty_util",
+    artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VER,
+    sha1 = "b8512ab02819de01f0f5a5c6026163041f579beb",
+    src_sha1 = "96f8e3dcdc3660a5c91f19c46695daa70ac95625",
+)
diff --git a/lib/BUCK b/lib/BUCK
deleted file mode 100644
index d89af5d..0000000
--- a/lib/BUCK
+++ /dev/null
@@ -1,134 +0,0 @@
-maven_jar(
-  name = 'jsch',
-  bin_sha1 = '658b682d5c817b27ae795637dfec047c63d29935',
-  src_sha1 = '791359d94d6edcace686a56d0727ee093a2f7c33',
-  group = 'com.jcraft',
-  artifact = 'jsch',
-  version = '0.1.53',
-)
-
-maven_jar(
-  name = 'javaewah',
-  bin_sha1 = '94ad16d728b374d65bd897625f3fbb3da223a2b6',
-  src_sha1 = 'ca2745e91c6a51f8e6809d1579bda36ad83f1f58',
-  group = 'com.googlecode.javaewah',
-  artifact = 'JavaEWAH',
-  version = '1.1.6',
-)
-
-maven_jar(
-  name = 'httpcomponents',
-  bin_sha1 = '4c47155e3e6c9a41a28db36680b828ced53b8af4',
-  src_sha1 = 'af4d76be0c46ee26b0d9d1d4a34d244a633cac84',
-  group = 'org.apache.httpcomponents',
-  artifact = 'httpclient',
-  version = '4.3.6',
-)
-
-maven_jar(
-  name = 'httpcore',
-  bin_sha1 = 'f91b7a4aadc5cf486df6e4634748d7dd7a73f06d',
-  src_sha1 = '1b0aa62a6a91e9fa00c16f0a4a2c874804ed3b1e',
-  group = 'org.apache.httpcomponents',
-  artifact = 'httpcore',
-  version = '4.3.3',
-)
-
-maven_jar(
-  name = 'commons-logging',
-  bin_sha1 = 'f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f',
-  src_sha1 = '28bb0405fddaf04f15058fbfbe01fe2780d7d3b6',
-  group = 'commons-logging',
-  artifact = 'commons-logging',
-  version = '1.1.3',
-)
-
-maven_jar(
-  name = 'slf4j-api',
-  bin_sha1 = '0081d61b7f33ebeab314e07de0cc596f8e858d97',
-  src_sha1 = '58d38f68d4a867d4552ae27960bb348d7eaa1297',
-  group = 'org.slf4j',
-  artifact = 'slf4j-api',
-  version = '1.7.2',
-)
-
-maven_jar(
-  name = 'slf4j-simple',
-  bin_sha1 = '760055906d7353ba4f7ce1b8908bc6b2e91f39fa',
-  src_sha1 = '09474919128b3a7fcf21a5f9c907f5251f234544',
-  group = 'org.slf4j',
-  artifact = 'slf4j-simple',
-  version = '1.7.2',
-)
-
-maven_jar(
-  name = 'servlet-api',
-  bin_sha1 = '3cd63d075497751784b2fa84be59432f4905bf7c',
-  src_sha1 = 'ab3976d4574c48d22dc1abf6a9e8bd0fdf928223',
-  group = 'javax.servlet',
-  artifact = 'javax.servlet-api',
-  version = '3.1.0',
-)
-
-maven_jar(
-  name = 'commons-compress',
-  bin_sha1 = 'c7d9b580aff9e9f1998361f16578e63e5c064699',
-  src_sha1 = '396b81bdfd0fb617178e1707ef64832215307c78',
-  group = 'org.apache.commons',
-  artifact = 'commons-compress',
-  version = '1.6',
-)
-
-maven_jar(
-  name = 'tukaani-xz',
-  bin_sha1 = '66db21c8484120cb6a51b5b3ea47b6f383942bec',
-  src_sha1 = '6396220725701d767c553902c41120d7bf38e9f5',
-  group = 'org.tukaani',
-  artifact = 'xz',
-  version = '1.3',
-)
-
-maven_jar(
-  name = 'args4j',
-  bin_sha1 = '139441471327b9cc6d56436cb2a31e60eb6ed2ba',
-  src_sha1 = '22631b78cc8f60a6918557e8cbdb33e90f63a77f',
-  group = 'args4j',
-  artifact = 'args4j',
-  version = '2.0.15',
-)
-
-maven_jar(
-  name = 'junit',
-  bin_sha1 = '4e031bb61df09069aeb2bffb4019e7a5034a4ee0',
-  src_sha1 = '28e0ad201304e4a4abf999ca0570b7cffc352c3c',
-  group = 'junit',
-  artifact = 'junit',
-  version = '4.11',
-)
-
-maven_jar(
-  name = 'hamcrest-library',
-  bin_sha1 = '4785a3c21320980282f9f33d0d1264a69040538f',
-  src_sha1 = '047a7ee46628ab7133129cd7cef1e92657bc275e',
-  group = 'org.hamcrest',
-  artifact = 'hamcrest-library',
-  version = '1.3',
-)
-
-maven_jar(
-  name = 'hamcrest-core',
-  bin_sha1 = '42a25dc3219429f0e5d060061f71acb49bf010a0',
-  src_sha1 = '1dc37250fbc78e23a65a67fbbaf71d2e9cbc3c0b',
-  group = 'org.hamcrest',
-  artifact = 'hamcrest-core',
-  version = '1.3',
-)
-
-maven_jar(
-  name = 'gson',
-  bin_sha1 = 'a60a5e993c98c864010053cb901b7eab25306568',
-  src_sha1 = 'a6dc5db8a12928e583bd3f23e72d3ab611ecd58f',
-  group = 'com.google.code.gson',
-  artifact = 'gson',
-  version = '2.2.4',
-)
diff --git a/lib/BUILD b/lib/BUILD
new file mode 100644
index 0000000..827e6b6
--- /dev/null
+++ b/lib/BUILD
@@ -0,0 +1,156 @@
+java_library(
+    name = "args4j",
+    visibility = [
+        "//org.eclipse.jgit.pgm:__pkg__",
+        "//org.eclipse.jgit.pgm.test:__pkg__",
+    ],
+    exports = ["@args4j//jar"],
+)
+
+java_library(
+    name = "commons-compress",
+    visibility = [
+        "//org.eclipse.jgit.archive:__pkg__",
+        "//org.eclipse.jgit.pgm.test:__pkg__",
+    ],
+    exports = ["@commons_compress//jar"],
+)
+
+java_library(
+    name = "commons-codec",
+    exports = ["@commons_codec//jar"],
+)
+
+java_library(
+    name = "commons-logging",
+    testonly = 1,
+    visibility = ["//visibility:public"],
+    exports = ["@commons_logging//jar"],
+)
+
+java_library(
+    name = "gson",
+    visibility = ["//org.eclipse.jgit.lfs.server:__pkg__"],
+    exports = ["@gson//jar"],
+)
+
+java_library(
+    name = "httpclient",
+    visibility = [
+        "//org.eclipse.jgit.http.apache:__pkg__",
+        "//org.eclipse.jgit.lfs.server.test:__pkg__",
+        "//org.eclipse.jgit.pgm:__pkg__",
+    ],
+    exports = ["@httpclient//jar"],
+)
+
+java_library(
+    name = "httpcore",
+    visibility = [
+        "//org.eclipse.jgit.http.apache:__pkg__",
+        "//org.eclipse.jgit.lfs.server:__pkg__",
+        "//org.eclipse.jgit.lfs.server.test:__pkg__",
+        "//org.eclipse.jgit.pgm:__pkg__",
+    ],
+    exports = ["@httpcore//jar"],
+)
+
+java_library(
+    name = "javaewah",
+    visibility = ["//visibility:public"],
+    exports = ["@javaewah//jar"],
+)
+
+java_library(
+    name = "jetty-http",
+    # TODO: This should be testonly but org.eclipse.jgit.pgm depends on it.
+    visibility = ["//visibility:public"],
+    exports = ["@jetty_http//jar"],
+    runtime_deps = [":commons-codec"],
+)
+
+java_library(
+    name = "jetty-io",
+    # TODO: This should be testonly but org.eclipse.jgit.pgm depends on it.
+    visibility = ["//visibility:public"],
+    exports = ["@jetty_io//jar"],
+)
+
+java_library(
+    name = "jetty-security",
+    # TODO: This should be testonly but org.eclipse.jgit.pgm depends on it.
+    visibility = ["//visibility:public"],
+    exports = ["@jetty_security//jar"],
+)
+
+java_library(
+    name = "jetty-server",
+    # TODO: This should be testonly but org.eclipse.jgit.pgm depends on it.
+    visibility = ["//visibility:public"],
+    exports = ["@jetty_server//jar"],
+)
+
+java_library(
+    name = "jetty-servlet",
+    # TODO: This should be testonly but org.eclipse.jgit.pgm depends on it.
+    visibility = ["//visibility:public"],
+    exports = ["@jetty_servlet//jar"],
+)
+
+java_library(
+    name = "jetty-util",
+    # TODO: This should be testonly but org.eclipse.jgit.pgm depends on it.
+    visibility = ["//visibility:public"],
+    exports = ["@jetty_util//jar"],
+)
+
+java_library(
+    name = "jsch",
+    visibility = ["//org.eclipse.jgit:__pkg__"],
+    exports = ["@jsch//jar"],
+)
+
+java_library(
+    name = "junit",
+    testonly = 1,
+    visibility = ["//visibility:public"],
+    exports = [
+       "@junit//jar",
+       "@hamcrest_core//jar",
+       "@hamcrest_library//jar",
+    ],
+)
+
+java_library(
+    name = "servlet-api",
+    visibility = [
+        "//org.eclipse.jgit.http.apache:__pkg__",
+        "//org.eclipse.jgit.http.server:__pkg__",
+        "//org.eclipse.jgit.http.test:__pkg__",
+        "//org.eclipse.jgit.junit.http:__pkg__",
+        "//org.eclipse.jgit.lfs.server:__pkg__",
+        "//org.eclipse.jgit.lfs.server.test:__pkg__",
+        "//org.eclipse.jgit.pgm:__pkg__",
+    ],
+    exports = ["@servlet_api_3_1//jar"],
+)
+
+java_library(
+    name = "slf4j-api",
+    visibility = ["//visibility:public"],
+    exports = ["@log_api//jar"],
+)
+
+java_library(
+    name = "slf4j-simple",
+    testonly = 1,
+    visibility = ["//visibility:public"],
+    exports = ["@slf4j_simple//jar"],
+)
+
+java_library(
+    name = "xz",
+    testonly = 1,
+    visibility = ["//visibility:public"],
+    exports = ["@tukaani_xz//jar"],
+)
diff --git a/lib/jetty/BUCK b/lib/jetty/BUCK
deleted file mode 100644
index fbb37c1..0000000
--- a/lib/jetty/BUCK
+++ /dev/null
@@ -1,56 +0,0 @@
-VERSION = '9.3.17.v20170317'
-GROUP = 'org.eclipse.jetty'
-
-maven_jar(
-  name = 'servlet',
-  bin_sha1 = 'ed6986b0d0ca7b9b0f9015c9efb80442e3043a8e',
-  src_sha1 = 'ee6b4784a00a92e5c1b6111033b7ae41ac6052a3',
-  group = GROUP,
-  artifact = 'jetty-servlet',
-  version = VERSION,
-)
-
-maven_jar(
-  name = 'security',
-  bin_sha1 = 'ca52535569445682d42aaa97c7039442719a0507',
-  src_sha1 = '2ff9f4fb18b320fd5a0272a427bacc4d5fe7bc86',
-  group = GROUP,
-  artifact = 'jetty-security',
-  version = VERSION,
-)
-
-maven_jar(
-  name = 'server',
-  bin_sha1 = '194e9a02e6ba249ef4a3f4bd56b4993087992299',
-  src_sha1 = '0c9bd572f530c411592aefb71781ecca0b3719a9',
-  group = GROUP,
-  artifact = 'jetty-server',
-  version = VERSION,
-)
-
-maven_jar(
-  name = 'http',
-  bin_sha1 = '6c02d728e15d4868486254039c867a1ac3e4a52e',
-  src_sha1 = '3c0a2a82792f268631b4fefd77be9f126ec974b1',
-  group = GROUP,
-  artifact = 'jetty-http',
-  version = VERSION,
-)
-
-maven_jar(
-  name = 'io',
-  bin_sha1 = '756a8cd2a1cbfb84a94973b6332dd3eccd47c0cd',
-  src_sha1 = 'a9afa99cccb19b441364fa805d027f457cbbb136',
-  group = GROUP,
-  artifact = 'jetty-io',
-  version = VERSION,
-)
-
-maven_jar(
-  name = 'util',
-  bin_sha1 = 'b8512ab02819de01f0f5a5c6026163041f579beb',
-  src_sha1 = '96f8e3dcdc3660a5c91f19c46695daa70ac95625',
-  group = GROUP,
-  artifact = 'jetty-util',
-  version = VERSION,
-)
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
index 10c29d5..64f7498 100644
--- a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index 3bf69c3..0380568 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -3,14 +3,13 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.ant.test
-Bundle-Version: 4.6.2.qualifier
+Bundle-Version: 4.7.2.qualifier
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.junit;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)",
- org.hamcrest;version="[1.1.0,2.0.0)",
+ org.eclipse.jgit.ant.tasks;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.junit;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,4.8.0)",
+ org.hamcrest.core;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 9592841..79c81af 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.2-SNAPSHOT</version>
+    <version>4.7.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ant.test</artifactId>
diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
index 80cfbbb..4d260cf 100644
--- a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index 6b008e1..6ca7f1c 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.2.qualifier
+Bundle-Version: 4.7.2.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: org.apache.tools.ant,
-  org.eclipse.jgit.storage.file;version="[4.6.2,4.7.0)"
+  org.eclipse.jgit.storage.file;version="[4.7.2,4.8.0)"
 Bundle-Localization: plugin
 Bundle-Vendor: %Provider-Name
-Export-Package: org.eclipse.jgit.ant.tasks;version="4.6.2";
+Export-Package: org.eclipse.jgit.ant.tasks;version="4.7.2";
  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 194c9dd..5b888e9 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.2-SNAPSHOT</version>
+		<version>4.7.2-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>org.eclipse.jgit.ant</artifactId>
diff --git a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
index 4f1759f..06ddbab 100644
--- a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.archive/BUCK b/org.eclipse.jgit.archive/BUCK
deleted file mode 100644
index ae17032..0000000
--- a/org.eclipse.jgit.archive/BUCK
+++ /dev/null
@@ -1,13 +0,0 @@
-java_library(
-  name = 'jgit-archive',
-  srcs = glob(
-    ['src/**'],
-    excludes = ['src/org/eclipse/jgit/archive/FormatActivator.java'],
-  ),
-  resources = glob(['resources/**']),
-  provided_deps = [
-    '//org.eclipse.jgit:jgit',
-    '//lib:commons-compress',
-  ],
-  visibility = ['PUBLIC'],
-)
diff --git a/org.eclipse.jgit.archive/BUILD b/org.eclipse.jgit.archive/BUILD
new file mode 100644
index 0000000..dfdbfdc
--- /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 = [
+        "//lib:commons-compress",
+        # We want these deps to be provided_deps
+        "//org.eclipse.jgit:jgit",
+    ],
+)
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index b2fe6b2..bf54571 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.2.qualifier
+Bundle-Version: 4.7.2.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
@@ -12,14 +12,15 @@
  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.2,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)",
+ org.eclipse.jgit.api;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,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.2";
+Export-Package: org.eclipse.jgit.archive;version="4.7.2";
   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 7d7477c..8d955b6 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.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.6.2.qualifier";roots="."
+Bundle-Version: 4.7.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.7.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index 9c38817..0646c6d 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.2-SNAPSHOT</version>
+    <version>4.7.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.archive</artifactId>
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ArchiveFormats.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ArchiveFormats.java
index 1be126a..9d3decd 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ArchiveFormats.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ArchiveFormats.java
@@ -55,7 +55,7 @@
  * that performs the same registration automatically.
  */
 public class ArchiveFormats {
-	private static final List<String> myFormats = new ArrayList<String>();
+	private static final List<String> myFormats = new ArrayList<>();
 
 	private static final void register(String name, ArchiveCommand.Format<?> fmt) {
 		myFormats.add(name);
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/FormatActivator.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/FormatActivator.java
index a6a954f..aa4e4f5 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/FormatActivator.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/FormatActivator.java
@@ -64,6 +64,7 @@
 	 * @param context
 	 *            unused
 	 */
+	@Override
 	public void start(BundleContext context) {
 		ArchiveFormats.registerAll();
 	}
@@ -75,6 +76,7 @@
 	 * @param context
 	 *            unused
 	 */
+	@Override
 	public void stop(BundleContext context) {
 		ArchiveFormats.unregisterAll();
 	}
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java
index d56cb35..7b7fbcd 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java
@@ -57,7 +57,10 @@
 import org.eclipse.jgit.api.ArchiveCommand;
 import org.eclipse.jgit.archive.internal.ArchiveText;
 import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.revwalk.RevCommit;
+
 
 /**
  * Unix TAR format (ustar + some PAX extensions).
@@ -67,6 +70,7 @@
 	private static final List<String> SUFFIXES = Collections
 			.unmodifiableList(Arrays.asList(".tar")); //$NON-NLS-1$
 
+	@Override
 	public ArchiveOutputStream createArchiveOutputStream(OutputStream s)
 			throws IOException {
 		return createArchiveOutputStream(s,
@@ -76,6 +80,7 @@
 	/**
 	 * @since 4.0
 	 */
+	@Override
 	public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
 			Map<String, Object> o) throws IOException {
 		TarArchiveOutputStream out = new TarArchiveOutputStream(s, "UTF-8"); //$NON-NLS-1$
@@ -84,9 +89,21 @@
 		return applyFormatOptions(out, o);
 	}
 
+	@Deprecated
+	@Override
 	public void putEntry(ArchiveOutputStream out,
 			String path, FileMode mode, ObjectLoader loader)
 			throws IOException {
+		putEntry(out, null, path, mode,loader);
+	}
+
+	/**
+	 * @since 4.7
+	 */
+	@Override
+	public void putEntry(ArchiveOutputStream out,
+			ObjectId tree, String path, FileMode mode, ObjectLoader loader)
+			throws IOException {
 		if (mode == FileMode.SYMLINK) {
 			final TarArchiveEntry entry = new TarArchiveEntry(
 					path, TarConstants.LF_SYMLINK);
@@ -106,6 +123,12 @@
 			path = path + "/"; //$NON-NLS-1$
 
 		final TarArchiveEntry entry = new TarArchiveEntry(path);
+
+		if (tree instanceof RevCommit) {
+			long t = ((RevCommit) tree).getCommitTime() * 1000L;
+			entry.setModTime(t);
+		}
+
 		if (mode == FileMode.TREE) {
 			out.putArchiveEntry(entry);
 			out.closeArchiveEntry();
@@ -127,6 +150,7 @@
 		out.closeArchiveEntry();
 	}
 
+	@Override
 	public Iterable<String> suffixes() {
 		return SUFFIXES;
 	}
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java
index f3ab4da..5f194ec 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java
@@ -53,6 +53,7 @@
 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
 import org.eclipse.jgit.api.ArchiveCommand;
 import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 
 /**
@@ -65,6 +66,7 @@
 
 	private final ArchiveCommand.Format<ArchiveOutputStream> tarFormat = new TarFormat();
 
+	@Override
 	public ArchiveOutputStream createArchiveOutputStream(OutputStream s)
 			throws IOException {
 		return createArchiveOutputStream(s,
@@ -74,18 +76,32 @@
 	/**
 	 * @since 4.0
 	 */
+	@Override
 	public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
 			Map<String, Object> o) throws IOException {
 		BZip2CompressorOutputStream out = new BZip2CompressorOutputStream(s);
 		return tarFormat.createArchiveOutputStream(out, o);
 	}
 
+	@Deprecated
+	@Override
 	public void putEntry(ArchiveOutputStream out,
 			String path, FileMode mode, ObjectLoader loader)
 			throws IOException {
-		tarFormat.putEntry(out, path, mode, loader);
+		putEntry(out, null, path, mode,loader);
 	}
 
+	/**
+	 * @since 4.7
+	 */
+	@Override
+	public void putEntry(ArchiveOutputStream out,
+			ObjectId tree, String path, FileMode mode, ObjectLoader loader)
+			throws IOException {
+		tarFormat.putEntry(out, tree, path, mode, loader);
+	}
+
+	@Override
 	public Iterable<String> suffixes() {
 		return SUFFIXES;
 	}
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java
index 06f09a4..a6d053e 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java
@@ -53,6 +53,7 @@
 import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
 import org.eclipse.jgit.api.ArchiveCommand;
 import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 
 /**
@@ -65,6 +66,7 @@
 
 	private final ArchiveCommand.Format<ArchiveOutputStream> tarFormat = new TarFormat();
 
+	@Override
 	public ArchiveOutputStream createArchiveOutputStream(OutputStream s)
 			throws IOException {
 		return createArchiveOutputStream(s,
@@ -74,18 +76,32 @@
 	/**
 	 * @since 4.0
 	 */
+	@Override
 	public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
 			Map<String, Object> o) throws IOException {
 		GzipCompressorOutputStream out = new GzipCompressorOutputStream(s);
 		return tarFormat.createArchiveOutputStream(out, o);
 	}
 
+	@Deprecated
+	@Override
 	public void putEntry(ArchiveOutputStream out,
 			String path, FileMode mode, ObjectLoader loader)
 			throws IOException {
-		tarFormat.putEntry(out, path, mode, loader);
+		putEntry(out, null, path, mode,loader);
 	}
 
+	/**
+	 * @since 4.7
+	 */
+	@Override
+	public void putEntry(ArchiveOutputStream out,
+			ObjectId tree, String path, FileMode mode, ObjectLoader loader)
+			throws IOException {
+		tarFormat.putEntry(out, tree, path, mode, loader);
+	}
+
+	@Override
 	public Iterable<String> suffixes() {
 		return SUFFIXES;
 	}
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java
index 1483935..b6742ac 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java
@@ -53,6 +53,7 @@
 import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
 import org.eclipse.jgit.api.ArchiveCommand;
 import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 
 /**
@@ -65,6 +66,7 @@
 
 	private final ArchiveCommand.Format<ArchiveOutputStream> tarFormat = new TarFormat();
 
+	@Override
 	public ArchiveOutputStream createArchiveOutputStream(OutputStream s)
 			throws IOException {
 		return createArchiveOutputStream(s,
@@ -74,18 +76,32 @@
 	/**
 	 * @since 4.0
 	 */
+	@Override
 	public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
 			Map<String, Object> o) throws IOException {
 		XZCompressorOutputStream out = new XZCompressorOutputStream(s);
 		return tarFormat.createArchiveOutputStream(out, o);
 	}
 
+	@Deprecated
+	@Override
 	public void putEntry(ArchiveOutputStream out,
 			String path, FileMode mode, ObjectLoader loader)
 			throws IOException {
-		tarFormat.putEntry(out, path, mode, loader);
+		putEntry(out, null, path, mode,loader);
 	}
 
+	/**
+	 * @since 4.7
+	 */
+	@Override
+	public void putEntry(ArchiveOutputStream out,
+			ObjectId tree, String path, FileMode mode, ObjectLoader loader)
+			throws IOException {
+		tarFormat.putEntry(out, tree, path, mode, loader);
+	}
+
+	@Override
 	public Iterable<String> suffixes() {
 		return SUFFIXES;
 	}
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ZipFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ZipFormat.java
index 0e1b253..46d918e 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ZipFormat.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ZipFormat.java
@@ -56,7 +56,9 @@
 import org.eclipse.jgit.api.ArchiveCommand;
 import org.eclipse.jgit.archive.internal.ArchiveText;
 import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.revwalk.RevCommit;
 
 /**
  * PKWARE's ZIP format.
@@ -66,6 +68,7 @@
 	private static final List<String> SUFFIXES = Collections
 			.unmodifiableList(Arrays.asList(".zip")); //$NON-NLS-1$
 
+	@Override
 	public ArchiveOutputStream createArchiveOutputStream(OutputStream s)
 			throws IOException {
 		return createArchiveOutputStream(s,
@@ -75,14 +78,27 @@
 	/**
 	 * @since 4.0
 	 */
+	@Override
 	public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
 			Map<String, Object> o) throws IOException {
 		return applyFormatOptions(new ZipArchiveOutputStream(s), o);
 	}
 
+	@Deprecated
+	@Override
 	public void putEntry(ArchiveOutputStream out,
 			String path, FileMode mode, ObjectLoader loader)
 			throws IOException {
+		putEntry(out, null, path, mode,loader);
+	}
+
+	/**
+	 * @since 4.7
+	 */
+	@Override
+	public void putEntry(ArchiveOutputStream out,
+			ObjectId tree, String path, FileMode mode, ObjectLoader loader)
+			throws IOException {
 		// ZipArchiveEntry detects directories by checking
 		// for '/' at the end of the filename.
 		if (path.endsWith("/") && mode != FileMode.TREE) //$NON-NLS-1$
@@ -92,6 +108,12 @@
 			path = path + "/"; //$NON-NLS-1$
 
 		final ZipArchiveEntry entry = new ZipArchiveEntry(path);
+
+		if (tree instanceof RevCommit) {
+			long t = ((RevCommit) tree).getCommitTime() * 1000L;
+			entry.setTime(t);
+		}
+
 		if (mode == FileMode.TREE) {
 			out.putArchiveEntry(entry);
 			out.closeArchiveEntry();
@@ -114,6 +136,7 @@
 		out.closeArchiveEntry();
 	}
 
+	@Override
 	public Iterable<String> suffixes() {
 		return SUFFIXES;
 	}
diff --git a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
index 80cfbbb..4d260cf 100644
--- a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.http.apache/BUCK b/org.eclipse.jgit.http.apache/BUCK
deleted file mode 100644
index 3b7a823..0000000
--- a/org.eclipse.jgit.http.apache/BUCK
+++ /dev/null
@@ -1,11 +0,0 @@
-java_library(
-  name = 'http-apache',
-  srcs = glob(['src/**']),
-  resources = glob(['resources/**']),
-  deps = [
-    '//org.eclipse.jgit:jgit',
-    '//lib:httpcomponents',
-    '//lib:httpcore',
-  ],
-  visibility = ['PUBLIC'],
-)
diff --git a/org.eclipse.jgit.http.apache/BUILD b/org.eclipse.jgit.http.apache/BUILD
new file mode 100644
index 0000000..c1538ab
--- /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 = [
+        "//lib:httpclient",
+        "//lib:httpcore",
+        "//org.eclipse.jgit:jgit",
+    ],
+)
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index 978be27..67ed714 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.2.qualifier
+Bundle-Version: 4.7.2.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-Localization: plugin
 Bundle-Vendor: %Provider-Name
@@ -20,13 +20,12 @@
  org.apache.http.conn.ssl;version="[4.3.0,5.0.0)",
  org.apache.http.entity;version="[4.3.0,5.0.0)",
  org.apache.http.impl.client;version="[4.3.0,5.0.0)",
- 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.2,4.7.0)",
- org.eclipse.jgit.transport.http;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="4.6.2";
+ org.eclipse.jgit.nls;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport.http;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,4.8.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="4.7.2";
   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 dfd8585..66086f4 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.2-SNAPSHOT</version>
+		<version>4.7.2-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>org.eclipse.jgit.http.apache</artifactId>
diff --git a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
index 281154f..945ecd5 100644
--- a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
+++ b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
@@ -223,15 +223,18 @@
 		this.proxy = proxy;
 	}
 
+	@Override
 	public int getResponseCode() throws IOException {
 		execute();
 		return resp.getStatusLine().getStatusCode();
 	}
 
+	@Override
 	public URL getURL() {
 		return url;
 	}
 
+	@Override
 	public String getResponseMessage() throws IOException {
 		execute();
 		return resp.getStatusLine().getReasonPhrase();
@@ -259,10 +262,11 @@
 		}
 	}
 
+	@Override
 	public Map<String, List<String>> getHeaderFields() {
-		Map<String, List<String>> ret = new HashMap<String, List<String>>();
+		Map<String, List<String>> ret = new HashMap<>();
 		for (Header hdr : resp.getAllHeaders()) {
-			List<String> list = new LinkedList<String>();
+			List<String> list = new LinkedList<>();
 			for (HeaderElement hdrElem : hdr.getElements())
 				list.add(hdrElem.toString());
 			ret.put(hdr.getName(), list);
@@ -270,10 +274,12 @@
 		return ret;
 	}
 
+	@Override
 	public void setRequestProperty(String name, String value) {
 		req.addHeader(name, value);
 	}
 
+	@Override
 	public void setRequestMethod(String method) throws ProtocolException {
 		this.method = method;
 		if (METHOD_GET.equalsIgnoreCase(method)) {
@@ -290,18 +296,22 @@
 		}
 	}
 
+	@Override
 	public void setUseCaches(boolean usecaches) {
 		// not needed
 	}
 
+	@Override
 	public void setConnectTimeout(int timeout) {
 		this.timeout = Integer.valueOf(timeout);
 	}
 
+	@Override
 	public void setReadTimeout(int readTimeout) {
 		this.readTimeout = Integer.valueOf(readTimeout);
 	}
 
+	@Override
 	public String getContentType() {
 		HttpEntity responseEntity = resp.getEntity();
 		if (responseEntity != null) {
@@ -312,16 +322,19 @@
 		return null;
 	}
 
+	@Override
 	public InputStream getInputStream() throws IOException {
 		return resp.getEntity().getContent();
 	}
 
 	// will return only the first field
+	@Override
 	public String getHeaderField(String name) {
 		Header header = resp.getFirstHeader(name);
 		return (header == null) ? null : header.getValue();
 	}
 
+	@Override
 	public int getContentLength() {
 		Header contentLength = resp.getFirstHeader("content-length"); //$NON-NLS-1$
 		if (contentLength == null) {
@@ -336,14 +349,17 @@
 		}
 	}
 
+	@Override
 	public void setInstanceFollowRedirects(boolean followRedirects) {
 		this.followRedirects = Boolean.valueOf(followRedirects);
 	}
 
+	@Override
 	public void setDoOutput(boolean dooutput) {
 		// TODO: check whether we can really ignore this.
 	}
 
+	@Override
 	public void setFixedLengthStreamingMode(int contentLength) {
 		if (entity != null)
 			throw new IllegalArgumentException();
@@ -351,52 +367,63 @@
 		entity.setContentLength(contentLength);
 	}
 
+	@Override
 	public OutputStream getOutputStream() throws IOException {
 		if (entity == null)
 			entity = new TemporaryBufferEntity(new LocalFile(null));
 		return entity.getBuffer();
 	}
 
+	@Override
 	public void setChunkedStreamingMode(int chunklen) {
 		if (entity == null)
 			entity = new TemporaryBufferEntity(new LocalFile(null));
 		entity.setChunked(true);
 	}
 
+	@Override
 	public String getRequestMethod() {
 		return method;
 	}
 
+	@Override
 	public boolean usingProxy() {
 		return isUsingProxy;
 	}
 
+	@Override
 	public void connect() throws IOException {
 		execute();
 	}
 
+	@Override
 	public void setHostnameVerifier(final HostnameVerifier hostnameverifier) {
 		this.hostnameverifier = new X509HostnameVerifier() {
+			@Override
 			public boolean verify(String hostname, SSLSession session) {
 				return hostnameverifier.verify(hostname, session);
 			}
 
+			@Override
 			public void verify(String host, String[] cns, String[] subjectAlts)
 					throws SSLException {
 				throw new UnsupportedOperationException(); // TODO message
 			}
 
+			@Override
 			public void verify(String host, X509Certificate cert)
 					throws SSLException {
 				throw new UnsupportedOperationException(); // TODO message
 			}
 
+			@Override
 			public void verify(String host, SSLSocket ssl) throws IOException {
 				hostnameverifier.verify(host, ssl.getSession());
 			}
 		};
 	}
 
+	@Override
 	public void configure(KeyManager[] km, TrustManager[] tm,
 			SecureRandom random) throws KeyManagementException {
 		getSSLContext().init(km, tm, random);
diff --git a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnectionFactory.java b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnectionFactory.java
index fe1eef4..f97d284 100644
--- a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnectionFactory.java
+++ b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnectionFactory.java
@@ -55,10 +55,12 @@
  * @since 3.3
  */
 public class HttpClientConnectionFactory implements HttpConnectionFactory {
+	@Override
 	public HttpConnection create(URL url) throws IOException {
 		return new HttpClientConnection(url.toString());
 	}
 
+	@Override
 	public HttpConnection create(URL url, Proxy proxy)
 			throws IOException {
 		return new HttpClientConnection(url.toString(), proxy);
diff --git a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/TemporaryBufferEntity.java b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/TemporaryBufferEntity.java
index 93328c9..3efff49 100644
--- a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/TemporaryBufferEntity.java
+++ b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/TemporaryBufferEntity.java
@@ -78,25 +78,30 @@
 		return buffer;
 	}
 
+	@Override
 	public boolean isRepeatable() {
 		return true;
 	}
 
+	@Override
 	public long getContentLength() {
 		if (contentLength != null)
 			return contentLength.intValue();
 		return buffer.length();
 	}
 
+	@Override
 	public InputStream getContent() throws IOException, IllegalStateException {
 		return buffer.openInputStream();
 	}
 
+	@Override
 	public void writeTo(OutputStream outstream) throws IOException {
 		// TODO: dont we need a progressmonitor
 		buffer.writeTo(outstream, null);
 	}
 
+	@Override
 	public boolean isStreaming() {
 		return false;
 	}
diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
index 80cfbbb..4d260cf 100644
--- a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.http.server/BUCK b/org.eclipse.jgit.http.server/BUCK
deleted file mode 100644
index 3743557..0000000
--- a/org.eclipse.jgit.http.server/BUCK
+++ /dev/null
@@ -1,10 +0,0 @@
-java_library(
-  name = 'jgit-servlet',
-  srcs = glob(['src/**']),
-  resources = glob(['resources/**']),
-  provided_deps = [
-    '//org.eclipse.jgit:jgit',
-    '//lib:servlet-api',
-  ],
-  visibility = ['PUBLIC'],
-)
diff --git a/org.eclipse.jgit.http.server/BUILD b/org.eclipse.jgit.http.server/BUILD
new file mode 100644
index 0000000..876c5fa
--- /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 = [
+        "//lib:servlet-api",
+        # We want these deps to be provided_deps
+        "//org.eclipse.jgit:jgit",
+    ],
+)
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index be3bb33..5f0d888 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.2.qualifier
+Bundle-Version: 4.7.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.http.server;version="4.6.2",
- org.eclipse.jgit.http.server.glue;version="4.6.2";
+Export-Package: org.eclipse.jgit.http.server;version="4.7.2",
+ org.eclipse.jgit.http.server.glue;version="4.7.2";
   uses:="javax.servlet,javax.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="4.6.2";
+ org.eclipse.jgit.http.server.resolver;version="4.7.2";
   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.2,4.7.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport.resolver;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)"
+ org.eclipse.jgit.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,4.8.0)"
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index 843ec4b..669e457 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.2-SNAPSHOT</version>
+    <version>4.7.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.server</artifactId>
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/AsIsFileFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/AsIsFileFilter.java
index d33362b..05391eb 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/AsIsFileFilter.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/AsIsFileFilter.java
@@ -70,14 +70,17 @@
 		this.asIs = getAnyFile;
 	}
 
+	@Override
 	public void init(FilterConfig config) throws ServletException {
 		// Do nothing.
 	}
 
+	@Override
 	public void destroy() {
 		// Do nothing.
 	}
 
+	@Override
 	public void doFilter(ServletRequest request, ServletResponse response,
 			FilterChain chain) throws IOException, ServletException {
 		HttpServletRequest req = (HttpServletRequest) request;
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
index 529b839..8070371 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitFilter.java
@@ -95,9 +95,9 @@
 
 	private ReceivePackFactory<HttpServletRequest> receivePackFactory = new DefaultReceivePackFactory();
 
-	private final List<Filter> uploadPackFilters = new LinkedList<Filter>();
+	private final List<Filter> uploadPackFilters = new LinkedList<>();
 
-	private final List<Filter> receivePackFilters = new LinkedList<Filter>();
+	private final List<Filter> receivePackFilters = new LinkedList<>();
 
 	/**
 	 * New servlet that will load its base directory from {@code web.xml}.
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java
index bca5210..3a5edee 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitServlet.java
@@ -174,18 +174,22 @@
 	@Override
 	public void init(final ServletConfig config) throws ServletException {
 		gitFilter.init(new FilterConfig() {
+			@Override
 			public String getFilterName() {
 				return gitFilter.getClass().getName();
 			}
 
+			@Override
 			public String getInitParameter(String name) {
 				return config.getInitParameter(name);
 			}
 
+			@Override
 			public Enumeration<String> getInitParameterNames() {
 				return config.getInitParameterNames();
 			}
 
+			@Override
 			public ServletContext getServletContext() {
 				return config.getServletContext();
 			}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
index bb6efb8..91c2f9f 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoPacksServlet.java
@@ -60,6 +60,7 @@
 class InfoPacksServlet extends HttpServlet {
 	private static final long serialVersionUID = 1L;
 
+	@Override
 	public void doGet(final HttpServletRequest req,
 			final HttpServletResponse rsp) throws IOException {
 		sendPlainText(packList(req), req, rsp);
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
index 446f6a2..72c7136 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
@@ -64,6 +64,7 @@
 class InfoRefsServlet extends HttpServlet {
 	private static final long serialVersionUID = 1L;
 
+	@Override
 	public void doGet(final HttpServletRequest req,
 			final HttpServletResponse rsp) throws IOException {
 		// Assume a dumb client and send back the dumb client
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java
index 511cdf1..223813f 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/IsLocalFilter.java
@@ -66,14 +66,17 @@
  * downstream servlet can directly access its contents on disk.
  */
 class IsLocalFilter implements Filter {
+	@Override
 	public void init(FilterConfig config) throws ServletException {
 		// Do nothing.
 	}
 
+	@Override
 	public void destroy() {
 		// Do nothing.
 	}
 
+	@Override
 	public void doFilter(ServletRequest request, ServletResponse response,
 			FilterChain chain) throws IOException, ServletException {
 		if (isLocal(getRepository(request)))
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/NoCacheFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/NoCacheFilter.java
index 6a23cb9..bdc3420 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/NoCacheFilter.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/NoCacheFilter.java
@@ -59,14 +59,17 @@
 
 /** Adds HTTP response headers to prevent caching by proxies/browsers. */
 class NoCacheFilter implements Filter {
+	@Override
 	public void init(FilterConfig config) throws ServletException {
 		// Do nothing.
 	}
 
+	@Override
 	public void destroy() {
 		// Do nothing.
 	}
 
+	@Override
 	public void doFilter(ServletRequest request, ServletResponse response,
 			FilterChain chain) throws IOException, ServletException {
 		HttpServletResponse rsp = (HttpServletResponse) response;
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
index c88670e..9d24bf7 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
@@ -130,6 +130,7 @@
 			this.receivePackFactory = receivePackFactory;
 		}
 
+		@Override
 		public void doFilter(ServletRequest request, ServletResponse response,
 				FilterChain chain) throws IOException, ServletException {
 			HttpServletRequest req = (HttpServletRequest) request;
@@ -153,10 +154,12 @@
 			}
 		}
 
+		@Override
 		public void init(FilterConfig filterConfig) throws ServletException {
 			// Nothing.
 		}
 
+		@Override
 		public void destroy() {
 			// Nothing.
 		}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java
index b3fad3d..de09c54 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java
@@ -100,14 +100,17 @@
 		this.resolver = resolver;
 	}
 
+	@Override
 	public void init(final FilterConfig config) throws ServletException {
 		context = config.getServletContext();
 	}
 
+	@Override
 	public void destroy() {
 		context = null;
 	}
 
+	@Override
 	public void doFilter(final ServletRequest request,
 			final ServletResponse response, final FilterChain chain)
 			throws IOException, ServletException {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java
index 145c63b..08a5eba 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java
@@ -95,6 +95,7 @@
 		return out;
 	}
 
+	@Override
 	public void close() throws IOException {
 		super.close();
 
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java
index a06bb1e..fe34f66 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java
@@ -80,14 +80,17 @@
 		this.filters = filters.toArray(new Filter[filters.size()]);
 	}
 
+	@Override
 	public void init(FilterConfig config) throws ServletException {
 		// Do nothing.
 	}
 
+	@Override
 	public void destroy() {
 		// Do nothing.
 	}
 
+	@Override
 	public void doFilter(ServletRequest request, ServletResponse response,
 			FilterChain chain) throws IOException, ServletException {
 		final HttpServletRequest req = (HttpServletRequest) request;
@@ -154,6 +157,7 @@
 	private class Chain implements FilterChain {
 		private int filterIdx;
 
+		@Override
 		public void doFilter(ServletRequest req, ServletResponse rsp)
 				throws IOException, ServletException {
 			if (filterIdx < filters.length)
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java
index 650059b..28ee17d 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/TextFileServlet.java
@@ -68,6 +68,7 @@
 		this.fileName = name;
 	}
 
+	@Override
 	public void doGet(final HttpServletRequest req,
 			final HttpServletResponse rsp) throws IOException {
 		try {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
index a9a0c5b..97d00c1 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
@@ -129,6 +129,7 @@
 			this.uploadPackFactory = uploadPackFactory;
 		}
 
+		@Override
 		public void doFilter(ServletRequest request, ServletResponse response,
 				FilterChain chain) throws IOException, ServletException {
 			HttpServletRequest req = (HttpServletRequest) request;
@@ -152,10 +153,12 @@
 			}
 		}
 
+		@Override
 		public void init(FilterConfig filterConfig) throws ServletException {
 			// Nothing.
 		}
 
+		@Override
 		public void destroy() {
 			// Nothing.
 		}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaFilter.java
index 2187cfa..adb6c42 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaFilter.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaFilter.java
@@ -89,7 +89,7 @@
 
 	/** Empty filter with no bindings. */
 	public MetaFilter() {
-		this.bindings = new ArrayList<ServletBinderImpl>();
+		this.bindings = new ArrayList<>();
 	}
 
 	/**
@@ -128,10 +128,12 @@
 		return register(new RegexPipeline.Binder(pattern));
 	}
 
+	@Override
 	public void init(FilterConfig filterConfig) throws ServletException {
 		servletContext = filterConfig.getServletContext();
 	}
 
+	@Override
 	public void destroy() {
 		if (pipelines != null) {
 			Set<Object> destroyed = newIdentitySet();
@@ -142,7 +144,7 @@
 	}
 
 	private static Set<Object> newIdentitySet() {
-		final Map<Object, Object> m = new IdentityHashMap<Object, Object>();
+		final Map<Object, Object> m = new IdentityHashMap<>();
 		return new AbstractSet<Object>() {
 			@Override
 			public boolean add(Object o) {
@@ -166,6 +168,7 @@
 		};
 	}
 
+	@Override
 	public void doFilter(ServletRequest request, ServletResponse response,
 			FilterChain chain) throws IOException, ServletException {
 		HttpServletRequest req = (HttpServletRequest) request;
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaServlet.java
index 0506065..71365c8 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/MetaServlet.java
@@ -123,6 +123,7 @@
 		filter.init(new NoParameterFilterConfig(name, ctx));
 	}
 
+	@Override
 	public void destroy() {
 		filter.destroy();
 	}
@@ -131,6 +132,7 @@
 	protected void service(HttpServletRequest req, HttpServletResponse res)
 			throws ServletException, IOException {
 		filter.doFilter(req, res, new FilterChain() {
+			@Override
 			public void doFilter(ServletRequest request,
 					ServletResponse response) throws IOException,
 					ServletException {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/NoParameterFilterConfig.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/NoParameterFilterConfig.java
index 8dfcc4d..961f88e 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/NoParameterFilterConfig.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/NoParameterFilterConfig.java
@@ -59,26 +59,32 @@
 		this.context = context;
 	}
 
+	@Override
 	public String getInitParameter(String name) {
 		return null;
 	}
 
+	@Override
 	public Enumeration<String> getInitParameterNames() {
 		return new Enumeration<String>() {
+			@Override
 			public boolean hasMoreElements() {
 				return false;
 			}
 
+			@Override
 			public String nextElement() {
 				throw new NoSuchElementException();
 			}
 		};
 	}
 
+	@Override
 	public ServletContext getServletContext() {
 		return context;
 	}
 
+	@Override
 	public String getFilterName() {
 		return filterName;
 	}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexGroupFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexGroupFilter.java
index 2414660..a402977 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexGroupFilter.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexGroupFilter.java
@@ -79,14 +79,17 @@
 		this.groupIdx = groupIdx - 1;
 	}
 
+	@Override
 	public void init(FilterConfig config) throws ServletException {
 		// Do nothing.
 	}
 
+	@Override
 	public void destroy() {
 		// Do nothing.
 	}
 
+	@Override
 	public void doFilter(final ServletRequest request,
 			final ServletResponse rsp, final FilterChain chain)
 			throws IOException, ServletException {
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexPipeline.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexPipeline.java
index 2ef7136..f33243b 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexPipeline.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/RegexPipeline.java
@@ -95,6 +95,7 @@
 			pattern = p;
 		}
 
+		@Override
 		UrlPipeline create() {
 			return new RegexPipeline(pattern, getFilters(), getServlet());
 		}
@@ -108,6 +109,7 @@
 		this.pattern = pattern;
 	}
 
+	@Override
 	boolean match(final HttpServletRequest req) {
 		final String pathInfo = req.getPathInfo();
 		return pathInfo != null && pattern.matcher(pathInfo).matches();
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinderImpl.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinderImpl.java
index e96fc59..4e879a9 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinderImpl.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/ServletBinderImpl.java
@@ -58,9 +58,10 @@
 	private HttpServlet httpServlet;
 
 	ServletBinderImpl() {
-		this.filters = new ArrayList<Filter>();
+		this.filters = new ArrayList<>();
 	}
 
+	@Override
 	public ServletBinder through(Filter filter) {
 		if (filter == null)
 			throw new NullPointerException(HttpServerText.get().filterMustNotBeNull);
@@ -68,6 +69,7 @@
 		return this;
 	}
 
+	@Override
 	public void with(HttpServlet servlet) {
 		if (servlet == null)
 			throw new NullPointerException(HttpServerText.get().servletMustNotBeNull);
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/SuffixPipeline.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/SuffixPipeline.java
index b942016..903de63 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/SuffixPipeline.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/SuffixPipeline.java
@@ -71,6 +71,7 @@
 			this.suffix = suffix;
 		}
 
+		@Override
 		UrlPipeline create() {
 			return new SuffixPipeline(suffix, getFilters(), getServlet());
 		}
@@ -87,6 +88,7 @@
 		this.suffixLen = suffix.length();
 	}
 
+	@Override
 	boolean match(final HttpServletRequest req) {
 		final String pathInfo = req.getPathInfo();
 		return pathInfo != null && pathInfo.endsWith(suffix);
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/UrlPipeline.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/UrlPipeline.java
index 2d0b844..56e4e22 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/UrlPipeline.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/glue/UrlPipeline.java
@@ -121,26 +121,32 @@
 			throws ServletException {
 		if (!inited.contains(ref)) {
 			ref.init(new ServletConfig() {
+				@Override
 				public String getInitParameter(String name) {
 					return null;
 				}
 
+				@Override
 				public Enumeration<String> getInitParameterNames() {
 					return new Enumeration<String>() {
+						@Override
 						public boolean hasMoreElements() {
 							return false;
 						}
 
+						@Override
 						public String nextElement() {
 							throw new NoSuchElementException();
 						}
 					};
 				}
 
+				@Override
 				public ServletContext getServletContext() {
 					return context;
 				}
 
+				@Override
 				public String getServletName() {
 					return ref.getClass().getName();
 				}
@@ -229,6 +235,7 @@
 			this.servlet = servlet;
 		}
 
+		@Override
 		public void doFilter(ServletRequest req, ServletResponse rsp)
 				throws IOException, ServletException {
 			if (filterIdx < filters.length)
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/AsIsFileService.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/AsIsFileService.java
index 4ef2a7c..88ad472 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/AsIsFileService.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/AsIsFileService.java
@@ -72,6 +72,7 @@
 	};
 
 	private static final SectionParser<ServiceConfig> CONFIG = new SectionParser<ServiceConfig>() {
+		@Override
 		public ServiceConfig parse(final Config cfg) {
 			return new ServiceConfig(cfg);
 		}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultReceivePackFactory.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultReceivePackFactory.java
index 8c39b79..04e192b 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultReceivePackFactory.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultReceivePackFactory.java
@@ -69,6 +69,7 @@
 public class DefaultReceivePackFactory implements
 		ReceivePackFactory<HttpServletRequest> {
 	private static final SectionParser<ServiceConfig> CONFIG = new SectionParser<ServiceConfig>() {
+		@Override
 		public ServiceConfig parse(final Config cfg) {
 			return new ServiceConfig(cfg);
 		}
@@ -85,6 +86,7 @@
 		}
 	}
 
+	@Override
 	public ReceivePack create(final HttpServletRequest req, final Repository db)
 			throws ServiceNotEnabledException, ServiceNotAuthorizedException {
 		final ServiceConfig cfg = db.getConfig().get(CONFIG);
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultUploadPackFactory.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultUploadPackFactory.java
index 34c069e..d01e2ef 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultUploadPackFactory.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/resolver/DefaultUploadPackFactory.java
@@ -62,6 +62,7 @@
 public class DefaultUploadPackFactory implements
 		UploadPackFactory<HttpServletRequest> {
 	private static final SectionParser<ServiceConfig> CONFIG = new SectionParser<ServiceConfig>() {
+		@Override
 		public ServiceConfig parse(final Config cfg) {
 			return new ServiceConfig(cfg);
 		}
@@ -75,6 +76,7 @@
 		}
 	}
 
+	@Override
 	public UploadPack create(final HttpServletRequest req, final Repository db)
 			throws ServiceNotEnabledException, ServiceNotAuthorizedException {
 		if (db.getConfig().get(CONFIG).enabled)
diff --git a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
index 10c29d5..64f7498 100644
--- a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.http.test/BUCK b/org.eclipse.jgit.http.test/BUCK
deleted file mode 100644
index f5cd10e..0000000
--- a/org.eclipse.jgit.http.test/BUCK
+++ /dev/null
@@ -1,41 +0,0 @@
-TESTS = glob(['tst/**/*.java'])
-
-for t in TESTS:
-  n = t[len('tst/'):len(t)-len('.java')].replace('/', '.')
-  java_test(
-    name = n,
-    labels = ['http'],
-    srcs = [t],
-    deps = [
-      ':helpers',
-      '//org.eclipse.jgit:jgit',
-      '//org.eclipse.jgit.http.apache:http-apache',
-      '//org.eclipse.jgit.http.server:jgit-servlet',
-      '//org.eclipse.jgit.junit:junit',
-      '//org.eclipse.jgit.junit.http:junit-http',
-      '//lib:hamcrest-core',
-      '//lib:hamcrest-library',
-      '//lib:junit',
-      '//lib:servlet-api',
-      '//lib/jetty:http',
-      '//lib/jetty:io',
-      '//lib/jetty:server',
-      '//lib/jetty:servlet',
-      '//lib/jetty:security',
-      '//lib/jetty:util',
-      '//lib:commons-logging',
-    ],
-    vm_args = ['-Djava.io.tmpdir=buck-out'],
-  )
-
-java_library(
-  name = 'helpers',
-  srcs = glob(['src/**/*.java']),
-  deps = [
-    '//org.eclipse.jgit:jgit',
-    '//org.eclipse.jgit.http.server:jgit-servlet',
-    '//org.eclipse.jgit.junit:junit',
-    '//org.eclipse.jgit.junit.http:junit-http',
-    '//lib:junit',
-  ],
-)
diff --git a/org.eclipse.jgit.http.test/BUILD b/org.eclipse.jgit.http.test/BUILD
new file mode 100644
index 0000000..ce2d611
--- /dev/null
+++ b/org.eclipse.jgit.http.test/BUILD
@@ -0,0 +1,42 @@
+load(
+    "@com_googlesource_gerrit_bazlets//tools:junit.bzl",
+    "junit_tests",
+)
+
+junit_tests(
+    name = "http",
+    srcs = glob(["tst/**/*.java"]),
+    tags = ["http"],
+    deps = [
+        ":helpers",
+        "//lib:commons-logging",
+        "//lib:jetty-http",
+        "//lib:jetty-io",
+        "//lib:jetty-security",
+        "//lib:jetty-server",
+        "//lib:jetty-servlet",
+        "//lib:jetty-util",
+        "//lib:junit",
+        "//lib:servlet-api",
+        "//lib:slf4j-api",
+        "//lib:slf4j-simple",
+        "//org.eclipse.jgit.http.apache:http-apache",
+        "//org.eclipse.jgit.http.server:jgit-servlet",
+        "//org.eclipse.jgit:jgit",
+        "//org.eclipse.jgit.junit.http:junit-http",
+        "//org.eclipse.jgit.junit:junit",
+    ],
+)
+
+java_library(
+    name = "helpers",
+    testonly = 1,
+    srcs = glob(["src/**/*.java"]),
+    deps = [
+        "//lib:junit",
+        "//org.eclipse.jgit.http.server:jgit-servlet",
+        "//org.eclipse.jgit:jgit",
+        "//org.eclipse.jgit.junit.http:junit-http",
+        "//org.eclipse.jgit.junit:junit",
+    ],
+)
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index 6effed6..5817ce3 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.2.qualifier
+Bundle-Version: 4.7.2.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,9.4.0)",
  org.eclipse.jetty.util.security;version="[9.0.0,9.4.0)",
  org.eclipse.jetty.util.thread;version="[9.0.0,9.4.0)",
- org.eclipse.jgit.errors;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.http.server;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.http.server.glue;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.http.server.resolver;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.junit;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.junit.http;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport.http;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport.resolver;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)",
+ org.eclipse.jgit.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.http.server;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.http.server.glue;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.http.server.resolver;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.junit;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.junit.http;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.storage.file;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport.http;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,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 0366f75..eee0092 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.2-SNAPSHOT</version>
+    <version>4.7.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.http.test</artifactId>
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
index 0285bd1..0e92b14 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AdvertiseErrorTest.java
@@ -80,6 +80,7 @@
 
 	private URIish remoteURI;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -90,6 +91,7 @@
 		ServletContextHandler app = server.addContext("/git");
 		GitServlet gs = new GitServlet();
 		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
+			@Override
 			public Repository open(HttpServletRequest req, String name)
 					throws RepositoryNotFoundException,
 					ServiceNotEnabledException {
@@ -102,6 +104,7 @@
 			}
 		});
 		gs.setReceivePackFactory(new DefaultReceivePackFactory() {
+			@Override
 			public ReceivePack create(HttpServletRequest req, Repository db)
 					throws ServiceNotEnabledException,
 					ServiceNotAuthorizedException {
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AsIsServiceTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AsIsServiceTest.java
index c6b8f09..e94a792 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AsIsServiceTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AsIsServiceTest.java
@@ -64,6 +64,7 @@
 
 	private AsIsFileService service;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultReceivePackFactoryTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultReceivePackFactoryTest.java
index f2879e0..b24e2df 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultReceivePackFactoryTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultReceivePackFactoryTest.java
@@ -71,6 +71,7 @@
 
 	private ReceivePackFactory<HttpServletRequest> factory;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultUploadPackFactoryTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultUploadPackFactoryTest.java
index 3bcb057..ce24d64 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultUploadPackFactoryTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultUploadPackFactoryTest.java
@@ -69,6 +69,7 @@
 
 	private UploadPackFactory<HttpServletRequest> factory;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
index 677132d..ab6dc35 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
@@ -109,6 +109,7 @@
 		HttpTransport.setConnectionFactory(cf);
 	}
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -212,7 +213,7 @@
 
 	@Test
 	public void testInitialClone_Packed() throws Exception {
-		new TestRepository<Repository>(remoteRepository).packAndPrune();
+		new TestRepository<>(remoteRepository).packAndPrune();
 
 		Repository dst = createBareRepository();
 		assertFalse(dst.hasObject(A_txt));
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
index bce44f9..06bfd79 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
@@ -114,6 +114,7 @@
 		HttpTransport.setConnectionFactory(cf);
 	}
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -124,6 +125,7 @@
 		ServletContextHandler app = server.addContext("/git");
 		GitServlet gs = new GitServlet();
 		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
+			@Override
 			public Repository open(HttpServletRequest req, String name)
 					throws RepositoryNotFoundException,
 					ServiceNotEnabledException {
@@ -239,7 +241,7 @@
 
 	@Test
 	public void testInitialClone_Packed() throws Exception {
-		new TestRepository<Repository>(remoteRepository).packAndPrune();
+		new TestRepository<>(remoteRepository).packAndPrune();
 
 		Repository dst = createBareRepository();
 		assertFalse(dst.hasObject(A_txt));
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java
index 7c6d591..82e79b8 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/FileResolverTest.java
@@ -83,7 +83,7 @@
 
 	private static void assertUnreasonable(String name)
 			throws ServiceNotEnabledException {
-		FileResolver<RepositoryResolver> r = new FileResolver<RepositoryResolver>(
+		FileResolver<RepositoryResolver> r = new FileResolver<>(
 				new File("."), false);
 		try {
 			r.open(null, name);
@@ -103,7 +103,7 @@
 		FileResolver<RepositoryResolver> resolver;
 
 		assertFalse("no git-daemon-export-ok", export.exists());
-		resolver = new FileResolver<RepositoryResolver>(base, false /*
+		resolver = new FileResolver<>(base, false /*
 																	 * require
 																	 * flag
 																	 */);
@@ -114,7 +114,7 @@
 			assertEquals("Service not enabled", e.getMessage());
 		}
 
-		resolver = new FileResolver<RepositoryResolver>(base, true /*
+		resolver = new FileResolver<>(base, true /*
 																	 * export
 																	 * all
 																	 */);
@@ -125,7 +125,7 @@
 		}
 
 		FileUtils.createNewFile(export);
-		resolver = new FileResolver<RepositoryResolver>(base, false /*
+		resolver = new FileResolver<>(base, false /*
 																	 * require
 																	 * flag
 																	 */);
@@ -142,7 +142,7 @@
 		final Repository a = createBareRepository();
 		final String name = a.getDirectory().getName() + "-not-a-git";
 		final File base = a.getDirectory().getParentFile();
-		FileResolver<RepositoryResolver> resolver = new FileResolver<RepositoryResolver>(
+		FileResolver<RepositoryResolver> resolver = new FileResolver<>(
 				base, false);
 
 		try {
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java
index 4b15d4b..de7891c 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java
@@ -107,6 +107,7 @@
 	 * configure the maximum pack file size, the object checker and custom hooks
 	 * just before they talk to the server.
 	 */
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -117,6 +118,7 @@
 		ServletContextHandler app = server.addContext("/git");
 		gs = new GitServlet();
 		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
+			@Override
 			public Repository open(HttpServletRequest req, String name)
 					throws RepositoryNotFoundException,
 					ServiceNotEnabledException {
@@ -129,6 +131,7 @@
 			}
 		});
 		gs.setReceivePackFactory(new DefaultReceivePackFactory() {
+			@Override
 			public ReceivePack create(HttpServletRequest req, Repository db)
 					throws ServiceNotEnabledException,
 					ServiceNotAuthorizedException {
@@ -266,10 +269,11 @@
 		Transport t;
 
 		// this maxPackSize leads to an unPackError
-		maxPackSize = 400;
+		maxPackSize = 100;
 		// this PostReceiveHook when called after an unsuccesfull unpack will
 		// lead to an IllegalStateException
 		postHook = new PostReceiveHook() {
+			@Override
 			public void onPostReceive(ReceivePack rp,
 					Collection<ReceiveCommand> commands) {
 				// the maxPackSize setting caused that the packfile couldn't be
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
index d67c817..adb69ec 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
@@ -88,6 +88,7 @@
 
 	private URIish remoteURI;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -98,6 +99,7 @@
 		ServletContextHandler app = server.addContext("/git");
 		GitServlet gs = new GitServlet();
 		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
+			@Override
 			public Repository open(HttpServletRequest req, String name)
 					throws RepositoryNotFoundException,
 					ServiceNotEnabledException {
@@ -110,11 +112,13 @@
 			}
 		});
 		gs.setReceivePackFactory(new DefaultReceivePackFactory() {
+			@Override
 			public ReceivePack create(HttpServletRequest req, Repository db)
 					throws ServiceNotEnabledException,
 					ServiceNotAuthorizedException {
 				ReceivePack recv = super.create(req, db);
 				recv.setPreReceiveHook(new PreReceiveHook() {
+					@Override
 					public void onPreReceive(ReceivePack rp,
 							Collection<ReceiveCommand> commands) {
 						rp.sendMessage("message line 1");
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
index ce78442..6dbe0e3 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
@@ -94,6 +94,7 @@
 
 	private URIish smartAuthBasicURI;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -132,6 +133,7 @@
 	private ServletContextHandler smart(final String path) {
 		GitServlet gs = new GitServlet();
 		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
+			@Override
 			public Repository open(HttpServletRequest req, String name)
 					throws RepositoryNotFoundException,
 					ServiceNotEnabledException {
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java
index 108e7bb..4c08ec2 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/MeasurePackSizeTest.java
@@ -83,6 +83,7 @@
 
 	long packSize = -1;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -93,6 +94,7 @@
 		ServletContextHandler app = server.addContext("/git");
 		GitServlet gs = new GitServlet();
 		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
+			@Override
 			public Repository open(HttpServletRequest req, String name)
 					throws RepositoryNotFoundException,
 					ServiceNotEnabledException {
@@ -105,12 +107,14 @@
 			}
 		});
 		gs.setReceivePackFactory(new DefaultReceivePackFactory() {
+			@Override
 			public ReceivePack create(HttpServletRequest req, Repository db)
 					throws ServiceNotEnabledException,
 					ServiceNotAuthorizedException {
 				ReceivePack recv = super.create(req, db);
 				recv.setPostReceiveHook(new PostReceiveHook() {
 
+					@Override
 					public void onPostReceive(ReceivePack rp,
 							Collection<ReceiveCommand> commands) {
 						packSize = rp.getPackSize();
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java
index 68c5a3a..87d0bad 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/ProtocolErrorTest.java
@@ -84,6 +84,7 @@
 
 	private RevBlob a_blob;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -94,6 +95,7 @@
 		ServletContextHandler app = server.addContext("/git");
 		GitServlet gs = new GitServlet();
 		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
+			@Override
 			public Repository open(HttpServletRequest req, String name)
 					throws RepositoryNotFoundException,
 					ServiceNotEnabledException {
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/RegexPipelineTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/RegexPipelineTest.java
index 64fbc01..725a590 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/RegexPipelineTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/RegexPipelineTest.java
@@ -93,12 +93,14 @@
 		}
 	}
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		server = new AppServer();
 		ctx = server.addContext("/");
 	}
 
+	@Override
 	@After
 	public void tearDown() throws Exception {
 		server.tearDown();
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SetAdditionalHeadersTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SetAdditionalHeadersTest.java
index 5be7834..ef8daec 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SetAdditionalHeadersTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SetAdditionalHeadersTest.java
@@ -77,6 +77,7 @@
 	private RevCommit A, B;
 
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -111,7 +112,7 @@
 			assertTrue("isa TransportHttp", t instanceof TransportHttp);
 			assertTrue("isa HttpTransport", t instanceof HttpTransport);
 
-			HashMap<String, String> headers = new HashMap<String, String>();
+			HashMap<String, String> headers = new HashMap<>();
 			headers.put("Cookie", "someTokenValue=23gBog34");
 			headers.put("AnotherKey", "someValue");
 			((TransportHttp) t).setAdditionalHeaders(headers);
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
index 3e88271..ed223c9 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -112,6 +112,7 @@
 import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory;
 import org.eclipse.jgit.transport.resolver.RepositoryResolver;
 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
+import org.eclipse.jgit.util.HttpSupport;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -128,6 +129,8 @@
 
 	private URIish brokenURI;
 
+	private URIish redirectURI;
+
 	private RevBlob A_txt;
 
 	private RevCommit A, B;
@@ -144,6 +147,7 @@
 		HttpTransport.setConnectionFactory(cf);
 	}
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -155,13 +159,42 @@
 				.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
 						ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
 
-		ServletContextHandler app = server.addContext("/git");
 		GitServlet gs = new GitServlet();
+
+		ServletContextHandler app = addNormalContext(gs, src, srcName);
+
+		ServletContextHandler broken = addBrokenContext(gs, src, srcName);
+
+		ServletContextHandler redirect = addRedirectContext(gs, src, srcName);
+
+		server.setUp();
+
+		remoteRepository = src.getRepository();
+		remoteURI = toURIish(app, srcName);
+		brokenURI = toURIish(broken, srcName);
+		redirectURI = toURIish(redirect, srcName);
+
+		A_txt = src.blob("A");
+		A = src.commit().add("A_txt", A_txt).create();
+		B = src.commit().parent(A).add("A_txt", "C").add("B", "B").create();
+		src.update(master, B);
+
+		src.update("refs/garbage/a/very/long/ref/name/to/compress", B);
+	}
+
+	private ServletContextHandler addNormalContext(GitServlet gs, TestRepository<Repository> src, String srcName) {
+		ServletContextHandler app = server.addContext("/git");
 		gs.setRepositoryResolver(new TestRepoResolver(src, srcName));
 		app.addServlet(new ServletHolder(gs), "/*");
+		return app;
+	}
 
+	@SuppressWarnings("unused")
+	private ServletContextHandler addBrokenContext(GitServlet gs, TestRepository<Repository> src, String srcName) {
 		ServletContextHandler broken = server.addContext("/bad");
 		broken.addFilter(new FilterHolder(new Filter() {
+
+			@Override
 			public void doFilter(ServletRequest request,
 					ServletResponse response, FilterChain chain)
 					throws IOException, ServletException {
@@ -173,29 +206,58 @@
 				w.close();
 			}
 
-			public void init(FilterConfig filterConfig) throws ServletException {
-				//
+			@Override
+			public void init(FilterConfig filterConfig)
+					throws ServletException {
+				// empty
 			}
 
+			@Override
 			public void destroy() {
-				//
+				// empty
 			}
 		}), "/" + srcName + "/git-upload-pack",
 				EnumSet.of(DispatcherType.REQUEST));
 		broken.addServlet(new ServletHolder(gs), "/*");
+		return broken;
+	}
 
-		server.setUp();
+	@SuppressWarnings("unused")
+	private ServletContextHandler addRedirectContext(GitServlet gs,
+			TestRepository<Repository> src, String srcName) {
+		ServletContextHandler redirect = server.addContext("/redirect");
+		redirect.addFilter(new FilterHolder(new Filter() {
 
-		remoteRepository = src.getRepository();
-		remoteURI = toURIish(app, srcName);
-		brokenURI = toURIish(broken, srcName);
+			@Override
+			public void init(FilterConfig filterConfig)
+					throws ServletException {
+				// empty
+			}
 
-		A_txt = src.blob("A");
-		A = src.commit().add("A_txt", A_txt).create();
-		B = src.commit().parent(A).add("A_txt", "C").add("B", "B").create();
-		src.update(master, B);
+			@Override
+			public void doFilter(ServletRequest request,
+					ServletResponse response, FilterChain chain)
+					throws IOException, ServletException {
+				final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+				final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+				final StringBuffer fullUrl = httpServletRequest.getRequestURL();
+				if (httpServletRequest.getQueryString() != null) {
+					fullUrl.append("?")
+							.append(httpServletRequest.getQueryString());
+				}
+				httpServletResponse
+						.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
+				httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
+						fullUrl.toString().replace("/redirect", "/git"));
+			}
 
-		src.update("refs/garbage/a/very/long/ref/name/to/compress", B);
+			@Override
+			public void destroy() {
+				// empty
+			}
+		}), "/*", EnumSet.of(DispatcherType.REQUEST));
+		redirect.addServlet(new ServletHolder(gs), "/*");
+		return redirect;
 	}
 
 	@Test
@@ -312,6 +374,52 @@
 	}
 
 	@Test
+	public void testInitialClone_RedirectSmall() throws Exception {
+		Repository dst = createBareRepository();
+		assertFalse(dst.hasObject(A_txt));
+
+		try (Transport t = Transport.open(dst, redirectURI)) {
+			t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
+		}
+
+		assertTrue(dst.hasObject(A_txt));
+		assertEquals(B, dst.exactRef(master).getObjectId());
+		fsck(dst, B);
+
+		List<AccessEvent> requests = getRequests();
+		assertEquals(4, requests.size());
+
+		AccessEvent firstRedirect = requests.get(0);
+		assertEquals(301, firstRedirect.getStatus());
+
+		AccessEvent info = requests.get(1);
+		assertEquals("GET", info.getMethod());
+		assertEquals(join(remoteURI, "info/refs"), info.getPath());
+		assertEquals(1, info.getParameters().size());
+		assertEquals("git-upload-pack", info.getParameter("service"));
+		assertEquals(200, info.getStatus());
+		assertEquals("application/x-git-upload-pack-advertisement",
+				info.getResponseHeader(HDR_CONTENT_TYPE));
+		assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
+
+		AccessEvent secondRedirect = requests.get(2);
+		assertEquals(301, secondRedirect.getStatus());
+
+		AccessEvent service = requests.get(3);
+		assertEquals("POST", service.getMethod());
+		assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
+		assertEquals(0, service.getParameters().size());
+		assertNotNull("has content-length",
+				service.getRequestHeader(HDR_CONTENT_LENGTH));
+		assertNull("not chunked",
+				service.getRequestHeader(HDR_TRANSFER_ENCODING));
+
+		assertEquals(200, service.getStatus());
+		assertEquals("application/x-git-upload-pack-result",
+				service.getResponseHeader(HDR_CONTENT_TYPE));
+	}
+
+	@Test
 	public void testFetch_FewLocalCommits() throws Exception {
 		// Bootstrap by doing the clone.
 		//
@@ -329,7 +437,7 @@
 
 		// Create a new commit on the remote.
 		//
-		b = new TestRepository<Repository>(remoteRepository).branch(master);
+		b = new TestRepository<>(remoteRepository).branch(master);
 		RevCommit Z = b.commit().message("Z").create();
 
 		// Now incrementally update.
@@ -389,7 +497,7 @@
 
 		// Create a new commit on the remote.
 		//
-		b = new TestRepository<Repository>(remoteRepository).branch(master);
+		b = new TestRepository<>(remoteRepository).branch(master);
 		RevCommit Z = b.commit().message("Z").create();
 
 		// Now incrementally update.
@@ -506,7 +614,7 @@
 			final String repoName = "refs-unreadable";
 			RefsUnreadableInMemoryRepository badRefsRepo = new RefsUnreadableInMemoryRepository(
 					new DfsRepositoryDescription(repoName));
-			final TestRepository<Repository> repo = new TestRepository<Repository>(
+			final TestRepository<Repository> repo = new TestRepository<>(
 					badRefsRepo);
 
 			ServletContextHandler app = noRefServer.addContext("/git");
@@ -727,6 +835,7 @@
 			this.repoName = repoName;
 		}
 
+		@Override
 		public Repository open(HttpServletRequest req, String name)
 				throws RepositoryNotFoundException, ServiceNotEnabledException {
 			if (!name.equals(repoName))
diff --git a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
index 10c29d5..64f7498 100644
--- a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.junit.http/BUCK b/org.eclipse.jgit.junit.http/BUCK
deleted file mode 100644
index 68976a6..0000000
--- a/org.eclipse.jgit.junit.http/BUCK
+++ /dev/null
@@ -1,18 +0,0 @@
-java_library(
-  name = 'junit-http',
-  srcs = glob(['src/**']),
-  resources = glob(['resources/**']),
-  provided_deps = [
-    '//org.eclipse.jgit:jgit',
-    '//org.eclipse.jgit.http.server:jgit-servlet',
-    '//org.eclipse.jgit.junit:junit',
-    '//lib:junit',
-    '//lib:servlet-api',
-    '//lib/jetty:http',
-    '//lib/jetty:server',
-    '//lib/jetty:servlet',
-    '//lib/jetty:security',
-    '//lib/jetty:util',
-  ],
-  visibility = ['PUBLIC'],
-)
diff --git a/org.eclipse.jgit.junit.http/BUILD b/org.eclipse.jgit.junit.http/BUILD
new file mode 100644
index 0000000..be6e1ae
--- /dev/null
+++ b/org.eclipse.jgit.junit.http/BUILD
@@ -0,0 +1,22 @@
+package(default_visibility = ["//visibility:public"])
+
+java_library(
+    name = "junit-http",
+    testonly = 1,
+    srcs = glob(["src/**"]),
+    resources = glob(["resources/**"]),
+    # TODO(davido): we want here provided deps
+    deps = [
+        "//lib:jetty-http",
+        "//lib:jetty-security",
+        "//lib:jetty-server",
+        "//lib:jetty-servlet",
+        "//lib:jetty-util",
+        "//lib:junit",
+        "//lib:servlet-api",
+        "//lib:slf4j-api",
+        "//org.eclipse.jgit.http.server:jgit-servlet",
+        "//org.eclipse.jgit:jgit",
+        "//org.eclipse.jgit.junit:junit",
+    ],
+)
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index cd42e2c..b313dfa 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.2.qualifier
+Bundle-Version: 4.7.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
@@ -20,22 +20,23 @@
  org.eclipse.jetty.util.component;version="[9.0.0,9.4.0)",
  org.eclipse.jetty.util.log;version="[9.0.0,9.4.0)",
  org.eclipse.jetty.util.security;version="[9.0.0,9.4.0)",
- org.eclipse.jgit.errors;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.http.server;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.junit;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport.resolver;version="[4.6.2,4.7.0)",
+ org.eclipse.jgit.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.http.server;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.junit;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.7.2,4.8.0)",
  org.junit;version="[4.0.0,5.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="4.6.2";
+Export-Package: org.eclipse.jgit.junit.http;version="4.7.2";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.junit,
    javax.servlet.http,
    org.eclipse.jgit.lib,
    org.eclipse.jgit.revwalk,
    org.eclipse.jetty.server.handler,
+   org.eclipse.jetty.security,
    javax.servlet,
    org.eclipse.jetty.server,
    org.eclipse.jetty.util.log,
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index ced9c2c..ecd6122 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.2-SNAPSHOT</version>
+    <version>4.7.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java
index aaccc66..6b7853d 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AccessEvent.java
@@ -76,7 +76,7 @@
 	}
 
 	private static Map<String, String> cloneHeaders(final Request req) {
-		Map<String, String> r = new TreeMap<String, String>();
+		Map<String, String> r = new TreeMap<>();
 		Enumeration hn = req.getHeaderNames();
 		while (hn.hasMoreElements()) {
 			String key = (String) hn.nextElement();
@@ -88,7 +88,7 @@
 	}
 
 	private static Map<String, String> cloneHeaders(final Response rsp) {
-		Map<String, String> r = new TreeMap<String, String>();
+		Map<String, String> r = new TreeMap<>();
 		Enumeration<String> hn = rsp.getHttpFields().getFieldNames();
 		while (hn.hasMoreElements()) {
 			String key = hn.nextElement();
@@ -153,6 +153,7 @@
 		return responseHeaders.get(name);
 	}
 
+	@Override
 	public String toString() {
 		StringBuilder b = new StringBuilder();
 		b.append(method);
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
index 44c1977..a663484 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
@@ -269,7 +269,7 @@
 
 	/** @return all requests since the server was started. */
 	public List<AccessEvent> getRequests() {
-		return new ArrayList<AccessEvent>(log.getEvents());
+		return new ArrayList<>(log.getEvents());
 	}
 
 	/**
@@ -289,7 +289,7 @@
 	 * @return all requests which match the given path.
 	 */
 	public List<AccessEvent> getRequests(String path) {
-		ArrayList<AccessEvent> r = new ArrayList<AccessEvent>();
+		ArrayList<AccessEvent> r = new ArrayList<>();
 		for (AccessEvent event : log.getEvents()) {
 			if (event.getPath().equals(path)) {
 				r.add(event);
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java
index ab5d3e1..1b94e02 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/HttpTestCase.java
@@ -74,11 +74,13 @@
 	/** In-memory application server; subclass must start. */
 	protected AppServer server;
 
+	@Override
 	public void setUp() throws Exception {
 		super.setUp();
 		server = new AppServer();
 	}
 
+	@Override
 	public void tearDown() throws Exception {
 		server.tearDown();
 		super.tearDown();
@@ -86,7 +88,7 @@
 
 	protected TestRepository<Repository> createTestRepository()
 			throws IOException {
-		return new TestRepository<Repository>(createBareRepository());
+		return new TestRepository<>(createBareRepository());
 	}
 
 	protected URIish toURIish(String path) throws URISyntaxException {
@@ -118,12 +120,12 @@
 	protected static void fsck(Repository db, RevObject... tips)
 			throws Exception {
 		TestRepository<? extends Repository> tr =
-				new TestRepository<Repository>(db);
+				new TestRepository<>(db);
 		tr.fsck(tips);
 	}
 
 	protected static Set<RefSpec> mirror(String... refs) {
-		HashSet<RefSpec> r = new HashSet<RefSpec>();
+		HashSet<RefSpec> r = new HashSet<>();
 		for (String name : refs) {
 			RefSpec rs = new RefSpec(name);
 			rs = rs.setDestination(name);
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/MockServletConfig.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/MockServletConfig.java
index 5976589..9defcd9 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/MockServletConfig.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/MockServletConfig.java
@@ -52,33 +52,39 @@
 import javax.servlet.ServletContext;
 
 public class MockServletConfig implements ServletConfig {
-	private final Map<String, String> parameters = new HashMap<String, String>();
+	private final Map<String, String> parameters = new HashMap<>();
 
 	public void setInitParameter(String name, String value) {
 		parameters.put(name, value);
 	}
 
+	@Override
 	public String getInitParameter(String name) {
 		return parameters.get(name);
 	}
 
+	@Override
 	public Enumeration<String> getInitParameterNames() {
 		final Iterator<String> i = parameters.keySet().iterator();
 		return new Enumeration<String>() {
+			@Override
 			public boolean hasMoreElements() {
 				return i.hasNext();
 			}
 
+			@Override
 			public String nextElement() {
 				return i.next();
 			}
 		};
 	}
 
+	@Override
 	public String getServletName() {
 		return "MOCK_SERVLET";
 	}
 
+	@Override
 	public ServletContext getServletContext() {
 		return null;
 	}
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java
index 7600843..415398d 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java
@@ -52,7 +52,7 @@
 
 /** Logs warnings into an array for later inspection. */
 public class RecordingLogger implements Logger {
-	private static List<Warning> warnings = new ArrayList<Warning>();
+	private static List<Warning> warnings = new ArrayList<>();
 
 	/** Clear the warnings, automatically done by {@link AppServer#setUp()} */
 	public static void clear() {
@@ -64,7 +64,7 @@
 	/** @return the warnings (if any) from the last execution */
 	public static List<Warning> getWarnings() {
 		synchronized (warnings) {
-			ArrayList<Warning> copy = new ArrayList<Warning>(warnings);
+			ArrayList<Warning> copy = new ArrayList<>(warnings);
 			return Collections.unmodifiableList(copy);
 		}
 	}
@@ -94,10 +94,12 @@
 		this.name = name;
 	}
 
+	@Override
 	public Logger getLogger(@SuppressWarnings("hiding") String name) {
 		return new RecordingLogger(name);
 	}
 
+	@Override
 	public String getName() {
 		return name;
 	}
@@ -108,6 +110,7 @@
 		}
 	}
 
+	@Override
 	public void warn(String msg, Throwable th) {
 		synchronized (warnings) {
 			warnings.add(new Warning(msg, th));
@@ -126,6 +129,7 @@
 		// Ignore (not relevant to test failures)
 	}
 
+	@Override
 	public void debug(String msg, Throwable th) {
 		// Ignore (not relevant to test failures)
 	}
@@ -144,46 +148,56 @@
 		// Ignore (not relevant to test failures)
 	}
 
+	@Override
 	public boolean isDebugEnabled() {
 		return false;
 	}
 
+	@Override
 	public void setDebugEnabled(boolean enabled) {
 		// Ignore (not relevant to test failures)
 	}
 
+	@Override
 	public void warn(String msg, Object... args) {
 		synchronized (warnings) {
 			warnings.add(new Warning(MessageFormat.format(msg, args)));
 		}
 	}
 
+	@Override
 	public void warn(Throwable thrown) {
 		synchronized (warnings) {
 			warnings.add(new Warning(thrown));
 		}
 	}
 
+	@Override
 	public void info(String msg, Object... args) {
 		// Ignore (not relevant to test failures)
 	}
 
+	@Override
 	public void info(Throwable thrown) {
 		// Ignore (not relevant to test failures)
 	}
 
+	@Override
 	public void info(String msg, Throwable thrown) {
 		// Ignore (not relevant to test failures)
 	}
 
+	@Override
 	public void debug(String msg, Object... args) {
 		// Ignore (not relevant to test failures)
 	}
 
+	@Override
 	public void debug(Throwable thrown) {
 		// Ignore (not relevant to test failures)
 	}
 
+	@Override
 	public void ignore(Throwable arg0) {
 		// Ignore (not relevant to test failures)
 	}
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java
index e550e6c..605c69a 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/SimpleHttpServer.java
@@ -92,6 +92,7 @@
 	private ServletContextHandler smart(final String path) {
 		GitServlet gs = new GitServlet();
 		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
+			@Override
 			public Repository open(HttpServletRequest req, String name)
 					throws RepositoryNotFoundException,
 					ServiceNotEnabledException {
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
index 14ea03a..c218c07 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
@@ -61,7 +61,7 @@
 class TestRequestLog extends HandlerWrapper {
   private static final int MAX = 16;
 
-  private final List<AccessEvent> events = new ArrayList<AccessEvent>();
+  private final List<AccessEvent> events = new ArrayList<>();
 
   private final Semaphore active = new Semaphore(MAX);
 
diff --git a/org.eclipse.jgit.junit/.settings/.api_filters b/org.eclipse.jgit.junit/.settings/.api_filters
new file mode 100644
index 0000000..a70ce77
--- /dev/null
+++ b/org.eclipse.jgit.junit/.settings/.api_filters
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.jgit.junit" version="2">
+    <resource path="src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java" type="org.eclipse.jgit.junit.LocalDiskRepositoryTestCase">
+        <filter comment="OK to use internal implementation in tests" id="643842064">
+            <message_arguments>
+                <message_argument value="FileRepository"/>
+                <message_argument value="LocalDiskRepositoryTestCase"/>
+                <message_argument value="createBareRepository()"/>
+            </message_arguments>
+        </filter>
+        <filter comment="OK to use internal implementation in tests" id="643842064">
+            <message_arguments>
+                <message_argument value="FileRepository"/>
+                <message_argument value="LocalDiskRepositoryTestCase"/>
+                <message_argument value="createRepository(boolean, boolean)"/>
+            </message_arguments>
+        </filter>
+        <filter comment="OK to use internal implementation in tests" id="643842064">
+            <message_arguments>
+                <message_argument value="FileRepository"/>
+                <message_argument value="LocalDiskRepositoryTestCase"/>
+                <message_argument value="createWorkRepository()"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/junit/RepositoryTestCase.java" type="org.eclipse.jgit.junit.RepositoryTestCase">
+        <filter comment="OK to use internal implementation in tests" id="627060751">
+            <message_arguments>
+                <message_argument value="FileRepository"/>
+                <message_argument value="RepositoryTestCase"/>
+                <message_argument value="db"/>
+            </message_arguments>
+        </filter>
+    </resource>
+</component>
diff --git a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
index 10c29d5..64f7498 100644
--- a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.junit/BUCK b/org.eclipse.jgit.junit/BUCK
deleted file mode 100644
index 7e25432..0000000
--- a/org.eclipse.jgit.junit/BUCK
+++ /dev/null
@@ -1,10 +0,0 @@
-java_library(
-  name = 'junit',
-  srcs = glob(['src/**']),
-  resources = glob(['resources/**']),
-  provided_deps = [
-    '//org.eclipse.jgit:jgit',
-    '//lib:junit',
-  ],
-  visibility = ['PUBLIC'],
-)
diff --git a/org.eclipse.jgit.junit/BUILD b/org.eclipse.jgit.junit/BUILD
new file mode 100644
index 0000000..350b25f
--- /dev/null
+++ b/org.eclipse.jgit.junit/BUILD
@@ -0,0 +1,14 @@
+package(default_visibility = ["//visibility:public"])
+
+java_library(
+    name = "junit",
+    testonly = 1,
+    srcs = glob(["src/**"]),
+    resource_strip_prefix = "org.eclipse.jgit.junit/resources",
+    resources = glob(["resources/**"]),
+    deps = [
+        "//lib:junit",
+        # We want these deps to be provided_deps
+        "//org.eclipse.jgit:jgit",
+    ],
+)
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index 9de6239..2ddb638 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.2.qualifier
+Bundle-Version: 4.7.2.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.2,4.7.0)",
- org.eclipse.jgit.api.errors;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.dircache;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.errors;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.merge;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.treewalk;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util.io;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util.time;version="[4.6.2,4.7.0)",
+Import-Package: org.eclipse.jgit.api;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.api.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.dircache;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.merge;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.storage.file;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.treewalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util.io;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util.time;version="[4.7.2,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.2";
+Export-Package: org.eclipse.jgit.junit;version="4.7.2";
   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.2"
+ org.eclipse.jgit.junit.time;version="4.7.2"
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index ab757af..1496470 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.2-SNAPSHOT</version>
+    <version>4.7.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
index dc2e8bf..6ace9fc 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
@@ -53,13 +53,16 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeSet;
 
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheEntry;
 import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
@@ -106,7 +109,7 @@
 	 */
 	protected MockSystemReader mockSystemReader;
 
-	private final List<Repository> toClose = new ArrayList<Repository>();
+	private final Set<Repository> toClose = new HashSet<>();
 	private File tmp;
 
 	@Before
@@ -119,6 +122,12 @@
 		mockSystemReader = new MockSystemReader();
 		mockSystemReader.userGitConfig = new FileBasedConfig(new File(tmp,
 				"usergitconfig"), FS.DETECTED);
+		// We have to set autoDetach to false for tests, because tests expect to be able
+		// to clean up by recursively removing the repository, and background GC might be
+		// in the middle of writing or deleting files, which would disrupt this.
+		mockSystemReader.userGitConfig.setBoolean(ConfigConstants.CONFIG_GC_SECTION,
+				null, ConfigConstants.CONFIG_KEY_AUTODETACH, false);
+		mockSystemReader.userGitConfig.save();
 		ceilTestDirectories(getCeilings());
 		SystemReader.setInstance(mockSystemReader);
 
@@ -284,7 +293,7 @@
 			throws IllegalStateException, IOException {
 		DirCache dc = repo.readDirCache();
 		StringBuilder sb = new StringBuilder();
-		TreeSet<Long> timeStamps = new TreeSet<Long>();
+		TreeSet<Long> timeStamps = new TreeSet<>();
 
 		// iterate once over the dircache just to collect all time stamps
 		if (0 != (includedOptions & MOD_TIME)) {
@@ -357,12 +366,32 @@
 	 * @throws IOException
 	 *             the repository could not be created in the temporary area
 	 */
-	private FileRepository createRepository(boolean bare) throws IOException {
+	private FileRepository createRepository(boolean bare)
+			throws IOException {
+		return createRepository(bare, true /* auto close */);
+	}
+
+	/**
+	 * Creates a new empty repository.
+	 *
+	 * @param bare
+	 *            true to create a bare repository; false to make a repository
+	 *            within its working directory
+	 * @param autoClose
+	 *            auto close the repository in #tearDown
+	 * @return the newly created repository, opened for access
+	 * @throws IOException
+	 *             the repository could not be created in the temporary area
+	 */
+	public FileRepository createRepository(boolean bare, boolean autoClose)
+			throws IOException {
 		File gitdir = createUniqueTestGitDir(bare);
 		FileRepository db = new FileRepository(gitdir);
 		assertFalse(gitdir.exists());
 		db.create(bare);
-		toClose.add(db);
+		if (autoClose) {
+			addRepoToClose(db);
+		}
 		return db;
 	}
 
@@ -530,7 +559,7 @@
 	}
 
 	private static HashMap<String, String> cloneEnv() {
-		return new HashMap<String, String>(System.getenv());
+		return new HashMap<>(System.getenv());
 	}
 
 	private static final class CleanupThread extends Thread {
@@ -552,7 +581,7 @@
 			}
 		}
 
-		private final List<File> toDelete = new ArrayList<File>();
+		private final List<File> toDelete = new ArrayList<>();
 
 		@Override
 		public void run() {
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
index 6faa2ec..68482c6 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
@@ -88,7 +88,7 @@
 
 	long now = 1250379778668L; // Sat Aug 15 20:12:58 GMT-03:30 2009
 
-	final Map<String, String> values = new HashMap<String, String>();
+	final Map<String, String> values = new HashMap<>();
 
 	FileBasedConfig userGitConfig;
 
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
index a44e999..d1358ee 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
@@ -876,7 +876,7 @@
 
 			final File pack, idx;
 			try (PackWriter pw = new PackWriter(db)) {
-				Set<ObjectId> all = new HashSet<ObjectId>();
+				Set<ObjectId> all = new HashSet<>();
 				for (Ref r : db.getAllRefs().values())
 					all.add(r.getObjectId());
 				pw.preparePack(m, all, PackWriter.NONE);
@@ -992,7 +992,7 @@
 
 		private ObjectId topLevelTree;
 
-		private final List<RevCommit> parents = new ArrayList<RevCommit>(2);
+		private final List<RevCommit> parents = new ArrayList<>(2);
 
 		private int tick = 1;
 
diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs
index 10c29d5..64f7498 100644
--- a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.lfs.server.test/BUCK b/org.eclipse.jgit.lfs.server.test/BUCK
deleted file mode 100644
index 25e9f09..0000000
--- a/org.eclipse.jgit.lfs.server.test/BUCK
+++ /dev/null
@@ -1,34 +0,0 @@
-TEST_BASE = ['tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java']
-TESTS = glob(['tst/**/*.java'],
-    excludes = TEST_BASE
-)
-
-for t in TESTS:
-  n = t[len('tst/'):len(t)-len('.java')].replace('/', '.')
-  java_test(
-    name = n,
-    labels = ['lfs-server'],
-    srcs = [t] + TEST_BASE,
-    deps = [
-      '//org.eclipse.jgit.lfs.test:helpers',
-      '//org.eclipse.jgit:jgit',
-      '//org.eclipse.jgit.junit:junit',
-      '//org.eclipse.jgit.junit.http:junit-http',
-      '//org.eclipse.jgit.lfs:jgit-lfs',
-      '//org.eclipse.jgit.lfs.server:jgit-lfs-server',
-      '//lib:hamcrest-core',
-      '//lib:hamcrest-library',
-      '//lib:httpcore',
-      '//lib:httpcomponents',
-      '//lib:junit',
-      '//lib/jetty:http',
-      '//lib/jetty:io',
-      '//lib/jetty:server',
-      '//lib/jetty:servlet',
-      '//lib/jetty:security',
-      '//lib/jetty:util',
-      '//lib:servlet-api',
-      '//lib:commons-logging',
-    ],
-    vm_args = ['-Xmx256m', '-Dfile.encoding=UTF-8'],
-  )
diff --git a/org.eclipse.jgit.lfs.server.test/BUILD b/org.eclipse.jgit.lfs.server.test/BUILD
new file mode 100644
index 0000000..1341dd6
--- /dev/null
+++ b/org.eclipse.jgit.lfs.server.test/BUILD
@@ -0,0 +1,49 @@
+load(
+    "@com_googlesource_gerrit_bazlets//tools:junit.bzl",
+    "junit_tests",
+)
+
+TEST_BASE = ["tst/org/eclipse/jgit/lfs/server/fs/LfsServerTest.java"]
+
+DEPS = [
+    "//org.eclipse.jgit.lfs.test:helpers",
+    "//org.eclipse.jgit:jgit",
+    "//org.eclipse.jgit.junit:junit",
+    "//org.eclipse.jgit.junit.http:junit-http",
+    "//org.eclipse.jgit.lfs:jgit-lfs",
+    "//org.eclipse.jgit.lfs.server:jgit-lfs-server",
+    "//lib:commons-logging",
+    "//lib:httpcore",
+    "//lib:httpclient",
+    "//lib:junit",
+    "//lib:jetty-http",
+    "//lib:jetty-io",
+    "//lib:jetty-server",
+    "//lib:jetty-servlet",
+    "//lib:jetty-security",
+    "//lib:jetty-util",
+    "//lib:servlet-api",
+]
+
+junit_tests(
+    name = "lfs_server",
+    srcs = glob(
+        ["tst/**/*.java"],
+        exclude = TEST_BASE,
+    ),
+    jvm_flags = [
+        "-Xmx256m",
+        "-Dfile.encoding=UTF-8",
+    ],
+    tags = ["lfs-server"],
+    deps = DEPS + [
+        ":helpers",
+    ],
+)
+
+java_library(
+    name = "helpers",
+    testonly = 1,
+    srcs = TEST_BASE,
+    deps = DEPS,
+)
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 405e07a..aa69609 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.2.qualifier
+Bundle-Version: 4.7.2.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,9.4.0)",
  org.eclipse.jetty.util.security;version="[9.0.0,9.4.0)",
  org.eclipse.jetty.util.thread;version="[9.0.0,9.4.0)",
- org.eclipse.jgit.junit.http;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs.server.fs;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs.test;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)",
+ org.eclipse.jgit.junit.http;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs.test;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,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 d178fef..f687723 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.2-SNAPSHOT</version>
+    <version>4.7.2-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
index e662937..b5f65e1 100644
--- a/org.eclipse.jgit.lfs.server/.settings/.api_filters
+++ b/org.eclipse.jgit.lfs.server/.settings/.api_filters
@@ -1,16 +1,10 @@
 <?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">
+        <filter comment="breaking API for implementors is ok in minor releases" 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_argument value="getLargeFileRepository(LfsProtocolServlet.LfsRequest, String, String)"/>
             </message_arguments>
         </filter>
     </resource>
diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs
index 808ec3a..ede0f7d 100644
--- a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.lfs.server/BUCK b/org.eclipse.jgit.lfs.server/BUCK
deleted file mode 100644
index 6b40b7c..0000000
--- a/org.eclipse.jgit.lfs.server/BUCK
+++ /dev/null
@@ -1,22 +0,0 @@
-SRCS = glob(['src/**'])
-RESOURCES = glob(['resources/**'])
-
-java_library(
-  name = 'jgit-lfs-server',
-  srcs = SRCS,
-  resources = RESOURCES,
-  deps = [
-    '//org.eclipse.jgit.http.apache:http-apache',
-    '//org.eclipse.jgit:jgit',
-    '//org.eclipse.jgit.lfs:jgit-lfs',
-    '//lib:gson',
-    '//lib:httpcore',
-    '//lib:servlet-api'
-  ],
-  visibility = ['PUBLIC'],
-)
-
-java_sources(
-  name = 'jgit-lfs-server_src',
-  srcs = SRCS + RESOURCES,
-)
diff --git a/org.eclipse.jgit.lfs.server/BUILD b/org.eclipse.jgit.lfs.server/BUILD
new file mode 100644
index 0000000..fa14e8a
--- /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 = [
+        "//lib:gson",
+        "//lib:httpcore",
+        "//lib:servlet-api",
+        "//lib:slf4j-api",
+        "//org.eclipse.jgit.http.apache:http-apache",
+        "//org.eclipse.jgit:jgit",
+        "//org.eclipse.jgit.lfs:jgit-lfs",
+    ],
+)
diff --git a/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.server/META-INF/MANIFEST.MF
index 3e8a9d1..773f0dc 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.2.qualifier
+Bundle-Version: 4.7.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.lfs.server;version="4.6.2";
+Export-Package: org.eclipse.jgit.lfs.server;version="4.7.2";
   uses:="javax.servlet.http,
    org.eclipse.jgit.lfs.lib",
- org.eclipse.jgit.lfs.server.fs;version="4.6.2";
+ org.eclipse.jgit.lfs.server.fs;version="4.7.2";
   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.2";x-internal:=true,
- org.eclipse.jgit.lfs.server.s3;version="4.6.2";
+ org.eclipse.jgit.lfs.server.internal;version="4.7.2";x-internal:=true,
+ org.eclipse.jgit.lfs.server.s3;version="4.7.2";
   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.2,4.7.0)",
- org.eclipse.jgit.internal;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs.errors;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs.internal;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport.http;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)"
+ org.eclipse.jgit.annotations;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs.internal;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport.http;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,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 cc84686..0a719ae 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.2-SNAPSHOT</version>
+    <version>4.7.2-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..2473dcd 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,12 @@
 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 static org.eclipse.jgit.util.HttpSupport.HDR_AUTHORIZATION;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -58,6 +63,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 +77,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 +96,8 @@
  * @since 4.3
  */
 public abstract class LfsProtocolServlet extends HttpServlet {
+	private static Logger LOG = LoggerFactory
+			.getLogger(LfsProtocolServlet.class);
 
 	private static final long serialVersionUID = 1L;
 
@@ -132,9 +144,54 @@
 	 *             <dd>when an unexpected internal server error occurred</dd>
 	 *             </dl>
 	 * @since 4.5
+	 * @deprecated use
+	 *             {@link #getLargeFileRepository(LfsRequest, String, String)}
+	 */
+	@Deprecated
+	protected LargeFileRepository getLargeFileRepository(LfsRequest request,
+			String path) throws LfsException {
+		return getLargeFileRepository(request, path, null);
+	}
+
+	/**
+	 * Get the large file repository for the given request and path.
+	 *
+	 * @param request
+	 *            the request
+	 * @param path
+	 *            the path
+	 * @param auth
+	 *            the Authorization HTTP header
+	 *
+	 * @return the large file repository storing large files.
+	 * @throws LfsException
+	 *             implementations should throw more specific exceptions to
+	 *             signal which type of error occurred:
+	 *             <dl>
+	 *             <dt>{@link LfsValidationError}</dt>
+	 *             <dd>when there is a validation error with one or more of the
+	 *             objects in the request</dd>
+	 *             <dt>{@link LfsRepositoryNotFound}</dt>
+	 *             <dd>when the repository does not exist for the user</dd>
+	 *             <dt>{@link LfsRepositoryReadOnly}</dt>
+	 *             <dd>when the user has read, but not write access. Only
+	 *             applicable when the operation in the request is "upload"</dd>
+	 *             <dt>{@link LfsRateLimitExceeded}</dt>
+	 *             <dd>when the user has hit a rate limit with the server</dd>
+	 *             <dt>{@link LfsBandwidthLimitExceeded}</dt>
+	 *             <dd>when the bandwidth limit for the user or repository has
+	 *             been exceeded</dd>
+	 *             <dt>{@link LfsInsufficientStorage}</dt>
+	 *             <dd>when there is insufficient storage on the server</dd>
+	 *             <dt>{@link LfsUnavailable}</dt>
+	 *             <dd>when LFS is not available</dd>
+	 *             <dt>{@link LfsException}</dt>
+	 *             <dd>when an unexpected internal server error occurred</dd>
+	 *             </dl>
+	 * @since 4.7
 	 */
 	protected abstract LargeFileRepository getLargeFileRepository(
-			LfsRequest request, String path) throws LfsException;
+			LfsRequest request, String path, String auth) throws LfsException;
 
 	/**
 	 * LFS request.
@@ -163,6 +220,30 @@
 		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
@@ -179,9 +260,13 @@
 		res.setContentType(CONTENTTYPE_VND_GIT_LFS_JSON);
 		LargeFileRepository repo = null;
 		try {
-			repo = getLargeFileRepository(request, path);
+			repo = getLargeFileRepository(request, path,
+					req.getHeader(HDR_AUTHORIZATION));
 			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 +286,8 @@
 			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.server/src/org/eclipse/jgit/lfs/server/s3/S3Repository.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Repository.java
index c229758..ed896ad 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Repository.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/S3Repository.java
@@ -94,10 +94,10 @@
 	@Override
 	public Response.Action getDownloadAction(AnyLongObjectId oid) {
 		URL endpointUrl = getObjectUrl(oid);
-		Map<String, String> queryParams = new HashMap<String, String>();
+		Map<String, String> queryParams = new HashMap<>();
 		queryParams.put(X_AMZ_EXPIRES,
 				Integer.toString(s3Config.getExpirationSeconds()));
-		Map<String, String> headers = new HashMap<String, String>();
+		Map<String, String> headers = new HashMap<>();
 		String authorizationQueryParameters = SignerV4.createAuthorizationQuery(
 				s3Config, endpointUrl, METHOD_GET, headers, queryParams,
 				UNSIGNED_PAYLOAD);
@@ -111,7 +111,7 @@
 	public Response.Action getUploadAction(AnyLongObjectId oid, long size) {
 		cacheObjectMetaData(oid, size);
 		URL objectUrl = getObjectUrl(oid);
-		Map<String, String> headers = new HashMap<String, String>();
+		Map<String, String> headers = new HashMap<>();
 		headers.put(X_AMZ_CONTENT_SHA256, oid.getName());
 		headers.put(HDR_CONTENT_LENGTH, Long.toString(size));
 		headers.put(X_AMZ_STORAGE_CLASS, s3Config.getStorageClass());
@@ -134,10 +134,10 @@
 	@Override
 	public long getSize(AnyLongObjectId oid) throws IOException {
 		URL endpointUrl = getObjectUrl(oid);
-		Map<String, String> queryParams = new HashMap<String, String>();
+		Map<String, String> queryParams = new HashMap<>();
 		queryParams.put(X_AMZ_EXPIRES,
 				Integer.toString(s3Config.getExpirationSeconds()));
-		Map<String, String> headers = new HashMap<String, String>();
+		Map<String, String> headers = new HashMap<>();
 
 		String authorizationQueryParameters = SignerV4.createAuthorizationQuery(
 				s3Config, endpointUrl, METHOD_HEAD, headers, queryParams,
diff --git a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java
index f95b605..a9b0ec4 100644
--- a/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java
+++ b/org.eclipse.jgit.lfs.server/src/org/eclipse/jgit/lfs/server/s3/SignerV4.java
@@ -57,6 +57,7 @@
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.SimpleTimeZone;
 import java.util.SortedMap;
@@ -239,7 +240,7 @@
 
 	private static String canonicalizeHeaderNames(
 			Map<String, String> headers) {
-		List<String> sortedHeaders = new ArrayList<String>();
+		List<String> sortedHeaders = new ArrayList<>();
 		sortedHeaders.addAll(headers.keySet());
 		Collections.sort(sortedHeaders, String.CASE_INSENSITIVE_ORDER);
 
@@ -247,7 +248,7 @@
 		for (String header : sortedHeaders) {
 			if (buffer.length() > 0)
 				buffer.append(";"); //$NON-NLS-1$
-			buffer.append(header.toLowerCase());
+			buffer.append(header.toLowerCase(Locale.ROOT));
 		}
 
 		return buffer.toString();
@@ -259,13 +260,14 @@
 			return ""; //$NON-NLS-1$
 		}
 
-		List<String> sortedHeaders = new ArrayList<String>();
+		List<String> sortedHeaders = new ArrayList<>();
 		sortedHeaders.addAll(headers.keySet());
 		Collections.sort(sortedHeaders, String.CASE_INSENSITIVE_ORDER);
 
 		StringBuilder buffer = new StringBuilder();
 		for (String key : sortedHeaders) {
-			buffer.append(key.toLowerCase().replaceAll("\\s+", " ") + ":" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			buffer.append(
+					key.toLowerCase(Locale.ROOT).replaceAll("\\s+", " ") + ":" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 					+ headers.get(key).replaceAll("\\s+", " ")); //$NON-NLS-1$//$NON-NLS-2$
 			buffer.append("\n"); //$NON-NLS-1$
 		}
@@ -303,7 +305,7 @@
 			return ""; //$NON-NLS-1$
 		}
 
-		SortedMap<String, String> sorted = new TreeMap<String, String>();
+		SortedMap<String, String> sorted = new TreeMap<>();
 
 		Iterator<Map.Entry<String, String>> pairs = parameters.entrySet()
 				.iterator();
diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
index 10c29d5..64f7498 100644
--- a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.lfs.test/BUCK b/org.eclipse.jgit.lfs.test/BUCK
deleted file mode 100644
index 1298e16..0000000
--- a/org.eclipse.jgit.lfs.test/BUCK
+++ /dev/null
@@ -1,30 +0,0 @@
-TESTS = glob(['tst/**/*.java'])
-
-for t in TESTS:
-  n = t[len('tst/'):len(t)-len('.java')].replace('/', '.')
-  java_test(
-    name = n,
-    labels = ['lfs'],
-    srcs = [t],
-    deps = [
-      ':helpers',
-      '//org.eclipse.jgit.junit:junit',
-      '//org.eclipse.jgit.lfs:jgit-lfs',
-      '//org.eclipse.jgit:jgit',
-      '//lib:hamcrest-core',
-      '//lib:hamcrest-library',
-      '//lib:junit',
-    ],
-  )
-
-java_library(
-  name = 'helpers',
-  srcs = glob(['src/**/*.java']),
-  deps = [
-    '//org.eclipse.jgit:jgit',
-    '//org.eclipse.jgit.lfs:jgit-lfs',
-    '//org.eclipse.jgit.junit:junit',
-    '//lib:junit',
-  ],
-  visibility = ['PUBLIC']
-)
diff --git a/org.eclipse.jgit.lfs.test/BUILD b/org.eclipse.jgit.lfs.test/BUILD
new file mode 100644
index 0000000..213ba57
--- /dev/null
+++ b/org.eclipse.jgit.lfs.test/BUILD
@@ -0,0 +1,31 @@
+package(default_visibility = ["//visibility:public"])
+
+load(
+    "@com_googlesource_gerrit_bazlets//tools:junit.bzl",
+    "junit_tests",
+)
+
+junit_tests(
+    name = "lfs",
+    srcs = glob(["tst/**/*.java"]),
+    tags = ["lfs"],
+    deps = [
+        ":helpers",
+        "//lib:junit",
+        "//org.eclipse.jgit:jgit",
+        "//org.eclipse.jgit.junit:junit",
+        "//org.eclipse.jgit.lfs:jgit-lfs",
+    ],
+)
+
+java_library(
+    name = "helpers",
+    testonly = 1,
+    srcs = glob(["src/**/*.java"]),
+    deps = [
+        "//lib:junit",
+        "//org.eclipse.jgit:jgit",
+        "//org.eclipse.jgit.junit:junit",
+        "//org.eclipse.jgit.lfs:jgit-lfs",
+    ],
+)
diff --git a/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.lfs.test/META-INF/MANIFEST.MF
index e859f63..3cc1048 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.2.qualifier
+Bundle-Version: 4.7.2.qualifier
 Bundle-Vendor: %provider_name
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.junit;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs.errors;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)",
+Import-Package: org.eclipse.jgit.internal.storage.dfs;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.junit;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.treewalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,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.2";x-friends:="org.eclipse.jgit.lfs.server.test"
+Export-Package: org.eclipse.jgit.lfs.test;version="4.7.2";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 a06aefc..cb77a17 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.2-SNAPSHOT</version>
+    <version>4.7.2-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.test/tst/org/eclipse/jgit/lfs/lib/LongObjectIdTest.java b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LongObjectIdTest.java
index 435a2a3..e754d6f 100644
--- a/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LongObjectIdTest.java
+++ b/org.eclipse.jgit.lfs.test/tst/org/eclipse/jgit/lfs/lib/LongObjectIdTest.java
@@ -57,6 +57,7 @@
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.Locale;
 
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.lfs.errors.InvalidLongObjectIdException;
@@ -152,7 +153,7 @@
 	public void test011_toString() {
 		final String x = "0123456789ABCDEFabcdef01234567890123456789ABCDEFabcdef0123456789";
 		final LongObjectId oid = LongObjectId.fromString(x);
-		assertEquals(x.toLowerCase(), oid.name());
+		assertEquals(x.toLowerCase(Locale.ROOT), oid.name());
 	}
 
 	@Test
@@ -290,7 +291,6 @@
 				"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
 		assertEquals(0, id1.compareTo(LongObjectId.fromString(
 				"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")));
-		assertEquals(0, id1.compareTo(id1));
 
 		assertEquals(-1, id1.compareTo(LongObjectId.fromString(
 				"1123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")));
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/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
index 808ec3a..ede0f7d 100644
--- a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.lfs/BUCK b/org.eclipse.jgit.lfs/BUCK
deleted file mode 100644
index ddb3a10..0000000
--- a/org.eclipse.jgit.lfs/BUCK
+++ /dev/null
@@ -1,17 +0,0 @@
-SRCS = glob(['src/**'])
-RESOURCES = glob(['resources/**'])
-
-java_library(
-  name = 'jgit-lfs',
-  srcs = SRCS,
-  resources = RESOURCES,
-  deps = [
-    '//org.eclipse.jgit:jgit'
-  ],
-  visibility = ['PUBLIC'],
-)
-
-java_sources(
-  name = 'jgit-lfs_src',
-  srcs = SRCS + RESOURCES,
-)
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 d5d1f7c..de2bec6 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.2.qualifier
+Bundle-Version: 4.7.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.lfs;version="4.6.2",
- org.eclipse.jgit.lfs.errors;version="4.6.2",
- org.eclipse.jgit.lfs.internal;version="4.6.2";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
- org.eclipse.jgit.lfs.lib;version="4.6.2"
+Export-Package: org.eclipse.jgit.lfs;version="4.7.2",
+ org.eclipse.jgit.lfs.errors;version="4.7.2",
+ org.eclipse.jgit.lfs.internal;version="4.7.2";x-friends:="org.eclipse.jgit.lfs.test,org.eclipse.jgit.lfs.server.fs,org.eclipse.jgit.lfs.server",
+ org.eclipse.jgit.lfs.lib;version="4.7.2"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.annotations;version="[4.6.2,4.7.0)";resolution:=optional,
- org.eclipse.jgit.attributes;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)"
+Import-Package: org.eclipse.jgit.annotations;version="[4.7.2,4.8.0)";resolution:=optional,
+ org.eclipse.jgit.attributes;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.treewalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,4.8.0)"
diff --git a/org.eclipse.jgit.lfs/pom.xml b/org.eclipse.jgit.lfs/pom.xml
index 049b360..54e4445 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.2-SNAPSHOT</version>
+    <version>4.7.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.lfs</artifactId>
diff --git a/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties b/org.eclipse.jgit.lfs/resources/org/eclipse/jgit/lfs/internal/LfsText.properties
index 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..b78ee04 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
@@ -130,6 +130,7 @@
 		this.aOut = new AtomicObjectOutputStream(tmpFile.toAbsolutePath());
 	}
 
+	@Override
 	public int run() throws IOException {
 		try {
 			byte[] buf = new byte[8192];
@@ -151,7 +152,10 @@
 						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..0f62025 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
@@ -51,6 +51,7 @@
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.StandardCharsets;
 import java.nio.charset.UnsupportedCharsetException;
+import java.util.Locale;
 
 import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
@@ -69,11 +70,17 @@
 	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"
 	 */
 	public static final String HASH_FUNCTION_NAME = Constants.LONG_HASH_FUNCTION
-			.toLowerCase().replace("-", ""); //$NON-NLS-1$ //$NON-NLS-2$
+			.toLowerCase(Locale.ROOT).replace("-", ""); //$NON-NLS-1$ //$NON-NLS-2$
 
 	private AnyLongObjectId oid;
 
@@ -150,14 +157,13 @@
 				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 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/AnyLongObjectId.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
index 1f0df88..caf034d 100644
--- a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/lib/AnyLongObjectId.java
@@ -171,6 +171,7 @@
 	 * @return &lt; 0 if this id comes before other; 0 if this id is equal to
 	 *         other; &gt; 0 if this id comes after other.
 	 */
+	@Override
 	public final int compareTo(final AnyLongObjectId other) {
 		if (this == other)
 			return 0;
@@ -262,6 +263,7 @@
 		return abbr.prefixCompare(this) == 0;
 	}
 
+	@Override
 	public final int hashCode() {
 		return (int) (w1 >> 32);
 	}
@@ -277,6 +279,7 @@
 		return other != null ? equals(this, other) : false;
 	}
 
+	@Override
 	public final boolean equals(final Object o) {
 		if (o instanceof AnyLongObjectId)
 			return equals((AnyLongObjectId) o);
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 @@
 			* 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 85ac88b..c244338 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.2.qualifier"
+      version="4.7.2.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 6f75186..b90c14d 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.2-SNAPSHOT</version>
+    <version>4.7.2-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 e272c0b..0abf4bd 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.2.qualifier"
+      version="4.7.2.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 679f880..af4e71f 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.2-SNAPSHOT</version>
+    <version>4.7.2-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 fe7e3ac..74b9925 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.2.qualifier"
+      version="4.7.2.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 0b0a335..4f66ab6 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.2-SNAPSHOT</version>
+    <version>4.7.2-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 4aefd72..7daa0b2 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.2.qualifier"
+      version="4.7.2.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 893c492..2ea040d 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.2-SNAPSHOT</version>
+    <version>4.7.2-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 81f5e71..546539b 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.2.qualifier"
+      version="4.7.2.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.1" match="equivalent"/>
+      <import feature="org.eclipse.jgit.lfs" version="4.7.1" 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 b6c5846..55d6657 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.2-SNAPSHOT</version>
+    <version>4.7.2-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 444a872..8aa9618 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.2.qualifier"
+      version="4.7.2.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 a35034a..5057957 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.2-SNAPSHOT</version>
+    <version>4.7.2-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 d611d29..c64d62c 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.2-SNAPSHOT</version>
+    <version>4.7.2-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 20ab58c..b3feeb7 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.2.qualifier"
+      version="4.7.2.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 e11e042..e23b722 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.2-SNAPSHOT</version>
+    <version>4.7.2-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 cd39231..65dabc3 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.2.qualifier
+Bundle-Version: 4.7.2.qualifier
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
index d0da0c4..be02826 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.5" sequenceNumber="1502746491">
+<target name="jgit-4.5" sequenceNumber="1502747250">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.3.17.v20170317"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
index 065284d..bcef50b 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.6" sequenceNumber="1502746433">
+<target name="jgit-4.6" sequenceNumber="1502747233">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.3.17.v20170317"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
index d3a1131..52ea6f8 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.7" sequenceNumber="1502746477">
+<target name="jgit-4.7" sequenceNumber="1502747215">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="org.eclipse.jetty.client" version="9.3.17.v20170317"/>
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 652b122..ad92198 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.2-SNAPSHOT</version>
+    <version>4.7.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.target</artifactId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.3.9.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.3.9.tpd
new file mode 100644
index 0000000..d5621a0
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.3.9.tpd
@@ -0,0 +1,20 @@
+target "jetty-9.4.3" with source configurePhase
+
+location jetty-9.4.3 "http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.3.9.v20160517/" {
+	org.eclipse.jetty.client [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.client.source [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.continuation [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.continuation.source [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.http [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.http.source [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.io [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.io.source [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.security [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.security.source [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.server [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.server.source [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.servlet [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.servlet.source [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.util [9.3.9.v20160517,9.3.9.v20160517]
+	org.eclipse.jetty.util.source [9.3.9.v20160517,9.3.9.v20160517]
+}
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 583b128..b284442 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.2-SNAPSHOT</version>
+  <version>4.7.2-SNAPSHOT</version>
   <packaging>pom</packaging>
 
   <name>JGit Tycho Parent</name>
diff --git a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
index 10c29d5..64f7498 100644
--- a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.pgm.test/BUCK b/org.eclipse.jgit.pgm.test/BUCK
deleted file mode 100644
index cd15510..0000000
--- a/org.eclipse.jgit.pgm.test/BUCK
+++ /dev/null
@@ -1,37 +0,0 @@
-TESTS = glob(['tst/**/*.java'])
-
-for t in TESTS:
-  n = t[len('tst/'):len(t)-len('.java')].replace('/', '.')
-  java_test(
-    name = n,
-    labels = ['pgm'],
-    srcs = [t],
-    deps = [
-      ':helpers',
-      '//org.eclipse.jgit:jgit',
-      '//org.eclipse.jgit.archive:jgit-archive',
-      '//org.eclipse.jgit.junit:junit',
-      '//org.eclipse.jgit.pgm:pgm',
-      '//lib:hamcrest-core',
-      '//lib:hamcrest-library',
-      '//lib:javaewah',
-      '//lib:junit',
-      '//lib:slf4j-api',
-      '//lib:slf4j-simple',
-      '//lib:commons-compress',
-      '//lib:tukaani-xz',
-    ],
-    vm_args = ['-Xmx256m', '-Dfile.encoding=UTF-8'],
-  )
-
-java_library(
-  name = 'helpers',
-  srcs = glob(['src/**/*.java']),
-  deps = [
-    '//org.eclipse.jgit:jgit',
-    '//org.eclipse.jgit.pgm:pgm',
-    '//org.eclipse.jgit.junit:junit',
-    '//lib:args4j',
-    '//lib:junit',
-  ],
-)
diff --git a/org.eclipse.jgit.pgm.test/BUILD b/org.eclipse.jgit.pgm.test/BUILD
new file mode 100644
index 0000000..5d4a175
--- /dev/null
+++ b/org.eclipse.jgit.pgm.test/BUILD
@@ -0,0 +1,40 @@
+load(
+    "@com_googlesource_gerrit_bazlets//tools:junit.bzl",
+    "junit_tests",
+)
+
+junit_tests(
+    name = "pgm",
+    srcs = glob(["tst/**/*.java"]),
+    jvm_flags = [
+        "-Xmx256m",
+        "-Dfile.encoding=UTF-8",
+    ],
+    tags = ["pgm"],
+    deps = [
+        ":helpers",
+        "//lib:commons-compress",
+        "//lib:javaewah",
+        "//lib:junit",
+        "//lib:slf4j-api",
+        "//lib:slf4j-simple",
+        "//lib:xz",
+        "//org.eclipse.jgit.archive:jgit-archive",
+        "//org.eclipse.jgit:jgit",
+        "//org.eclipse.jgit.junit:junit",
+        "//org.eclipse.jgit.pgm:pgm",
+    ],
+)
+
+java_library(
+    name = "helpers",
+    testonly = 1,
+    srcs = glob(["src/**/*.java"]),
+    deps = [
+        "//lib:args4j",
+        "//lib:junit",
+        "//org.eclipse.jgit:jgit",
+        "//org.eclipse.jgit.junit:junit",
+        "//org.eclipse.jgit.pgm:pgm",
+    ],
+)
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index 8e7d5ab..3cba803 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.2.qualifier
+Bundle-Version: 4.7.2.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.2,4.7.0)",
- org.eclipse.jgit.api.errors;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.diff;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.dircache;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="4.6.2",
- org.eclipse.jgit.junit;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.merge;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.pgm;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.pgm.internal;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.pgm.opt;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.treewalk;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util.io;version="[4.6.2,4.7.0)",
+Import-Package: org.eclipse.jgit.api;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.api.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.diff;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.dircache;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="4.7.2",
+ org.eclipse.jgit.junit;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.merge;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.pgm;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.pgm.opt;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.storage.file;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.treewalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util.io;version="[4.7.2,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 c8188cb..4cc2eeb 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.2-SNAPSHOT</version>
+    <version>4.7.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.pgm.test</artifactId>
diff --git a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
index b675d3c..0eeabab 100644
--- a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
+++ b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
@@ -79,7 +79,7 @@
 	 * @throws Exception
 	 */
 	protected String[] executeUnchecked(String... cmds) throws Exception {
-		List<String> result = new ArrayList<String>(cmds.length);
+		List<String> result = new ArrayList<>(cmds.length);
 		for (String cmd : cmds) {
 			result.addAll(CLIGitCommand.executeUnchecked(cmd, db));
 		}
@@ -97,7 +97,7 @@
 	 * @throws Exception
 	 */
 	protected String[] execute(String... cmds) throws Exception {
-		List<String> result = new ArrayList<String>(cmds.length);
+		List<String> result = new ArrayList<>(cmds.length);
 		for (String cmd : cmds) {
 			Result r = CLIGitCommand.executeRaw(cmd, db);
 			if (r.ex instanceof TerminatedByHelpException) {
@@ -127,6 +127,7 @@
 		return JGitTestUtil.writeTrashFile(db, name, data);
 	}
 
+	@Override
 	protected String read(final File file) throws IOException {
 		return JGitTestUtil.read(file);
 	}
diff --git a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java
index b08bc8a..69eb198 100644
--- a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java
+++ b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java
@@ -156,6 +156,7 @@
 		return new PrintWriter(result.err);
 	}
 
+	@Override
 	void init(final TextBuiltin cmd) throws IOException {
 		cmd.outs = result.out;
 		cmd.errs = result.err;
@@ -188,7 +189,7 @@
 	 * @return the array
 	 */
 	static String[] split(String commandLine) {
-		final List<String> list = new ArrayList<String>();
+		final List<String> list = new ArrayList<>();
 		boolean inquote = false;
 		boolean inDblQuote = false;
 		StringBuilder r = new StringBuilder();
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java
index 35467c6..6f32bfa 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java
@@ -348,7 +348,7 @@
 		commitBazAndFooSlashBar();
 		byte[] result = CLIGitCommand.executeRaw(
 				"git archive --prefix=x/ --format=zip master", db).outBytes();
-		String[] expect = { "x/baz", "x/foo/", "x/foo/bar" };
+		String[] expect = { "x/", "x/baz", "x/foo/", "x/foo/bar" };
 		String[] actual = listZipEntries(result);
 
 		Arrays.sort(expect);
@@ -361,7 +361,7 @@
 		commitBazAndFooSlashBar();
 		byte[] result = CLIGitCommand.executeRaw(
 				"git archive --prefix=x/ --format=tar master", db).outBytes();
-		String[] expect = { "x/baz", "x/foo/", "x/foo/bar" };
+		String[] expect = { "x/", "x/baz", "x/foo/", "x/foo/bar" };
 		String[] actual = listTarEntries(result);
 
 		Arrays.sort(expect);
@@ -380,7 +380,7 @@
 		commitFoo();
 		byte[] result = CLIGitCommand.executeRaw(
 				"git archive --prefix=x// --format=zip master", db).outBytes();
-		String[] expect = { "x//foo" };
+		String[] expect = { "x/", "x//foo" };
 		assertArrayEquals(expect, listZipEntries(result));
 	}
 
@@ -389,7 +389,7 @@
 		commitFoo();
 		byte[] result = CLIGitCommand.executeRaw(
 				"git archive --prefix=x// --format=tar master", db).outBytes();
-		String[] expect = { "x//foo" };
+		String[] expect = { "x/", "x//foo" };
 		assertArrayEquals(expect, listTarEntries(result));
 	}
 
@@ -529,7 +529,7 @@
 	@Test
 	public void testArchiveWithLongFilename() throws Exception {
 		StringBuilder filename = new StringBuilder();
-		List<String> l = new ArrayList<String>();
+		List<String> l = new ArrayList<>();
 		for (int i = 0; i < 20; i++) {
 			filename.append("1234567890/");
 			l.add(filename.toString());
@@ -549,7 +549,7 @@
 	@Test
 	public void testTarWithLongFilename() throws Exception {
 		StringBuilder filename = new StringBuilder();
-		List<String> l = new ArrayList<String>();
+		List<String> l = new ArrayList<>();
 		for (int i = 0; i < 20; i++) {
 			filename.append("1234567890/");
 			l.add(filename.toString());
@@ -691,7 +691,7 @@
 	}
 
 	private static String[] listZipEntries(byte[] zipData) throws IOException {
-		List<String> l = new ArrayList<String>();
+		List<String> l = new ArrayList<>();
 		ZipInputStream in = new ZipInputStream(
 				new ByteArrayInputStream(zipData));
 
@@ -706,6 +706,7 @@
 		ExecutorService executor = Executors.newSingleThreadExecutor();
 
 		return executor.submit(new Callable<Object>() {
+			@Override
 			public Object call() throws IOException {
 				try {
 					stream.write(data);
@@ -718,7 +719,7 @@
 	}
 
 	private String[] listTarEntries(byte[] tarData) throws Exception {
-		List<String> l = new ArrayList<String>();
+		List<String> l = new ArrayList<>();
 		Process proc = spawnAssumingCommandPresent("tar", "tf", "-");
 		BufferedReader reader = readFromProcess(proc);
 		OutputStream out = proc.getOutputStream();
@@ -749,7 +750,7 @@
 				continue;
 
 			// found!
-			List<String> l = new ArrayList<String>();
+			List<String> l = new ArrayList<>();
 			BufferedReader reader = new BufferedReader(
 					new InputStreamReader(in, "UTF-8"));
 			String line;
@@ -764,7 +765,7 @@
 
 	private String[] tarEntryContent(byte[] tarData, String path)
 			throws Exception {
-		List<String> l = new ArrayList<String>();
+		List<String> l = new ArrayList<>();
 		Process proc = spawnAssumingCommandPresent("tar", "Oxf", "-", path);
 		BufferedReader reader = readFromProcess(proc);
 		OutputStream out = proc.getOutputStream();
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
index 23aa97e..0ce6451 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
@@ -73,7 +73,8 @@
 				.equals("Mac OS X");
 
 		String[] output = execute("git config --list");
-		List<String> expect = new ArrayList<String>();
+		List<String> expect = new ArrayList<>();
+		expect.add("gc.autoDetach=false");
 		expect.add("core.filemode=" + !isWindows);
 		expect.add("core.logallrefupdates=true");
 		if (isMac)
diff --git a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
index 4f1759f..06ddbab 100644
--- a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.pgm/BUCK b/org.eclipse.jgit.pgm/BUCK
deleted file mode 100644
index 5d5e6f7..0000000
--- a/org.eclipse.jgit.pgm/BUCK
+++ /dev/null
@@ -1,81 +0,0 @@
-include_defs('//tools/git.defs')
-
-java_library(
-  name = 'pgm',
-  srcs = glob(['src/**']),
-  resources = glob(['resources/**']),
-  deps = [
-    ':services',
-    '//org.eclipse.jgit:jgit',
-    '//org.eclipse.jgit.archive:jgit-archive',
-    '//org.eclipse.jgit.http.apache:http-apache',
-    '//org.eclipse.jgit.lfs:jgit-lfs',
-    '//org.eclipse.jgit.lfs.server:jgit-lfs-server',
-    '//org.eclipse.jgit.ui:ui',
-    '//lib:args4j',
-    '//lib:httpcomponents',
-    '//lib:httpcore',
-    '//lib/jetty:http',
-    '//lib/jetty:io',
-    '//lib/jetty:server',
-    '//lib/jetty:servlet',
-    '//lib/jetty:security',
-    '//lib/jetty:util',
-    '//lib:servlet-api'
-  ],
-  visibility = ['PUBLIC'],
-)
-
-prebuilt_jar(
-  name = 'services',
-  binary_jar = ':services__jar',
-)
-
-genrule(
-  name = 'services__jar',
-  cmd = 'cd $SRCDIR ; zip -qr $OUT .',
-  srcs = glob(['META-INF/services/*']),
-  out = 'services.jar',
-)
-
-genrule(
-  name = 'jgit',
-  cmd = ''.join([
-    'mkdir $TMP/META-INF &&',
-    'cp $(location :binary_manifest) $TMP/META-INF/MANIFEST.MF &&',
-    'cp $(location :jgit_jar) $TMP/jgit.jar &&',
-    'cd $TMP && zip $TMP/jgit.jar META-INF/MANIFEST.MF &&',
-    'cat $SRCDIR/jgit.sh $TMP/jgit.jar >$OUT &&',
-    'chmod a+x $OUT',
-  ]),
-  srcs = ['jgit.sh'],
-  out = 'jgit',
-  visibility = ['PUBLIC'],
-)
-
-java_binary(
-  name = 'jgit_jar',
-  deps = [
-    ':pgm',
-    '//lib:slf4j-simple',
-    '//lib:tukaani-xz',
-  ],
-  blacklist = [
-    'META-INF/DEPENDENCIES',
-    'META-INF/maven/.*',
-  ],
-)
-
-genrule(
-  name = 'binary_manifest',
-  cmd = ';'.join(['echo "%s: %s" >>$OUT' % e for e in [
-    ('Manifest-Version', '1.0'),
-    ('Main-Class', 'org.eclipse.jgit.pgm.Main'),
-    ('Bundle-Version', git_version()),
-    ('Implementation-Title', 'JGit Command Line Interface'),
-    ('Implementation-Vendor', 'Eclipse.org - JGit'),
-    ('Implementation-Vendor-URL', 'http://www.eclipse.org/jgit/'),
-    ('Implementation-Vendor-Id', 'org.eclipse.jgit'),
-  ]] + ['echo >>$OUT']),
-  out = 'MANIFEST.MF',
-)
diff --git a/org.eclipse.jgit.pgm/BUILD b/org.eclipse.jgit.pgm/BUILD
new file mode 100644
index 0000000..6d32790
--- /dev/null
+++ b/org.eclipse.jgit.pgm/BUILD
@@ -0,0 +1,38 @@
+java_library(
+    name = "pgm",
+    srcs = glob(["src/**"]),
+    resource_strip_prefix = "org.eclipse.jgit.pgm/resources",
+    resources = glob(["resources/**"]),
+    visibility = ["//visibility:public"],
+    deps = [
+        ":services",
+        "//lib:args4j",
+        "//lib:httpclient",
+        "//lib:httpcore",
+        "//lib:jetty-http",
+        "//lib:jetty-io",
+        "//lib:jetty-security",
+        "//lib:jetty-server",
+        "//lib:jetty-servlet",
+        "//lib:jetty-util",
+        "//lib:servlet-api",
+        "//org.eclipse.jgit.archive:jgit-archive",
+        "//org.eclipse.jgit.http.apache:http-apache",
+        "//org.eclipse.jgit:jgit",
+        "//org.eclipse.jgit.lfs:jgit-lfs",
+        "//org.eclipse.jgit.lfs.server:jgit-lfs-server",
+        "//org.eclipse.jgit.ui:ui",
+    ],
+)
+
+java_import(
+    name = "services",
+    jars = [":services_jar"],
+)
+
+genrule(
+    name = "services_jar",
+    srcs = glob(["META-INF/services/*"]),
+    outs = ["services_jar.jar"],
+    cmd = "r=$$PWD && cd org.eclipse.jgit.pgm && zip -qr $$r/$@ .",
+)
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index 5c30da7..7a8b1e7 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.2.qualifier
+Bundle-Version: 4.7.2.qualifier
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
 Bundle-Localization: plugin
@@ -27,46 +27,46 @@
  org.eclipse.jetty.util.log;version="[9.0.0,9.4.0)",
  org.eclipse.jetty.util.security;version="[9.0.0,9.4.0)",
  org.eclipse.jetty.util.thread;version="[9.0.0,9.4.0)",
- org.eclipse.jgit.api;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.api.errors;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.archive;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.awtui;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.blame;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.diff;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.dircache;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.errors;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.gitrepo;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.ketch;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs.server;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs.server.fs;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs.server.s3;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.merge;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.notes;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.revplot;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.revwalk.filter;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.storage.pack;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport.http.apache;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport.resolver;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.treewalk;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util.io;version="[4.6.2,4.7.0)",
+ org.eclipse.jgit.api;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.api.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.archive;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.awtui;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.blame;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.diff;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.dircache;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.gitrepo;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.ketch;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs.server;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs.server.fs;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs.server.s3;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.merge;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.notes;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revplot;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.storage.file;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.storage.pack;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.treewalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util.io;version="[4.7.2,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.2";
+Export-Package: org.eclipse.jgit.console;version="4.7.2";
   uses:="org.eclipse.jgit.transport,
    org.eclipse.jgit.util",
- org.eclipse.jgit.pgm;version="4.6.2";
+ org.eclipse.jgit.pgm;version="4.7.2";
   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.2";
+ org.eclipse.jgit.pgm.debug;version="4.7.2";
   uses:="org.eclipse.jgit.util.io,
    org.eclipse.jgit.pgm",
- org.eclipse.jgit.pgm.internal;version="4.6.2";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="4.6.2";
+ org.eclipse.jgit.pgm.internal;version="4.7.2";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
+ org.eclipse.jgit.pgm.opt;version="4.7.2";
   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 443ccc8..913c8f4 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.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.6.2.qualifier";roots="."
+Bundle-Version: 4.7.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.7.2.qualifier";roots="."
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index c6f4aca..8bad5ce 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.2-SNAPSHOT</version>
+    <version>4.7.2-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/Add.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java
index c36c485..3c13590 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java
@@ -58,7 +58,7 @@
 	private boolean update = false;
 
 	@Argument(required = true, metaVar = "metaVar_filepattern", usage = "usage_filesToAddContentFrom")
-	private List<String> filepatterns = new ArrayList<String>();
+	private List<String> filepatterns = new ArrayList<>();
 
 	@Override
 	protected void run() throws Exception {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
index 0f54171..4193254 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
@@ -114,7 +114,7 @@
 	private String rangeString;
 
 	@Option(name = "--reverse", metaVar = "metaVar_blameReverse", usage = "usage_blameReverse")
-	private List<RevCommit> reverseRange = new ArrayList<RevCommit>(2);
+	private List<RevCommit> reverseRange = new ArrayList<>(2);
 
 	@Argument(index = 0, required = false, metaVar = "metaVar_revision")
 	private String revision;
@@ -124,7 +124,7 @@
 
 	private ObjectReader reader;
 
-	private final Map<RevCommit, String> abbreviatedCommits = new HashMap<RevCommit, String>();
+	private final Map<RevCommit, String> abbreviatedCommits = new HashMap<>();
 
 	private SimpleDateFormat dateFmt;
 
@@ -163,7 +163,7 @@
 
 			if (!reverseRange.isEmpty()) {
 				RevCommit rangeStart = null;
-				List<RevCommit> rangeEnd = new ArrayList<RevCommit>(2);
+				List<RevCommit> rangeEnd = new ArrayList<>(2);
 				for (RevCommit c : reverseRange) {
 					if (c.has(RevFlag.UNINTERESTING))
 						rangeStart = c;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java
index 5f3740c..f6e3810 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java
@@ -149,7 +149,7 @@
 	@Argument(metaVar = "metaVar_name")
 	private String branch;
 
-	private final Map<String, Ref> printRefs = new LinkedHashMap<String, Ref>();
+	private final Map<String, Ref> printRefs = new LinkedHashMap<>();
 
 	/** Only set for verbose branch listing at-the-moment */
 	private RevWalk rw;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
index 0c3b720..c2f3c46 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
@@ -78,7 +78,7 @@
 	private String name;
 
 	@Option(name = "--", metaVar = "metaVar_paths", multiValued = true, handler = RestOfArgumentsHandler.class)
-	private List<String> paths = new ArrayList<String>();
+	private List<String> paths = new ArrayList<>();
 
 	@Override
 	protected void run() throws Exception {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java
index 2673cc8..cf4c6e3 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/CommandCatalog.java
@@ -100,7 +100,7 @@
 	 * @return all common commands, sorted by command name.
 	 */
 	public static CommandRef[] common() {
-		final ArrayList<CommandRef> common = new ArrayList<CommandRef>();
+		final ArrayList<CommandRef> common = new ArrayList<>();
 		for (final CommandRef c : INSTANCE.commands.values())
 			if (c.isCommon())
 				common.add(c);
@@ -110,6 +110,7 @@
 	private static CommandRef[] toSortedArray(final Collection<CommandRef> c) {
 		final CommandRef[] r = c.toArray(new CommandRef[c.size()]);
 		Arrays.sort(r, new Comparator<CommandRef>() {
+			@Override
 			public int compare(final CommandRef o1, final CommandRef o2) {
 				return o1.getName().compareTo(o2.getName());
 			}
@@ -123,7 +124,7 @@
 
 	private CommandCatalog() {
 		ldr = Thread.currentThread().getContextClassLoader();
-		commands = new HashMap<String, CommandRef>();
+		commands = new HashMap<>();
 
 		final Enumeration<URL> catalogs = catalogs();
 		while (catalogs.hasMoreElements())
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java
index 2cfbd86..befc4ec 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java
@@ -75,7 +75,7 @@
 	private boolean amend;
 
 	@Argument(metaVar = "metaVar_commitPaths", usage = "usage_CommitPaths")
-	private List<String> paths = new ArrayList<String>();
+	private List<String> paths = new ArrayList<>();
 
 	@Override
 	protected void run() throws NoHeadException, NoMessageException,
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
index a7bdde9..1008593 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Daemon.java
@@ -87,16 +87,16 @@
 	int timeout = -1;
 
 	@Option(name = "--enable", metaVar = "metaVar_service", usage = "usage_enableTheServiceInAllRepositories", multiValued = true)
-	final List<String> enable = new ArrayList<String>();
+	final List<String> enable = new ArrayList<>();
 
 	@Option(name = "--disable", metaVar = "metaVar_service", usage = "usage_disableTheServiceInAllRepositories", multiValued = true)
-	final List<String> disable = new ArrayList<String>();
+	final List<String> disable = new ArrayList<>();
 
 	@Option(name = "--allow-override", metaVar = "metaVar_service", usage = "usage_configureTheServiceInDaemonServicename", multiValued = true)
-	final List<String> canOverride = new ArrayList<String>();
+	final List<String> canOverride = new ArrayList<>();
 
 	@Option(name = "--forbid-override", metaVar = "metaVar_service", usage = "usage_configureTheServiceInDaemonServicename", multiValued = true)
-	final List<String> forbidOverride = new ArrayList<String>();
+	final List<String> forbidOverride = new ArrayList<>();
 
 	@Option(name = "--export-all", usage = "usage_exportWithoutGitDaemonExportOk")
 	boolean exportAll;
@@ -109,7 +109,7 @@
 	}
 
 	@Argument(required = true, metaVar = "metaVar_directory", usage = "usage_directoriesToExport")
-	final List<File> directory = new ArrayList<File>();
+	final List<File> directory = new ArrayList<>();
 
 	@Override
 	protected boolean requiresRepository() {
@@ -139,7 +139,7 @@
 		if (1 < threads)
 			packConfig.setExecutor(Executors.newFixedThreadPool(threads));
 
-		final FileResolver<DaemonClient> resolver = new FileResolver<DaemonClient>();
+		final FileResolver<DaemonClient> resolver = new FileResolver<>();
 		for (final File f : directory) {
 			outw.println(MessageFormat.format(CLIText.get().exporting, f.getAbsolutePath()));
 			resolver.exportDirectory(f);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTree.java
index 95c2132..56b6241 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTree.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTree.java
@@ -67,7 +67,7 @@
 	}
 
 	@Argument(index = 1, metaVar = "metaVar_treeish", required = true)
-	private final List<AbstractTreeIterator> trees = new ArrayList<AbstractTreeIterator>();
+	private final List<AbstractTreeIterator> trees = new ArrayList<>();
 
 	@Option(name = "--", metaVar = "metaVar_path", multiValued = true, handler = PathTreeFilterHandler.class)
 	private TreeFilter pathFilter = TreeFilter.ALL;
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 @@
 	@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/Glog.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java
index f07c3ca..7b71575 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Glog.java
@@ -88,6 +88,7 @@
 		final JButton repaint = new JButton();
 		repaint.setText(CLIText.get().repaint);
 		repaint.addActionListener(new ActionListener() {
+			@Override
 			public void actionPerformed(ActionEvent e) {
 				graphPane.repaint();
 			}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
index 62e7728..1108ddd 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
@@ -53,6 +53,7 @@
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
@@ -93,7 +94,7 @@
 	@Option(name = "--no-standard-notes", usage = "usage_noShowStandardNotes")
 	private boolean noStandardNotes;
 
-	private List<String> additionalNoteRefs = new ArrayList<String>();
+	private List<String> additionalNoteRefs = new ArrayList<>();
 
 	@Option(name = "--show-notes", usage = "usage_showNotes", metaVar = "metaVar_ref")
 	void addAdditionalNoteRef(String notesRef) {
@@ -102,8 +103,8 @@
 
 	@Option(name = "--date", usage = "usage_date")
 	void dateFormat(String date) {
-		if (date.toLowerCase().equals(date))
-			date = date.toUpperCase();
+		if (date.toLowerCase(Locale.ROOT).equals(date))
+			date = date.toUpperCase(Locale.ROOT);
 		dateFormatter = new GitDateFormatter(Format.valueOf(date));
 	}
 
@@ -203,7 +204,7 @@
 
 			if (!noStandardNotes || !additionalNoteRefs.isEmpty()) {
 				createWalk();
-				noteMaps = new LinkedHashMap<String, NoteMap>();
+				noteMaps = new LinkedHashMap<>();
 				if (!noStandardNotes) {
 					addNoteMap(Constants.R_NOTES_COMMITS);
 				}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java
index 6262ad2..7a5f3d8 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsRemote.java
@@ -74,8 +74,9 @@
 	protected void run() throws Exception {
 		LsRemoteCommand command = Git.lsRemoteRepository().setRemote(remote)
 				.setTimeout(timeout).setHeads(heads).setTags(tags);
-		TreeSet<Ref> refs = new TreeSet<Ref>(new Comparator<Ref>() {
+		TreeSet<Ref> refs = new TreeSet<>(new Comparator<Ref>() {
 
+			@Override
 			public int compare(Ref r1, Ref r2) {
 				return r1.getName().compareTo(r2.getName());
 			}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java
index 872ea67..02d61e5 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java
@@ -68,7 +68,7 @@
 
 	@Argument(index = 1)
 	@Option(name = "--", metaVar = "metaVar_paths", multiValued = true, handler = StopOptionHandler.class)
-	private List<String> paths = new ArrayList<String>();
+	private List<String> paths = new ArrayList<>();
 
 	@Override
 	protected void run() throws Exception {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
index a0f4d06..3addecb 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
@@ -53,6 +53,7 @@
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 
 import org.eclipse.jgit.awtui.AwtAuthenticator;
 import org.eclipse.jgit.awtui.AwtCredentialsProvider;
@@ -90,7 +91,7 @@
 	private TextBuiltin subcommand;
 
 	@Argument(index = 1, metaVar = "metaVar_arg")
-	private List<String> arguments = new ArrayList<String>();
+	private List<String> arguments = new ArrayList<>();
 
 	PrintWriter writer;
 
@@ -240,7 +241,8 @@
 		}
 
 		if (version) {
-			String cmdId = Version.class.getSimpleName().toLowerCase();
+			String cmdId = Version.class.getSimpleName()
+					.toLowerCase(Locale.ROOT);
 			subcommand = CommandCatalog.get(cmdId).create();
 		}
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java
index 9dcd512..f8bae1d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MergeBase.java
@@ -63,7 +63,7 @@
 	}
 
 	@Argument(index = 1, metaVar = "metaVar_commitish", required = true)
-	private final List<RevCommit> commits = new ArrayList<RevCommit>();
+	private final List<RevCommit> commits = new ArrayList<>();
 
 	@Override
 	protected void run() throws Exception {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java
index 98af186..1b805d1 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java
@@ -77,7 +77,7 @@
 	private String remote = Constants.DEFAULT_REMOTE_NAME;
 
 	@Argument(index = 1, metaVar = "metaVar_refspec")
-	private final List<RefSpec> refSpecs = new ArrayList<RefSpec>();
+	private final List<RefSpec> refSpecs = new ArrayList<>();
 
 	@Option(name = "--all")
 	private boolean all;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java
index 6833ad3..a66b7fa 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java
@@ -68,7 +68,7 @@
 	boolean verify;
 
 	@Argument(index = 0, metaVar = "metaVar_commitish")
-	private final List<ObjectId> commits = new ArrayList<ObjectId>();
+	private final List<ObjectId> commits = new ArrayList<>();
 
 	@Override
 	protected void run() throws Exception {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevWalkTextBuiltin.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevWalkTextBuiltin.java
index 1543586..74135e4 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevWalkTextBuiltin.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevWalkTextBuiltin.java
@@ -124,12 +124,12 @@
 	private String followPath;
 
 	@Argument(index = 0, metaVar = "metaVar_commitish")
-	private final List<RevCommit> commits = new ArrayList<RevCommit>();
+	private final List<RevCommit> commits = new ArrayList<>();
 
 	@Option(name = "--", metaVar = "metaVar_path", multiValued = true, handler = PathTreeFilterHandler.class)
 	protected TreeFilter pathFilter = TreeFilter.ALL;
 
-	private final List<RevFilter> revLimiter = new ArrayList<RevFilter>();
+	private final List<RevFilter> revLimiter = new ArrayList<>();
 
 	@Option(name = "--author")
 	void addAuthorRevFilter(final String who) {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java
index f4f864b..79c3f09 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java
@@ -58,7 +58,7 @@
 	@Argument(metaVar = "metaVar_path", usage = "usage_path", multiValued = true, required = true)
 
 	@Option(name = "--", handler = StopOptionHandler.class)
-	private List<String> paths = new ArrayList<String>();
+	private List<String> paths = new ArrayList<>();
 
 
 	@Override
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
index 43b292e..b7f5e58 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
@@ -117,7 +117,7 @@
 		Map<String, StageState> conflicting = status.getConflictingStageState();
 
 		// build a sorted list of all paths except untracked and ignored
-		TreeSet<String> sorted = new TreeSet<String>();
+		TreeSet<String> sorted = new TreeSet<>();
 		sorted.addAll(added);
 		sorted.addAll(changed);
 		sorted.addAll(removed);
@@ -185,7 +185,7 @@
 
 		// untracked are always at the end of the list
 		if ("all".equals(untrackedFilesMode)) { //$NON-NLS-1$
-			TreeSet<String> untracked = new TreeSet<String>(
+			TreeSet<String> untracked = new TreeSet<>(
 					status.getUntracked());
 			for (String path : untracked)
 				printPorcelainLine('?', '?', path);
@@ -221,7 +221,7 @@
 		Collection<String> untracked = status.getUntracked();
 		Map<String, StageState> unmergedStates = status
 				.getConflictingStageState();
-		Collection<String> toBeCommitted = new ArrayList<String>(added);
+		Collection<String> toBeCommitted = new ArrayList<>(added);
 		toBeCommitted.addAll(changed);
 		toBeCommitted.addAll(removed);
 		int nbToBeCommitted = toBeCommitted.size();
@@ -232,7 +232,7 @@
 					toBeCommitted, added, changed, removed);
 			firstHeader = false;
 		}
-		Collection<String> notStagedForCommit = new ArrayList<String>(modified);
+		Collection<String> notStagedForCommit = new ArrayList<>(modified);
 		notStagedForCommit.addAll(missing);
 		int nbNotStagedForCommit = notStagedForCommit.size();
 		if (nbNotStagedForCommit > 0) {
@@ -274,7 +274,7 @@
 
 	protected int printList(Collection<String> list) throws IOException {
 		if (!list.isEmpty()) {
-			List<String> sortedList = new ArrayList<String>(list);
+			List<String> sortedList = new ArrayList<>(list);
 			java.util.Collections.sort(sortedList);
 			for (String filename : sortedList) {
 				outw.println(CLIText.formatLine(String.format(
@@ -291,7 +291,7 @@
 			Collection<String> set2,
 			@SuppressWarnings("unused") Collection<String> set3)
 			throws IOException {
-		List<String> sortedList = new ArrayList<String>(list);
+		List<String> sortedList = new ArrayList<>(list);
 		java.util.Collections.sort(sortedList);
 		for (String filename : sortedList) {
 			String prefix;
@@ -311,7 +311,7 @@
 
 	private void printUnmerged(Map<String, StageState> unmergedStates)
 			throws IOException {
-		List<String> paths = new ArrayList<String>(unmergedStates.keySet());
+		List<String> paths = new ArrayList<>(unmergedStates.keySet());
 		Collections.sort(paths);
 		for (String path : paths) {
 			StageState state = unmergedStates.get(path);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
index 05d094f..44ec3f4 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
@@ -84,12 +84,14 @@
 class DiffAlgorithms extends TextBuiltin {
 
 	final Algorithm myers = new Algorithm() {
+		@Override
 		DiffAlgorithm create() {
 			return MyersDiff.INSTANCE;
 		}
 	};
 
 	final Algorithm histogram = new Algorithm() {
+		@Override
 		DiffAlgorithm create() {
 			HistogramDiff d = new HistogramDiff();
 			d.setFallbackAlgorithm(null);
@@ -98,6 +100,7 @@
 	};
 
 	final Algorithm histogram_myers = new Algorithm() {
+		@Override
 		DiffAlgorithm create() {
 			HistogramDiff d = new HistogramDiff();
 			d.setFallbackAlgorithm(MyersDiff.INSTANCE);
@@ -112,13 +115,13 @@
 	//
 
 	@Option(name = "--algorithm", multiValued = true, metaVar = "NAME", usage = "Enable algorithm(s)")
-	List<String> algorithms = new ArrayList<String>();
+	List<String> algorithms = new ArrayList<>();
 
 	@Option(name = "--text-limit", metaVar = "LIMIT", usage = "Maximum size in KiB to scan per file revision")
 	int textLimit = 15 * 1024; // 15 MiB as later we do * 1024.
 
 	@Option(name = "--repository", aliases = { "-r" }, multiValued = true, metaVar = "GIT_DIR", usage = "Repository to scan")
-	List<File> gitDirs = new ArrayList<File>();
+	List<File> gitDirs = new ArrayList<>();
 
 	@Option(name = "--count", metaVar = "LIMIT", usage = "Number of file revisions to be compared")
 	int count = 0; // unlimited
@@ -234,6 +237,7 @@
 		}
 
 		Collections.sort(all, new Comparator<Test>() {
+			@Override
 			public int compare(Test a, Test b) {
 				int result = Long.signum(a.runningTimeNanos - b.runningTimeNanos);
 				if (result == 0) {
@@ -320,7 +324,7 @@
 	}
 
 	private List<Test> init() {
-		List<Test> all = new ArrayList<Test>();
+		List<Test> all = new ArrayList<>();
 
 		try {
 			for (Field f : DiffAlgorithms.class.getDeclaredFields()) {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/LfsStore.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/LfsStore.java
index 52b6d19..5839f33 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/LfsStore.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/LfsStore.java
@@ -219,6 +219,7 @@
 		return false;
 	}
 
+	@Override
 	protected void run() throws Exception {
 		AppServer server = new AppServer(port);
 		URI baseURI = server.getURI();
@@ -254,7 +255,7 @@
 
 			@Override
 			protected LargeFileRepository getLargeFileRepository(
-					LfsRequest request, String path) {
+					LfsRequest request, String path, String auth) {
 				return repository;
 			}
 		};
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
index 8cfcba9..da602d0 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
@@ -112,7 +112,7 @@
 
 	private final ProgressMonitor pm = new TextProgressMonitor(errw);
 
-	private Map<ObjectId, ObjectId> rewrites = new HashMap<ObjectId, ObjectId>();
+	private Map<ObjectId, ObjectId> rewrites = new HashMap<>();
 
 	@Override
 	protected void run() throws Exception {
@@ -137,8 +137,8 @@
 	}
 
 	private void recreateCommitGraph() throws IOException {
-		final Map<ObjectId, ToRewrite> toRewrite = new HashMap<ObjectId, ToRewrite>();
-		List<ToRewrite> queue = new ArrayList<ToRewrite>();
+		final Map<ObjectId, ToRewrite> toRewrite = new HashMap<>();
+		List<ToRewrite> queue = new ArrayList<>();
 		try (RevWalk rw = new RevWalk(db);
 				final BufferedReader br = new BufferedReader(
 						new InputStreamReader(new FileInputStream(graph),
@@ -176,7 +176,7 @@
 			while (!queue.isEmpty()) {
 				final ListIterator<ToRewrite> itr = queue
 						.listIterator(queue.size());
-				queue = new ArrayList<ToRewrite>();
+				queue = new ArrayList<>();
 				REWRITE: while (itr.hasPrevious()) {
 					final ToRewrite t = itr.previous();
 					final ObjectId[] newParents = new ObjectId[t.oldParents.length];
@@ -278,7 +278,7 @@
 	}
 
 	private Map<String, Ref> computeNewRefs() throws IOException {
-		final Map<String, Ref> refs = new HashMap<String, Ref>();
+		final Map<String, Ref> refs = new HashMap<>();
 		try (RevWalk rw = new RevWalk(db);
 				BufferedReader br = new BufferedReader(
 						new InputStreamReader(new FileInputStream(refList),
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCommands.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCommands.java
index aa25807..415c7d3 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCommands.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowCommands.java
@@ -85,6 +85,7 @@
 	static enum Format {
 		/** */
 		USAGE {
+			@Override
 			void print(ThrowingPrintWriter err, final CommandRef c) throws IOException {
 				String usage = c.getUsage();
 				if (usage != null && usage.length() > 0)
@@ -94,6 +95,7 @@
 
 		/** */
 		CLASSES {
+			@Override
 			void print(ThrowingPrintWriter err, final CommandRef c) throws IOException {
 				err.print(c.getImplementationClassName());
 			}
@@ -101,6 +103,7 @@
 
 		/** */
 		URLS {
+			@Override
 			void print(ThrowingPrintWriter err, final CommandRef c) throws IOException {
 				final ClassLoader ldr = c.getImplementationClassLoader();
 
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
index 28d92ae..0eb4e05 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
@@ -251,16 +251,16 @@
 	//
 
 	@Option(name = "--hash", multiValued = true, metaVar = "NAME", usage = "Enable hash function(s)")
-	List<String> hashFunctions = new ArrayList<String>();
+	List<String> hashFunctions = new ArrayList<>();
 
 	@Option(name = "--fold", multiValued = true, metaVar = "NAME", usage = "Enable fold function(s)")
-	List<String> foldFunctions = new ArrayList<String>();
+	List<String> foldFunctions = new ArrayList<>();
 
 	@Option(name = "--text-limit", metaVar = "LIMIT", usage = "Maximum size in KiB to scan")
 	int textLimit = 15 * 1024; // 15 MiB as later we do * 1024.
 
 	@Option(name = "--repository", aliases = { "-r" }, multiValued = true, metaVar = "GIT_DIR", usage = "Repository to scan")
-	List<File> gitDirs = new ArrayList<File>();
+	List<File> gitDirs = new ArrayList<>();
 
 	@Override
 	protected boolean requiresRepository() {
@@ -327,7 +327,7 @@
 				RawText txt = new RawText(raw);
 				int[] lines = new int[txt.size()];
 				int cnt = 0;
-				HashSet<Line> u = new HashSet<Line>();
+				HashSet<Line> u = new HashSet<>();
 				for (int i = 0; i < txt.size(); i++) {
 					if (u.add(new Line(txt, i)))
 						lines[cnt++] = i;
@@ -386,8 +386,8 @@
 	}
 
 	private List<Function> init() {
-		List<Hash> hashes = new ArrayList<Hash>();
-		List<Fold> folds = new ArrayList<Fold>();
+		List<Hash> hashes = new ArrayList<>();
+		List<Fold> folds = new ArrayList<>();
 
 		try {
 			for (Field f : TextHashFunctions.class.getDeclaredFields()) {
@@ -410,7 +410,7 @@
 			throw new RuntimeException("Cannot determine names", e); //$NON-NLS-1$
 		}
 
-		List<Function> all = new ArrayList<Function>();
+		List<Function> all = new ArrayList<>();
 		for (Hash cmp : hashes) {
 			if (include(cmp.name, hashFunctions)) {
 				for (Fold f : folds) {
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 @@
 	 * @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 @@
 	 * @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.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
index b531ba6..020b625 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
@@ -141,7 +141,7 @@
 
 	@Override
 	public void parseArgument(final String... args) throws CmdLineException {
-		final ArrayList<String> tmp = new ArrayList<String>(args.length);
+		final ArrayList<String> tmp = new ArrayList<>(args.length);
 		for (int argi = 0; argi < args.length; argi++) {
 			final String str = args[argi];
 			if (str.equals("--")) { //$NON-NLS-1$
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/PathTreeFilterHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/PathTreeFilterHandler.java
index e468023..b873c3d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/PathTreeFilterHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/PathTreeFilterHandler.java
@@ -81,7 +81,7 @@
 
 	@Override
 	public int parseArguments(final Parameters params) throws CmdLineException {
-		final List<PathFilter> filters = new ArrayList<PathFilter>();
+		final List<PathFilter> filters = new ArrayList<>();
 		for (int idx = 0;; idx++) {
 			final String path;
 			try {
diff --git a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
index 10c29d5..64f7498 100644
--- a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.test/BUCK b/org.eclipse.jgit.test/BUCK
deleted file mode 100644
index 5b7be89..0000000
--- a/org.eclipse.jgit.test/BUCK
+++ /dev/null
@@ -1,95 +0,0 @@
-PKG = 'tst/org/eclipse/jgit/'
-HELPERS = glob(['src/**/*.java']) + [PKG + c for c in [
-  'api/AbstractRemoteCommandTest.java',
-  'diff/AbstractDiffTestCase.java',
-  'internal/storage/file/GcTestCase.java',
-  'internal/storage/file/PackIndexTestCase.java',
-  'internal/storage/file/XInputStream.java',
-  'nls/GermanTranslatedBundle.java',
-  'nls/MissingPropertyBundle.java',
-  'nls/NoPropertiesBundle.java',
-  'nls/NonTranslatedBundle.java',
-  'revwalk/RevQueueTestCase.java',
-  'revwalk/RevWalkTestCase.java',
-  'transport/SpiTransport.java',
-  'treewalk/FileTreeIteratorWithTimeControl.java',
-  'treewalk/filter/AlwaysCloneTreeFilter.java',
-  'test/resources/SampleDataRepositoryTestCase.java',
-  'util/CPUTimeStopWatch.java',
-  'util/io/Strings.java',
-]]
-
-DATA = [
-  PKG + 'lib/empty.gitindex.dat',
-  PKG + 'lib/sorttest.gitindex.dat',
-]
-
-TESTS = glob(
-  ['tst/**/*.java'],
-  excludes = HELPERS + DATA,
-)
-
-DEPS = {
-  PKG + 'nls/RootLocaleTest.java': [
-    '//org.eclipse.jgit.pgm:pgm',
-    '//org.eclipse.jgit.ui:ui',
-  ],
-}
-
-for src in TESTS:
-  name = src[len('tst/'):len(src)-len('.java')].replace('/', '.')
-  labels = []
-  if name.startswith('org.eclipse.jgit.'):
-    l = name[len('org.eclipse.jgit.'):]
-    if l.startswith('internal.storage.'):
-      l = l[len('internal.storage.'):]
-    i = l.find('.')
-    if i > 0:
-      labels.append(l[:i])
-    else:
-      labels.append(i)
-  if 'lib' not in labels:
-    labels.append('lib')
-
-  java_test(
-    name = name,
-    labels = labels,
-    srcs = [src],
-    deps = [
-      ':helpers',
-      ':tst_rsrc',
-      '//org.eclipse.jgit:jgit',
-      '//org.eclipse.jgit.junit:junit',
-      '//org.eclipse.jgit.lfs:jgit-lfs',
-      '//lib:hamcrest-core',
-      '//lib:hamcrest-library',
-      '//lib:javaewah',
-      '//lib:junit',
-      '//lib:slf4j-api',
-      '//lib:slf4j-simple',
-    ] + DEPS.get(src, []),
-    vm_args = ['-Xmx256m', '-Dfile.encoding=UTF-8'],
-  )
-
-java_library(
-  name = 'helpers',
-  srcs = HELPERS,
-  resources = DATA,
-  deps = [
-    '//org.eclipse.jgit:jgit',
-    '//org.eclipse.jgit.junit:junit',
-    '//lib:junit',
-  ],
-)
-
-prebuilt_jar(
-  name = 'tst_rsrc',
-  binary_jar = ':tst_rsrc_jar',
-)
-
-genrule(
-  name = 'tst_rsrc_jar',
-  cmd = 'cd $SRCDIR/tst-rsrc ; zip -qr $OUT .',
-  srcs = glob(['tst-rsrc/**']),
-  out = 'tst_rsrc.jar',
-)
diff --git a/org.eclipse.jgit.test/BUILD b/org.eclipse.jgit.test/BUILD
new file mode 100644
index 0000000..ae6f242
--- /dev/null
+++ b/org.eclipse.jgit.test/BUILD
@@ -0,0 +1,61 @@
+load(":tests.bzl", "tests")
+load(
+    "@com_googlesource_gerrit_bazlets//tools:genrule2.bzl",
+    "genrule2",
+)
+
+PKG = "tst/org/eclipse/jgit/"
+
+HELPERS = glob(["src/**/*.java"]) + [PKG + c for c in [
+    "api/AbstractRemoteCommandTest.java",
+    "diff/AbstractDiffTestCase.java",
+    "internal/storage/file/GcTestCase.java",
+    "internal/storage/file/PackIndexTestCase.java",
+    "internal/storage/file/XInputStream.java",
+    "nls/GermanTranslatedBundle.java",
+    "nls/MissingPropertyBundle.java",
+    "nls/NoPropertiesBundle.java",
+    "nls/NonTranslatedBundle.java",
+    "revwalk/RevQueueTestCase.java",
+    "revwalk/RevWalkTestCase.java",
+    "transport/SpiTransport.java",
+    "treewalk/FileTreeIteratorWithTimeControl.java",
+    "treewalk/filter/AlwaysCloneTreeFilter.java",
+    "test/resources/SampleDataRepositoryTestCase.java",
+    "util/CPUTimeStopWatch.java",
+    "util/io/Strings.java",
+]]
+
+DATA = [
+    PKG + "lib/empty.gitindex.dat",
+    PKG + "lib/sorttest.gitindex.dat",
+]
+
+tests(glob(
+    ["tst/**/*.java"],
+    exclude = HELPERS + DATA
+))
+
+java_library(
+    name = "helpers",
+    testonly = 1,
+    srcs = HELPERS,
+    resources = DATA,
+    deps = [
+        "//lib:junit",
+        "//org.eclipse.jgit:jgit",
+        "//org.eclipse.jgit.junit:junit",
+    ],
+)
+
+java_import(
+    name = "tst_rsrc",
+    jars = [":tst_rsrc_jar"],
+)
+
+genrule2(
+  name = 'tst_rsrc_jar',
+  cmd = 'o=$$PWD/$@ && tar cf - $(SRCS) | tar -C $$TMP --strip-components=2 -xf - && cd  $$TMP && zip -qr $$o .',
+  srcs = glob(['tst-rsrc/**']),
+  outs = ['tst_rsrc.jar',],
+)
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index f48cae0..73c1a03 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -2,53 +2,54 @@
 Bundle-ManifestVersion: 2
 Bundle-Name: %plugin_name
 Bundle-SymbolicName: org.eclipse.jgit.test
-Bundle-Version: 4.6.2.qualifier
+Bundle-Version: 4.7.2.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.2,4.7.0)",
- org.eclipse.jgit.api.errors;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.attributes;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.awtui;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.blame;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.diff;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.dircache;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.errors;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.events;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.fnmatch;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.gitrepo;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.hooks;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.ignore;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.ignore.internal;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.pack;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.internal.storage.reftree;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.junit;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lfs;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.merge;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.notes;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.patch;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.pgm;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.pgm.internal;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.revplot;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.revwalk.filter;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.storage.file;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.storage.pack;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.submodule;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport.http;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport.resolver;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.treewalk;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.treewalk.filter;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util.io;version="[4.6.2,4.7.0)",
+ org.eclipse.jgit.api;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.api.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.attributes;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.awtui;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.blame;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.diff;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.dircache;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.events;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.fnmatch;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.gitrepo;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.hooks;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.ignore;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.ignore.internal;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.junit;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lfs;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.merge;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.notes;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.patch;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.pgm;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revplot;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.storage.file;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.storage.pack;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.submodule;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport.http;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.treewalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util.io;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util.sha1;version="[4.7.2,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/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java
index 76930f2..7c0ea44 100644
--- a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java
+++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/patch/EGitPatchHistoryTest.java
@@ -89,7 +89,7 @@
 			super(new String[] { "-p" });
 			stats = s;
 
-			offBy1 = new HashSet<String>();
+			offBy1 = new HashSet<>();
 			offBy1.add("9bda5ece6806cd797416eaa47c7b927cc6e9c3b2");
 		}
 
@@ -158,7 +158,7 @@
 	}
 
 	static class NumStatReader extends CommitReader {
-		final HashMap<String, HashMap<String, StatInfo>> stats = new HashMap<String, HashMap<String, StatInfo>>();
+		final HashMap<String, HashMap<String, StatInfo>> stats = new HashMap<>();
 
 		NumStatReader() throws IOException {
 			super(new String[] { "--numstat" });
@@ -166,7 +166,7 @@
 
 		@Override
 		void onCommit(String commitId, byte[] buf) {
-			final HashMap<String, StatInfo> files = new HashMap<String, StatInfo>();
+			final HashMap<String, StatInfo> files = new HashMap<>();
 			final MutableInteger ptr = new MutableInteger();
 			while (ptr.value < buf.length) {
 				if (buf[ptr.value] == '\n')
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index 1aff289..6ea0642 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.2-SNAPSHOT</version>
+    <version>4.7.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.test</artifactId>
@@ -155,6 +155,10 @@
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
           <argLine>-Xmx256m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine>
+          <includes>
+            <include>**/*Test.java</include>
+            <include>**/*Tests.java</include>
+          </includes>
         </configuration>
       </plugin>
     </plugins>
diff --git a/org.eclipse.jgit.test/src/org/eclipse/jgit/lib/Sets.java b/org.eclipse.jgit.test/src/org/eclipse/jgit/lib/Sets.java
index 4454e1a..5a01eae 100644
--- a/org.eclipse.jgit.test/src/org/eclipse/jgit/lib/Sets.java
+++ b/org.eclipse.jgit.test/src/org/eclipse/jgit/lib/Sets.java
@@ -49,7 +49,7 @@
 public class Sets {
 	@SafeVarargs
 	public static <T> Set<T> of(T... elements) {
-		Set<T> ret = new HashSet<T>();
+		Set<T> ret = new HashSet<>();
 		for (T element : elements)
 			ret.add(element);
 		return ret;
diff --git a/org.eclipse.jgit.test/tests.bzl b/org.eclipse.jgit.test/tests.bzl
new file mode 100644
index 0000000..9cadc74
--- /dev/null
+++ b/org.eclipse.jgit.test/tests.bzl
@@ -0,0 +1,48 @@
+load(
+    "@com_googlesource_gerrit_bazlets//tools:junit.bzl",
+    "junit_tests",
+)
+
+def tests(tests):
+  for src in tests:
+    name = src[len('tst/'):len(src)-len('.java')].replace('/', '_')
+    labels = []
+    if name.startswith('org_eclipse_jgit_'):
+      l = name[len('org.eclipse.jgit_'):]
+      if l.startswith('internal_storage_'):
+        l = l[len('internal.storage_'):]
+      i = l.find('_')
+      if i > 0:
+        labels.append(l[:i])
+      else:
+        labels.append(i)
+    if 'lib' not in labels:
+      labels.append('lib')
+
+    additional_deps = []
+    if src.endswith("RootLocaleTest.java"):
+      additional_deps = [
+        '//org.eclipse.jgit.pgm:pgm',
+        '//org.eclipse.jgit.ui:ui',
+      ]
+    if src.endswith("WalkEncryptionTest.java"):
+      additional_deps = [
+        '//org.eclipse.jgit:insecure_cipher_factory',
+      ]
+
+    junit_tests(
+      name = name,
+      tags = labels,
+      srcs = [src],
+      deps = additional_deps + [
+        ':helpers',
+        ':tst_rsrc',
+        '//lib:javaewah',
+        '//lib:junit',
+        '//lib:slf4j-api',
+        '//org.eclipse.jgit:jgit',
+        '//org.eclipse.jgit.junit:junit',
+        '//org.eclipse.jgit.lfs:jgit-lfs',
+      ],
+      jvm_flags = ["-Xmx256m", "-Dfile.encoding=UTF-8"],
+    )
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/util/sha1/shattered-1.pdf b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/util/sha1/shattered-1.pdf
new file mode 100644
index 0000000..ba9aaa1
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/util/sha1/shattered-1.pdf
Binary files differ
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/util/sha1/shattered-2.pdf b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/util/sha1/shattered-2.pdf
new file mode 100644
index 0000000..b621eec
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/util/sha1/shattered-2.pdf
Binary files differ
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
index 5ad73f1..ed3907e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -1074,30 +1074,37 @@
 
 		FS executableFs = new FS() {
 
+			@Override
 			public boolean supportsExecute() {
 				return true;
 			}
 
+			@Override
 			public boolean setExecute(File f, boolean canExec) {
 				return true;
 			}
 
+			@Override
 			public ProcessBuilder runInShell(String cmd, String[] args) {
 				return null;
 			}
 
+			@Override
 			public boolean retryFailedLockFileCommit() {
 				return false;
 			}
 
+			@Override
 			public FS newInstance() {
 				return this;
 			}
 
+			@Override
 			protected File discoverGitExe() {
 				return null;
 			}
 
+			@Override
 			public boolean canExecute(File f) {
 				try {
 					return read(f).startsWith("binary:");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
index fc8df42..edab96b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
@@ -57,6 +57,7 @@
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectLoader;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.util.StringUtils;
@@ -78,6 +79,7 @@
 		ArchiveCommand.registerFormat(format.SUFFIXES.get(0), format);
 	}
 
+	@Override
 	@After
 	public void tearDown() {
 		ArchiveCommand.unregisterFormat(format.SUFFIXES.get(0));
@@ -190,7 +192,7 @@
 
 	private class MockFormat implements ArchiveCommand.Format<MockOutputStream> {
 
-		private Map<String, String> entries = new HashMap<String, String>();
+		private Map<String, String> entries = new HashMap<>();
 
 		private int size() {
 			return entries.size();
@@ -203,12 +205,14 @@
 		private final List<String> SUFFIXES = Collections
 				.unmodifiableList(Arrays.asList(".mck"));
 
+		@Override
 		public MockOutputStream createArchiveOutputStream(OutputStream s)
 				throws IOException {
 			return createArchiveOutputStream(s,
 					Collections.<String, Object> emptyMap());
 		}
 
+		@Override
 		public MockOutputStream createArchiveOutputStream(OutputStream s,
 				Map<String, Object> o) throws IOException {
 			for (Map.Entry<String, Object> p : o.entrySet()) {
@@ -224,11 +228,18 @@
 			return new MockOutputStream();
 		}
 
+		@Override
 		public void putEntry(MockOutputStream out, String path, FileMode mode, ObjectLoader loader) {
+			putEntry(out, null, path, mode, loader);
+		}
+
+		@Override
+		public void putEntry(MockOutputStream out, ObjectId tree, String path, FileMode mode, ObjectLoader loader) {
 			String content = mode != FileMode.TREE ? new String(loader.getBytes()) : null;
 			entries.put(path, content);
 		}
 
+		@Override
 		public Iterable<String> suffixes() {
 			return SUFFIXES;
 		}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
index 0e1cf02..85436db 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CleanCommandTest.java
@@ -64,6 +64,7 @@
 public class CleanCommandTest extends RepositoryTestCase {
 	private Git git;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -143,7 +144,7 @@
 		assertTrue(files.size() > 0);
 
 		// run clean with setPaths
-		Set<String> paths = new TreeSet<String>();
+		Set<String> paths = new TreeSet<>();
 		paths.add("File3.txt");
 		Set<String> cleanedFiles = git.clean().setPaths(paths).call();
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
index 8a728ca..ae0b8dd 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
@@ -85,9 +85,10 @@
 
 	private TestRepository<Repository> tr;
 
+	@Override
 	public void setUp() throws Exception {
 		super.setUp();
-		tr = new TestRepository<Repository>(db);
+		tr = new TestRepository<>(db);
 
 		git = new Git(db);
 		// commit something
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
index 473dce1..7e657e6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
@@ -92,30 +92,37 @@
 
 		FS executableFs = new FS() {
 
+			@Override
 			public boolean supportsExecute() {
 				return true;
 			}
 
+			@Override
 			public boolean setExecute(File f, boolean canExec) {
 				return true;
 			}
 
+			@Override
 			public ProcessBuilder runInShell(String cmd, String[] args) {
 				return null;
 			}
 
+			@Override
 			public boolean retryFailedLockFileCommit() {
 				return false;
 			}
 
+			@Override
 			public FS newInstance() {
 				return this;
 			}
 
+			@Override
 			protected File discoverGitExe() {
 				return null;
 			}
 
+			@Override
 			public boolean canExecute(File f) {
 				return true;
 			}
@@ -137,30 +144,37 @@
 
 		FS nonExecutableFs = new FS() {
 
+			@Override
 			public boolean supportsExecute() {
 				return false;
 			}
 
+			@Override
 			public boolean setExecute(File f, boolean canExec) {
 				return false;
 			}
 
+			@Override
 			public ProcessBuilder runInShell(String cmd, String[] args) {
 				return null;
 			}
 
+			@Override
 			public boolean retryFailedLockFileCommit() {
 				return false;
 			}
 
+			@Override
 			public FS newInstance() {
 				return this;
 			}
 
+			@Override
 			protected File discoverGitExe() {
 				return null;
 			}
 
+			@Override
 			public boolean canExecute(File f) {
 				return false;
 			}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
index 5f10131..48d3733 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
@@ -623,6 +623,7 @@
 			for (int i = 0; i < dc.getEntryCount(); i++) {
 				editor.add(new DirCacheEditor.PathEdit(
 						dc.getEntry(i).getPathString()) {
+					@Override
 					public void apply(DirCacheEntry ent) {
 						ent.smudgeRacilyClean();
 					}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchAndPullCommandsRecurseSubmodulesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchAndPullCommandsRecurseSubmodulesTest.java
new file mode 100644
index 0000000..73a705b
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchAndPullCommandsRecurseSubmodulesTest.java
@@ -0,0 +1,361 @@
+/*
+ * 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.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.eclipse.jgit.api.ResetCommand.ResetType;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.submodule.SubmoduleStatus;
+import org.eclipse.jgit.submodule.SubmoduleStatusType;
+import org.eclipse.jgit.submodule.SubmoduleWalk;
+import org.eclipse.jgit.transport.FetchResult;
+import org.eclipse.jgit.transport.RefSpec;
+import org.junit.Before;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
+@RunWith(Theories.class)
+public class FetchAndPullCommandsRecurseSubmodulesTest extends RepositoryTestCase {
+	@DataPoints
+	public static boolean[] useFetch = { true, false };
+
+	private Git git;
+
+	private Git git2;
+
+	private Git sub1Git;
+
+	private Git sub2Git;
+
+	private RevCommit commit1;
+
+	private RevCommit commit2;
+
+	private ObjectId submodule1Head;
+
+	private ObjectId submodule2Head;
+
+	private final RefSpec REFSPEC = new RefSpec("refs/heads/master");
+
+	private final String REMOTE = "origin";
+
+	private final String PATH = "sub";
+
+	@Before
+	public void setUpSubmodules() throws Exception {
+		git = new Git(db);
+
+		// Create submodule 1
+		File submodule1 = createTempDirectory(
+				"testCloneRepositoryWithNestedSubmodules1");
+		sub1Git = Git.init().setDirectory(submodule1).call();
+		assertNotNull(sub1Git);
+		Repository sub1 = sub1Git.getRepository();
+		assertNotNull(sub1);
+		addRepoToClose(sub1);
+
+		String file = "file.txt";
+
+		write(new File(sub1.getWorkTree(), file), "content");
+		sub1Git.add().addFilepattern(file).call();
+		RevCommit commit = sub1Git.commit().setMessage("create file").call();
+		assertNotNull(commit);
+
+		// Create submodule 2
+		File submodule2 = createTempDirectory(
+				"testCloneRepositoryWithNestedSubmodules2");
+		sub2Git = Git.init().setDirectory(submodule2).call();
+		assertNotNull(sub2Git);
+		Repository sub2 = sub2Git.getRepository();
+		assertNotNull(sub2);
+		addRepoToClose(sub2);
+
+		write(new File(sub2.getWorkTree(), file), "content");
+		sub2Git.add().addFilepattern(file).call();
+		RevCommit sub2Head = sub2Git.commit().setMessage("create file").call();
+		assertNotNull(sub2Head);
+
+		// Add submodule 2 to submodule 1
+		Repository r2 = sub1Git.submoduleAdd().setPath(PATH)
+				.setURI(sub2.getDirectory().toURI().toString()).call();
+		assertNotNull(r2);
+		addRepoToClose(r2);
+		RevCommit sub1Head = sub1Git.commit().setAll(true)
+				.setMessage("Adding submodule").call();
+		assertNotNull(sub1Head);
+
+		// Add submodule 1 to default repository
+		Repository r1 = git.submoduleAdd().setPath(PATH)
+				.setURI(sub1.getDirectory().toURI().toString()).call();
+		assertNotNull(r1);
+		addRepoToClose(r1);
+		assertNotNull(git.commit().setAll(true).setMessage("Adding submodule")
+				.call());
+
+		// Clone default repository and include submodules
+		File directory = createTempDirectory(
+				"testCloneRepositoryWithNestedSubmodules");
+		CloneCommand clone = Git.cloneRepository();
+		clone.setDirectory(directory);
+		clone.setCloneSubmodules(true);
+		clone.setURI(git.getRepository().getDirectory().toURI().toString());
+		git2 = clone.call();
+		addRepoToClose(git2.getRepository());
+		assertNotNull(git2);
+
+		// Record current FETCH_HEAD of submodules
+		try (SubmoduleWalk walk = SubmoduleWalk
+				.forIndex(git2.getRepository())) {
+			assertTrue(walk.next());
+			Repository r = walk.getRepository();
+			submodule1Head = r.resolve(Constants.FETCH_HEAD);
+
+			try (SubmoduleWalk walk2 = SubmoduleWalk.forIndex(r)) {
+				assertTrue(walk2.next());
+				submodule2Head = walk2.getRepository()
+						.resolve(Constants.FETCH_HEAD);
+			}
+		}
+
+		// Commit in submodule 1
+		JGitTestUtil.writeTrashFile(r1, "f1.txt", "test");
+		sub1Git.add().addFilepattern("f1.txt").call();
+		commit1 = sub1Git.commit().setMessage("new commit").call();
+
+		// Commit in submodule 2
+		JGitTestUtil.writeTrashFile(r2, "f2.txt", "test");
+		sub2Git.add().addFilepattern("f2.txt").call();
+		commit2 = sub2Git.commit().setMessage("new commit").call();
+	}
+
+	@Theory
+	public void shouldNotFetchSubmodulesWhenNo(boolean fetch) throws Exception {
+		FetchResult result = execute(FetchRecurseSubmodulesMode.NO, fetch);
+		assertTrue(result.submoduleResults().isEmpty());
+		assertSubmoduleFetchHeads(submodule1Head, submodule2Head);
+	}
+
+	@Theory
+	public void shouldFetchSubmodulesWhenYes(boolean fetch) throws Exception {
+		FetchResult result = execute(FetchRecurseSubmodulesMode.YES, fetch);
+		assertTrue(result.submoduleResults().containsKey("sub"));
+		FetchResult subResult = result.submoduleResults().get("sub");
+		assertTrue(subResult.submoduleResults().containsKey("sub"));
+		assertSubmoduleFetchHeads(commit1, commit2);
+	}
+
+	@Theory
+	public void shouldFetchSubmodulesWhenOnDemandAndRevisionChanged(
+			boolean fetch) throws Exception {
+		RevCommit update = updateSubmoduleRevision();
+		FetchResult result = execute(FetchRecurseSubmodulesMode.ON_DEMAND,
+				fetch);
+
+		// The first submodule should have been updated
+		assertTrue(result.submoduleResults().containsKey("sub"));
+		FetchResult subResult = result.submoduleResults().get("sub");
+
+		// The second submodule should not get updated
+		assertTrue(subResult.submoduleResults().isEmpty());
+		assertSubmoduleFetchHeads(commit1, submodule2Head);
+
+		// After fetch the parent repo's fetch head should be the commit
+		// that updated the submodule.
+		assertEquals(update,
+				git2.getRepository().resolve(Constants.FETCH_HEAD));
+	}
+
+	@Theory
+	public void shouldNotFetchSubmodulesWhenOnDemandAndRevisionNotChanged(
+			boolean fetch) throws Exception {
+		FetchResult result = execute(FetchRecurseSubmodulesMode.ON_DEMAND,
+				fetch);
+		assertTrue(result.submoduleResults().isEmpty());
+		assertSubmoduleFetchHeads(submodule1Head, submodule2Head);
+	}
+
+	@Theory
+	public void shouldNotFetchSubmodulesWhenSubmoduleConfigurationSetToNo(
+			boolean fetch) throws Exception {
+		StoredConfig config = git2.getRepository().getConfig();
+		config.setEnum(ConfigConstants.CONFIG_SUBMODULE_SECTION, PATH,
+				ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES,
+				FetchRecurseSubmodulesMode.NO);
+		config.save();
+		updateSubmoduleRevision();
+		FetchResult result = execute(null, fetch);
+		assertTrue(result.submoduleResults().isEmpty());
+		assertSubmoduleFetchHeads(submodule1Head, submodule2Head);
+	}
+
+	@Theory
+	public void shouldFetchSubmodulesWhenSubmoduleConfigurationSetToYes(
+			boolean fetch) throws Exception {
+		StoredConfig config = git2.getRepository().getConfig();
+		config.setEnum(ConfigConstants.CONFIG_SUBMODULE_SECTION, PATH,
+				ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES,
+				FetchRecurseSubmodulesMode.YES);
+		config.save();
+		FetchResult result = execute(null, fetch);
+		assertTrue(result.submoduleResults().containsKey("sub"));
+		FetchResult subResult = result.submoduleResults().get("sub");
+		assertTrue(subResult.submoduleResults().containsKey("sub"));
+		assertSubmoduleFetchHeads(commit1, commit2);
+	}
+
+	@Theory
+	public void shouldNotFetchSubmodulesWhenFetchConfigurationSetToNo(
+			boolean fetch) throws Exception {
+		StoredConfig config = git2.getRepository().getConfig();
+		config.setEnum(ConfigConstants.CONFIG_FETCH_SECTION, null,
+				ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES,
+				FetchRecurseSubmodulesMode.NO);
+		config.save();
+		updateSubmoduleRevision();
+		FetchResult result = execute(null, fetch);
+		assertTrue(result.submoduleResults().isEmpty());
+		assertSubmoduleFetchHeads(submodule1Head, submodule2Head);
+	}
+
+	@Theory
+	public void shouldFetchSubmodulesWhenFetchConfigurationSetToYes(
+			boolean fetch) throws Exception {
+		StoredConfig config = git2.getRepository().getConfig();
+		config.setEnum(ConfigConstants.CONFIG_FETCH_SECTION, null,
+				ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES,
+				FetchRecurseSubmodulesMode.YES);
+		config.save();
+		FetchResult result = execute(null, fetch);
+		assertTrue(result.submoduleResults().containsKey("sub"));
+		FetchResult subResult = result.submoduleResults().get("sub");
+		assertTrue(subResult.submoduleResults().containsKey("sub"));
+		assertSubmoduleFetchHeads(commit1, commit2);
+	}
+
+	private RevCommit updateSubmoduleRevision() throws Exception {
+		// Fetch the submodule in the original git and reset it to
+		// the commit that was created
+		try (SubmoduleWalk w = SubmoduleWalk.forIndex(git.getRepository())) {
+			assertTrue(w.next());
+			try (Git g = new Git(w.getRepository())) {
+				g.fetch().setRemote(REMOTE).setRefSpecs(REFSPEC).call();
+				g.reset().setMode(ResetType.HARD).setRef(commit1.name()).call();
+			}
+		}
+
+		// Submodule index Id should be same as before, but head Id should be
+		// updated to the new commit, and status should be "checked out".
+		SubmoduleStatus subStatus = git.submoduleStatus().call().get("sub");
+		assertEquals(submodule1Head, subStatus.getIndexId());
+		assertEquals(commit1, subStatus.getHeadId());
+		assertEquals(SubmoduleStatusType.REV_CHECKED_OUT, subStatus.getType());
+
+		// Add and commit the submodule status
+		git.add().addFilepattern("sub").call();
+		RevCommit update = git.commit().setMessage("update sub").call();
+
+		// Both submodule index and head should now be at the new commit, and
+		// the status should be "initialized".
+		subStatus = git.submoduleStatus().call().get("sub");
+		assertEquals(commit1, subStatus.getIndexId());
+		assertEquals(commit1, subStatus.getHeadId());
+		assertEquals(SubmoduleStatusType.INITIALIZED, subStatus.getType());
+
+		return update;
+	}
+
+	private FetchResult execute(FetchRecurseSubmodulesMode mode, boolean fetch)
+			throws Exception {
+		FetchResult result;
+
+		if (fetch) {
+			result = git2.fetch().setRemote(REMOTE).setRefSpecs(REFSPEC)
+					.setRecurseSubmodules(mode).call();
+		} else {
+			// For the purposes of this test we don't need to care about the
+			// pull result, or the result of pull with merge. We are only
+			// interested in checking whether or not the submodules were updated
+			// as expected. Setting to rebase makes it easier to assert about
+			// the state of the parent repository head, i.e. we know it should
+			// be at the submodule update commit, and don't need to consider a
+			// merge commit created by the pull.
+			result = git2.pull().setRemote(REMOTE).setRebase(true)
+					.setRecurseSubmodules(mode).call().getFetchResult();
+		}
+		assertNotNull(result);
+		return result;
+	}
+
+	private void assertSubmoduleFetchHeads(ObjectId expectedHead1,
+			ObjectId expectedHead2) throws Exception {
+		try (SubmoduleWalk walk = SubmoduleWalk
+				.forIndex(git2.getRepository())) {
+			assertTrue(walk.next());
+			Repository r = walk.getRepository();
+			ObjectId newHead1 = r.resolve(Constants.FETCH_HEAD);
+			ObjectId newHead2;
+			try (SubmoduleWalk walk2 = SubmoduleWalk.forIndex(r)) {
+				assertTrue(walk2.next());
+				newHead2 = walk2.getRepository().resolve(Constants.FETCH_HEAD);
+			}
+
+			assertEquals(expectedHead1, newHead1);
+			assertEquals(expectedHead2, newHead2);
+		}
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java
index 56a1f38..a36f6c5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/FetchCommandTest.java
@@ -45,12 +45,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 
-import java.io.IOException;
-import java.net.URISyntaxException;
 import java.util.Collection;
 
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
@@ -74,7 +70,7 @@
 	private Git remoteGit;
 
 	@Before
-	public void setupRemoteRepository() throws IOException, URISyntaxException {
+	public void setupRemoteRepository() throws Exception {
 		git = new Git(db);
 
 		// create other repository
@@ -91,8 +87,7 @@
 	}
 
 	@Test
-	public void testFetch() throws JGitInternalException, IOException,
-			GitAPIException {
+	public void testFetch() throws Exception {
 
 		// create some refs via commits and tag
 		RevCommit commit = remoteGit.commit().setMessage("initial commit").call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
index 3bff8f2..a4e5574 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
@@ -55,7 +55,6 @@
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.util.FileUtils;
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -80,14 +79,6 @@
 		addRepoToClose(bareRepo);
 	}
 
-	@Override
-	@After
-	public void tearDown() throws Exception {
-		db.close();
-		bareRepo.close();
-		super.tearDown();
-	}
-
 	@Test
 	public void testWrap() throws JGitInternalException, GitAPIException {
 		Git git = Git.wrap(db);
@@ -141,7 +132,6 @@
 	public void testClose() throws IOException, JGitInternalException,
 			GitAPIException {
 		File workTree = db.getWorkTree();
-		db.close();
 		Git git = Git.open(workTree);
 		git.gc().setExpire(null).call();
 		git.checkout().setName(git.getRepository().resolve("HEAD^").getName())
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogCommandTest.java
index 1310625..38178bf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LogCommandTest.java
@@ -62,7 +62,7 @@
 
 	@Test
 	public void logAllCommits() throws Exception {
-		List<RevCommit> commits = new ArrayList<RevCommit>();
+		List<RevCommit> commits = new ArrayList<>();
 		Git git = Git.wrap(db);
 
 		writeTrashFile("Test.txt", "Hello world");
@@ -94,7 +94,7 @@
 
     @Test
     public void logAllCommitsWithTag() throws Exception {
-		List<RevCommit> commits = new ArrayList<RevCommit>();
+		List<RevCommit> commits = new ArrayList<>();
 		Git git = Git.wrap(db);
 
 		writeTrashFile("Test.txt", "Hello world");
@@ -123,7 +123,7 @@
 	}
 
 	private List<RevCommit> createCommits(Git git) throws Exception {
-		List<RevCommit> commits = new ArrayList<RevCommit>();
+		List<RevCommit> commits = new ArrayList<>();
 		writeTrashFile("Test.txt", "Hello world");
 		git.add().addFilepattern("Test.txt").call();
 		commits.add(git.commit().setMessage("commit#1").call());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java
index a853d6a..c9f77ea 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/LsRemoteCommandTest.java
@@ -58,6 +58,7 @@
 
 	private Git git;
 
+	@Override
 	public void setUp() throws Exception {
 		super.setUp();
 		git = new Git(db);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
index cb3dbf1..4b23349 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
@@ -1556,7 +1556,7 @@
 
 	@Test
 	public void testRecursiveMergeWithConflict() throws Exception {
-		TestRepository<Repository> db_t = new TestRepository<Repository>(db);
+		TestRepository<Repository> db_t = new TestRepository<>(db);
 		BranchBuilder master = db_t.branch("master");
 		RevCommit m0 = master.commit().add("f", "1\n2\n3\n4\n5\n6\n7\n8\n9\n")
 				.message("m0").create();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java
index bd62200..138b7af 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java
@@ -64,7 +64,7 @@
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
-		tr = new TestRepository<Repository>(db);
+		tr = new TestRepository<>(db);
 		git = new Git(db);
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
index a526fda..823516b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
@@ -346,6 +346,7 @@
 	/** global rebase config should be respected */
 	public void testPullWithRebasePreserve1Config() throws Exception {
 		Callable<PullResult> setup = new Callable<PullResult>() {
+			@Override
 			public PullResult call() throws Exception {
 				StoredConfig config = dbTarget.getConfig();
 				config.setString("pull", null, "rebase", "preserve");
@@ -360,6 +361,7 @@
 	/** the branch-local config should win over the global config */
 	public void testPullWithRebasePreserveConfig2() throws Exception {
 		Callable<PullResult> setup = new Callable<PullResult>() {
+			@Override
 			public PullResult call() throws Exception {
 				StoredConfig config = dbTarget.getConfig();
 				config.setString("pull", null, "rebase", "false");
@@ -375,6 +377,7 @@
 	/** the branch-local config should be respected */
 	public void testPullWithRebasePreserveConfig3() throws Exception {
 		Callable<PullResult> setup = new Callable<PullResult>() {
+			@Override
 			public PullResult call() throws Exception {
 				StoredConfig config = dbTarget.getConfig();
 				config.setString("branch", "master", "rebase", "preserve");
@@ -389,6 +392,7 @@
 	/** global rebase config should be respected */
 	public void testPullWithRebaseConfig1() throws Exception {
 		Callable<PullResult> setup = new Callable<PullResult>() {
+			@Override
 			public PullResult call() throws Exception {
 				StoredConfig config = dbTarget.getConfig();
 				config.setString("pull", null, "rebase", "true");
@@ -403,6 +407,7 @@
 	/** the branch-local config should win over the global config */
 	public void testPullWithRebaseConfig2() throws Exception {
 		Callable<PullResult> setup = new Callable<PullResult>() {
+			@Override
 			public PullResult call() throws Exception {
 				StoredConfig config = dbTarget.getConfig();
 				config.setString("pull", null, "rebase", "preserve");
@@ -418,6 +423,7 @@
 	/** the branch-local config should be respected */
 	public void testPullWithRebaseConfig3() throws Exception {
 		Callable<PullResult> setup = new Callable<PullResult>() {
+			@Override
 			public PullResult call() throws Exception {
 				StoredConfig config = dbTarget.getConfig();
 				config.setString("branch", "master", "rebase", "true");
@@ -432,6 +438,7 @@
 	/** without config it should merge */
 	public void testPullWithoutConfig() throws Exception {
 		Callable<PullResult> setup = new Callable<PullResult>() {
+			@Override
 			public PullResult call() throws Exception {
 				return target.pull().call();
 			}
@@ -443,6 +450,7 @@
 	/** the branch local config should win over the global config */
 	public void testPullWithMergeConfig() throws Exception {
 		Callable<PullResult> setup = new Callable<PullResult>() {
+			@Override
 			public PullResult call() throws Exception {
 				StoredConfig config = dbTarget.getConfig();
 				config.setString("pull", null, "rebase", "true");
@@ -458,6 +466,7 @@
 	/** the branch local config should win over the global config */
 	public void testPullWithMergeConfig2() throws Exception {
 		Callable<PullResult> setup = new Callable<PullResult>() {
+			@Override
 			public PullResult call() throws Exception {
 				StoredConfig config = dbTarget.getConfig();
 				config.setString("pull", null, "rebase", "false");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
index 2a32540..8c613ec 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
@@ -66,8 +66,10 @@
 import org.eclipse.jgit.lib.StoredConfig;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.transport.PushResult;
+import org.eclipse.jgit.transport.RefLeaseSpec;
 import org.eclipse.jgit.transport.RefSpec;
 import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
 import org.eclipse.jgit.transport.TrackingRefUpdate;
 import org.eclipse.jgit.transport.URIish;
 import org.eclipse.jgit.util.FS;
@@ -379,4 +381,56 @@
 					db2.resolve(commit3.getId().getName() + "^{commit}"));
 		}
 	}
+
+	@Test
+	public void testPushWithLease() throws JGitInternalException, IOException,
+			GitAPIException, URISyntaxException {
+
+		// create other repository
+		Repository db2 = createWorkRepository();
+
+		// setup the first repository
+		final StoredConfig config = db.getConfig();
+		RemoteConfig remoteConfig = new RemoteConfig(config, "test");
+		URIish uri = new URIish(db2.getDirectory().toURI().toURL());
+		remoteConfig.addURI(uri);
+		remoteConfig.update(config);
+		config.save();
+
+		try (Git git1 = new Git(db)) {
+			// create one commit and push it
+			RevCommit commit = git1.commit().setMessage("initial commit").call();
+			git1.branchCreate().setName("initial").call();
+
+			RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x");
+			git1.push().setRemote("test").setRefSpecs(spec)
+					.call();
+
+			assertEquals(commit.getId(),
+					db2.resolve(commit.getId().getName() + "^{commit}"));
+			//now try to force-push a new commit, with a good lease
+
+			git1.commit().setMessage("second commit").call();
+			Iterable<PushResult> results =
+					git1.push().setRemote("test").setRefSpecs(spec)
+							.setRefLeaseSpecs(new RefLeaseSpec("refs/heads/x", "initial"))
+							.call();
+			for (PushResult result : results) {
+				RemoteRefUpdate update = result.getRemoteUpdate("refs/heads/x");
+				assertEquals(update.getStatus(), RemoteRefUpdate.Status.OK);
+			}
+
+			git1.commit().setMessage("third commit").call();
+			//now try to force-push a new commit, with a bad lease
+
+			results =
+					git1.push().setRemote("test").setRefSpecs(spec)
+							.setRefLeaseSpecs(new RefLeaseSpec("refs/heads/x", "initial"))
+							.call();
+			for (PushResult result : results) {
+				RemoteRefUpdate update = result.getRemoteUpdate("refs/heads/x");
+				assertEquals(update.getStatus(), RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED);
+			}
+		}
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
index 24cb522..0cc0816 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
@@ -2269,11 +2269,13 @@
 
 		RebaseResult res = git.rebase().setUpstream("HEAD~2")
 				.runInteractively(new InteractiveHandler() {
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						steps.add(0, new RebaseTodoLine(
 								"# Comment that should not be processed"));
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						fail("modifyCommitMessage() was not expected to be called");
 						return commit;
@@ -2284,6 +2286,7 @@
 
 		RebaseResult res2 = git.rebase().setUpstream("HEAD~2")
 				.runInteractively(new InteractiveHandler() {
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							// delete RevCommit c4
@@ -2293,6 +2296,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						fail("modifyCommitMessage() was not expected to be called");
 						return commit;
@@ -2514,6 +2518,7 @@
 		RebaseResult res = git.rebase().setUpstream("HEAD~2")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							steps.get(0).setAction(Action.REWORD);
@@ -2522,6 +2527,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						return "rewritten commit message";
 					}
@@ -2560,6 +2566,7 @@
 
 		RebaseResult res = git.rebase().setUpstream("HEAD~2")
 				.runInteractively(new InteractiveHandler() {
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							steps.get(0).setAction(Action.EDIT);
@@ -2568,6 +2575,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						return ""; // not used
 					}
@@ -2624,6 +2632,7 @@
 		git.rebase().setUpstream("HEAD~3")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							steps.get(1).setAction(Action.SQUASH);
@@ -2632,6 +2641,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						final File messageSquashFile = new File(db
 								.getDirectory(), "rebase-merge/message-squash");
@@ -2704,6 +2714,7 @@
 		git.rebase().setUpstream("HEAD~4")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							steps.get(1).setAction(Action.SQUASH);
@@ -2713,6 +2724,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						final File messageSquashFile = new File(db.getDirectory(),
 								"rebase-merge/message-squash");
@@ -2786,6 +2798,7 @@
 		git.rebase().setUpstream("HEAD~4")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							steps.get(1).setAction(Action.FIXUP);
@@ -2795,6 +2808,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						final File messageSquashFile = new File(db
 								.getDirectory(), "rebase-merge/message-squash");
@@ -2861,6 +2875,7 @@
 		git.rebase().setUpstream("HEAD~3")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							steps.get(1).setAction(Action.FIXUP);
@@ -2869,6 +2884,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						fail("No callback to modify commit message expected for single fixup");
 						return commit;
@@ -2910,6 +2926,7 @@
 		git.rebase().setUpstream("HEAD~2")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							steps.get(1).setAction(Action.FIXUP);
@@ -2918,6 +2935,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						fail("No callback to modify commit message expected for single fixup");
 						return commit;
@@ -2950,6 +2968,7 @@
 		git.rebase().setUpstream("HEAD~1")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							steps.get(0).setAction(Action.FIXUP);
@@ -2958,6 +2977,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						return commit;
 					}
@@ -2982,6 +3002,7 @@
 		git.rebase().setUpstream("HEAD~1")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							steps.get(0).setAction(Action.SQUASH);
@@ -2990,6 +3011,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						return commit;
 					}
@@ -3013,6 +3035,7 @@
 		git.rebase().setUpstream("HEAD~1")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							steps.get(0).setAction(Action.EDIT);
@@ -3021,6 +3044,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						return commit;
 					}
@@ -3055,6 +3079,7 @@
 		RebaseResult result = git.rebase().setUpstream("HEAD~2")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						steps.remove(0);
 						try {
@@ -3064,6 +3089,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						return commit;
 					}
@@ -3097,6 +3123,7 @@
 		RebaseResult result = git.rebase().setUpstream("HEAD~2")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						steps.remove(0);
 						try {
@@ -3106,6 +3133,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						return "rewritten commit message";
 					}
@@ -3114,6 +3142,7 @@
 		git.add().addFilepattern(FILE1).call();
 		result = git.rebase().runInteractively(new InteractiveHandler() {
 
+			@Override
 			public void prepareSteps(List<RebaseTodoLine> steps) {
 				steps.remove(0);
 				try {
@@ -3123,6 +3152,7 @@
 				}
 			}
 
+			@Override
 			public String modifyCommitMessage(String commit) {
 				return "rewritten commit message";
 			}
@@ -3160,6 +3190,7 @@
 		RebaseResult result = git.rebase().setUpstream("HEAD~3")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							steps.get(0).setAction(Action.PICK);
@@ -3170,6 +3201,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						return "squashed message";
 					}
@@ -3178,6 +3210,7 @@
 		git.add().addFilepattern(FILE1).call();
 		result = git.rebase().runInteractively(new InteractiveHandler() {
 
+			@Override
 			public void prepareSteps(List<RebaseTodoLine> steps) {
 				try {
 					steps.get(0).setAction(Action.PICK);
@@ -3188,6 +3221,7 @@
 				}
 			}
 
+			@Override
 			public String modifyCommitMessage(String commit) {
 				return "squashed message";
 			}
@@ -3226,6 +3260,7 @@
 		RebaseResult result = git.rebase().setUpstream("HEAD~3")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							steps.get(0).setAction(Action.PICK);
@@ -3236,6 +3271,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						return commit;
 					}
@@ -3244,6 +3280,7 @@
 		git.add().addFilepattern(FILE1).call();
 		result = git.rebase().runInteractively(new InteractiveHandler() {
 
+			@Override
 			public void prepareSteps(List<RebaseTodoLine> steps) {
 				try {
 					steps.get(0).setAction(Action.PICK);
@@ -3254,6 +3291,7 @@
 				}
 			}
 
+			@Override
 			public String modifyCommitMessage(String commit) {
 				return "commit";
 			}
@@ -3297,6 +3335,7 @@
 		RebaseResult result = git.rebase().setUpstream("HEAD~2")
 				.runInteractively(new InteractiveHandler() {
 
+					@Override
 					public void prepareSteps(List<RebaseTodoLine> steps) {
 						try {
 							steps.get(0).setAction(Action.EDIT);
@@ -3306,6 +3345,7 @@
 						}
 					}
 
+					@Override
 					public String modifyCommitMessage(String commit) {
 						return commit;
 					}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RenameBranchCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RenameBranchCommandTest.java
index 4c09a82..5c437ac 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RenameBranchCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RenameBranchCommandTest.java
@@ -69,6 +69,7 @@
 
 	private Git git;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
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..f2e4d5b 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
@@ -77,6 +77,7 @@
 
 	private File committedFile;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -736,4 +737,21 @@
 		}
 		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/api/StashCreateCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
index ae8551e..b9f9f5b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
@@ -82,6 +82,7 @@
 
 	private File untrackedFile;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
index 859277e..5d5be5d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
@@ -73,6 +73,7 @@
 
 	private File committedFile;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java
index b044c01..1d9cd78 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java
@@ -204,7 +204,7 @@
 	@Test
 	public void testIndexOnly() throws IOException, NoFilepatternException,
 			GitAPIException {
-		List<File> attrFiles = new ArrayList<File>();
+		List<File> attrFiles = new ArrayList<>();
 		attrFiles.add(writeGlobalAttributeFile("globalAttributesFile",
 				"*.txt -custom2"));
 		attrFiles.add(writeAttributesFile(".git/info/attributes",
@@ -813,7 +813,7 @@
 	}
 
 	private static Set<Attribute> asSet(Collection<Attribute> attributes) {
-		Set<Attribute> ret = new HashSet<Attribute>();
+		Set<Attribute> ret = new HashSet<>();
 		for (Attribute a : attributes) {
 			ret.add(a);
 		}
@@ -853,7 +853,7 @@
 	}
 
 	static Set<Attribute> asSet(Attribute... attrs) {
-		HashSet<Attribute> result = new HashSet<Attribute>();
+		HashSet<Attribute> result = new HashSet<>();
 		for (Attribute attr : attrs)
 			result.add(attr);
 		return result;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java
index 443c956..73c230a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java
@@ -395,6 +395,7 @@
 			assertTrue(walk.next());
 
 			editor.add(new PathEdit("a.txt") {
+				@Override
 				public void apply(DirCacheEntry ent) {
 					ent.setFileMode(FileMode.EXECUTABLE_FILE);
 					ent.setObjectId(walk.getObjectId(0));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java
index b4234dc..fabf034 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java
@@ -89,7 +89,7 @@
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
-		testDb = new TestRepository<Repository>(db);
+		testDb = new TestRepository<>(db);
 		df = new DiffFormatter(DisabledOutputStream.INSTANCE);
 		df.setRepository(db);
 		df.setAbbreviationLength(8);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
index 4315be9..64eb1c9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
@@ -75,7 +75,7 @@
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
-		testDb = new TestRepository<Repository>(db);
+		testDb = new TestRepository<>(db);
 		rd = new RenameDetector(db);
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java
index 5408f76..d12f302 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheBuilderTest.java
@@ -211,6 +211,7 @@
 		DirCache dc = db.lockDirCache();
 		IndexChangedListener listener = new IndexChangedListener() {
 
+			@Override
 			public void onIndexChanged(IndexChangedEvent event) {
 				throw new ReceivedEventMarkerException();
 			}
@@ -238,6 +239,7 @@
 		dc = db.lockDirCache();
 		listener = new IndexChangedListener() {
 
+			@Override
 			public void onIndexChanged(IndexChangedEvent event) {
 				throw new ReceivedEventMarkerException();
 			}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java
index 3e78046..92ce4e1 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java
@@ -178,7 +178,7 @@
 				.getObjectId());
 		assertEquals(cList.size(), jTree.getEntrySpan());
 
-		final ArrayList<CGitLsTreeRecord> subtrees = new ArrayList<CGitLsTreeRecord>();
+		final ArrayList<CGitLsTreeRecord> subtrees = new ArrayList<>();
 		for (final CGitLsTreeRecord r : cTree.values()) {
 			if (FileMode.TREE.equals(r.mode))
 				subtrees.add(r);
@@ -233,7 +233,7 @@
 	}
 
 	private static Map<String, CGitIndexRecord> readLsFiles() throws Exception {
-		final LinkedHashMap<String, CGitIndexRecord> r = new LinkedHashMap<String, CGitIndexRecord>();
+		final LinkedHashMap<String, CGitIndexRecord> r = new LinkedHashMap<>();
 		final BufferedReader br = new BufferedReader(new InputStreamReader(
 				new FileInputStream(pathOf("gitgit.lsfiles")), "UTF-8"));
 		try {
@@ -249,7 +249,7 @@
 	}
 
 	private static Map<String, CGitLsTreeRecord> readLsTree() throws Exception {
-		final LinkedHashMap<String, CGitLsTreeRecord> r = new LinkedHashMap<String, CGitLsTreeRecord>();
+		final LinkedHashMap<String, CGitLsTreeRecord> r = new LinkedHashMap<>();
 		final BufferedReader br = new BufferedReader(new InputStreamReader(
 				new FileInputStream(pathOf("gitgit.lstree")), "UTF-8"));
 		try {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java
index c85e156..20897a8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java
@@ -72,7 +72,7 @@
 	}
 
 	private static final class RecordingEdit extends PathEdit {
-		final List<DirCacheEntry> entries = new ArrayList<DirCacheEntry>();
+		final List<DirCacheEntry> entries = new ArrayList<>();
 
 		public RecordingEdit(String entryPath) {
 			super(entryPath);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/events/ConfigChangeEventTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/events/ConfigChangeEventTest.java
index 3c1f231..3624100 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/events/ConfigChangeEventTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/events/ConfigChangeEventTest.java
@@ -56,6 +56,7 @@
 		final ConfigChangedEvent[] events = new ConfigChangedEvent[1];
 		db.getListenerList().addConfigChangedListener(
 				new ConfigChangedListener() {
+					@Override
 					public void onConfigChanged(ConfigChangedEvent event) {
 						events[0] = event;
 					}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java
index b1138f0..c9673a6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java
@@ -44,12 +44,16 @@
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.URI;
 import java.util.HashSet;
 import java.util.Set;
 
 import org.junit.Test;
+import org.xml.sax.SAXException;
 
 public class ManifestParserTest {
 
@@ -57,7 +61,7 @@
 	public void testManifestParser() throws Exception {
 		String baseUrl = "https://git.google.com/";
 		StringBuilder xmlContent = new StringBuilder();
-		Set<String> results = new HashSet<String>();
+		Set<String> results = new HashSet<>();
 		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
 			.append("<manifest>")
 			.append("<remote name=\"remote1\" fetch=\".\" />")
@@ -110,4 +114,49 @@
 				"Filtered projects shouldn't contain any unexpected results",
 				results.isEmpty());
 	}
+
+	@Test
+	public void testManifestParserWithMissingFetchOnRemote() throws Exception {
+		String baseUrl = "https://git.google.com/";
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+				.append("<manifest>")
+				.append("<remote name=\"remote1\" />")
+				.append("<default revision=\"master\" remote=\"remote1\" />")
+				.append("<project path=\"foo\" name=\"").append("foo")
+				.append("\" groups=\"a,test\" />")
+				.append("<project path=\"bar\" name=\"").append("bar")
+				.append("\" groups=\"notdefault\" />")
+				.append("<project path=\"foo/a\" name=\"").append("a")
+				.append("\" groups=\"a\" />")
+				.append("<project path=\"b\" name=\"").append("b")
+				.append("\" groups=\"b\" />").append("</manifest>");
+
+		ManifestParser parser = new ManifestParser(null, null, "master",
+				baseUrl, null, null);
+		try {
+			parser.read(new ByteArrayInputStream(
+					xmlContent.toString().getBytes(UTF_8)));
+			fail("ManifestParser did not throw exception for missing fetch");
+		} catch (IOException e) {
+			assertTrue(e.getCause() instanceof SAXException);
+			assertTrue(e.getCause().getMessage()
+					.contains("is missing fetch attribute"));
+		}
+	}
+
+	void testNormalize(String in, String want) {
+		URI got = ManifestParser.normalizeEmptyPath(URI.create(in));
+		if (!got.toString().equals(want)) {
+			fail(String.format("normalize(%s) = %s want %s", in, got, want));
+		}
+	}
+
+	@Test
+	public void testNormalizeEmptyPath() {
+		testNormalize("http://a.b", "http://a.b/");
+		testNormalize("http://a.b/", "http://a.b/");
+		testNormalize("", "");
+		testNormalize("a/b", "a/b");
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
index ccd15d0..9cf4569 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
@@ -48,15 +48,28 @@
 import static org.junit.Assert.assertTrue;
 
 import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileReader;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
 
 import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.InvalidRemoteException;
+import org.eclipse.jgit.api.errors.RefNotFoundException;
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.BlobBasedConfig;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.eclipse.jgit.util.FS;
 import org.junit.Test;
@@ -79,6 +92,7 @@
 
 	private ObjectId oldCommitId;
 
+	@Override
 	public void setUp() throws Exception {
 		super.setUp();
 
@@ -123,6 +137,108 @@
 		resolveRelativeUris();
 	}
 
+	class IndexedRepos implements RepoCommand.RemoteReader {
+		Map<String, Repository> uriRepoMap;
+		IndexedRepos() {
+			uriRepoMap = new HashMap<>();
+		}
+
+		void put(String u, Repository r) {
+			uriRepoMap.put(u, r);
+		}
+
+		@Override
+		public ObjectId sha1(String uri, String refname) throws GitAPIException {
+			if (!uriRepoMap.containsKey(uri)) {
+				return null;
+			}
+
+			Repository r = uriRepoMap.get(uri);
+			try {
+				Ref ref = r.findRef(refname);
+				if (ref == null) return null;
+
+				ref = r.peel(ref);
+				ObjectId id = ref.getObjectId();
+				return id;
+			} catch (IOException e) {
+				throw new InvalidRemoteException("", e);
+			}
+		}
+
+		@Override
+		public byte[] readFile(String uri, String refName, String path)
+			throws GitAPIException, IOException {
+			Repository repo = uriRepoMap.get(uri);
+
+			String idStr = refName + ":" + path;
+			ObjectId id = repo.resolve(idStr);
+			if (id == null) {
+				throw new RefNotFoundException(
+					String.format("repo %s does not have %s", repo.toString(), idStr));
+			}
+			try (ObjectReader reader = repo.newObjectReader()) {
+				return reader.open(id).getCachedBytes(Integer.MAX_VALUE);
+			}
+		}
+	}
+
+	@Test
+	public void absoluteRemoteURL() throws Exception {
+		Repository child =
+			Git.cloneRepository().setURI(groupADb.getDirectory().toURI().toString())
+				.setDirectory(createUniqueTestGitDir(true))
+				.setBare(true).call().getRepository();
+		Repository dest = Git.cloneRepository()
+			.setURI(db.getDirectory().toURI().toString()).setDirectory(createUniqueTestGitDir(true))
+			.setBare(true).call().getRepository();
+		String abs = "https://chromium.googlesource.com";
+		String repoUrl = "https://chromium.googlesource.com/chromium/src";
+		boolean fetchSlash = false;
+		boolean baseSlash = false;
+		do {
+			do {
+				String fetchUrl = fetchSlash ? abs + "/" : abs;
+				String baseUrl = baseSlash ? abs + "/" : abs;
+
+				StringBuilder xmlContent = new StringBuilder();
+				xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+					.append("<manifest>")
+					.append("<remote name=\"origin\" fetch=\"" + fetchUrl + "\" />")
+					.append("<default revision=\"master\" remote=\"origin\" />")
+					.append("<project path=\"src\" name=\"chromium/src\" />")
+					.append("</manifest>");
+				RepoCommand cmd = new RepoCommand(dest);
+
+				IndexedRepos repos = new IndexedRepos();
+				repos.put(repoUrl, child);
+
+				RevCommit commit = cmd
+					.setInputStream(new ByteArrayInputStream(xmlContent.toString().getBytes(StandardCharsets.UTF_8)))
+					.setRemoteReader(repos)
+					.setURI(baseUrl)
+					.setRecordRemoteBranch(true)
+					.setRecordSubmoduleLabels(true)
+					.call();
+
+				String idStr = commit.getId().name() + ":" + ".gitmodules";
+				ObjectId modId = dest.resolve(idStr);
+
+				try (ObjectReader reader = dest.newObjectReader()) {
+					byte[] bytes = reader.open(modId).getCachedBytes(Integer.MAX_VALUE);
+					Config base = new Config();
+					BlobBasedConfig cfg = new BlobBasedConfig(base, bytes);
+					String subUrl = cfg.getString("submodule", "src", "url");
+					assertEquals("https://chromium.googlesource.com/chromium/src", subUrl);
+				}
+				fetchSlash = !fetchSlash;
+			} while (fetchSlash);
+			baseSlash = !baseSlash;
+		} while (baseSlash);
+		child.close();
+		dest.close();
+	}
+
 	@Test
 	public void testAddRepoManifest() throws Exception {
 		StringBuilder xmlContent = new StringBuilder();
@@ -248,46 +364,42 @@
 
 	@Test
 	public void testBareRepo() throws Exception {
-		try (
-				Repository remoteDb = createBareRepository();
-				Repository tempDb = createWorkRepository()) {
-			StringBuilder xmlContent = new StringBuilder();
-			xmlContent
-					.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
-					.append("<manifest>")
-					.append("<remote name=\"remote1\" fetch=\".\" />")
-					.append("<default revision=\"master\" remote=\"remote1\" />")
-					.append("<project path=\"foo\" name=\"").append(defaultUri)
-					.append("\" />").append("</manifest>");
-			JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
-					xmlContent.toString());
-			RepoCommand command = new RepoCommand(remoteDb);
-			command.setPath(
-					tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
-					.setURI(rootUri).call();
-			// Clone it
-			File directory = createTempDirectory("testBareRepo");
-			Repository localDb = Git.cloneRepository().setDirectory(directory)
-					.setURI(remoteDb.getDirectory().toURI().toString()).call()
-					.getRepository();
-			// The .gitmodules file should exist
-			File gitmodules = new File(localDb.getWorkTree(), ".gitmodules");
-			assertTrue("The .gitmodules file should exist", gitmodules.exists());
-			// The first line of .gitmodules file should be expected
-			BufferedReader reader = new BufferedReader(new FileReader(
-					gitmodules));
-			String content = reader.readLine();
-			reader.close();
-			assertEquals(
-					"The first line of .gitmodules file should be as expected",
-					"[submodule \"foo\"]", content);
-			// The gitlink should be the same as remote head sha1
-			String gitlink = localDb.resolve(Constants.HEAD + ":foo").name();
-			localDb.close();
-			String remote = defaultDb.resolve(Constants.HEAD).name();
-			assertEquals("The gitlink should be the same as remote head",
-					remote, gitlink);
-		}
+		Repository remoteDb = createBareRepository();
+		Repository tempDb = createWorkRepository();
+
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+				.append("<manifest>")
+				.append("<remote name=\"remote1\" fetch=\".\" />")
+				.append("<default revision=\"master\" remote=\"remote1\" />")
+				.append("<project path=\"foo\" name=\"").append(defaultUri)
+				.append("\" />").append("</manifest>");
+		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+				xmlContent.toString());
+		RepoCommand command = new RepoCommand(remoteDb);
+		command.setPath(
+				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+				.setURI(rootUri).call();
+		// Clone it
+		File directory = createTempDirectory("testBareRepo");
+		Repository localDb = Git.cloneRepository().setDirectory(directory)
+				.setURI(remoteDb.getDirectory().toURI().toString()).call()
+				.getRepository();
+		// The .gitmodules file should exist
+		File gitmodules = new File(localDb.getWorkTree(), ".gitmodules");
+		assertTrue("The .gitmodules file should exist", gitmodules.exists());
+		// The first line of .gitmodules file should be expected
+		BufferedReader reader = new BufferedReader(new FileReader(gitmodules));
+		String content = reader.readLine();
+		reader.close();
+		assertEquals("The first line of .gitmodules file should be as expected",
+				"[submodule \"foo\"]", content);
+		// The gitlink should be the same as remote head sha1
+		String gitlink = localDb.resolve(Constants.HEAD + ":foo").name();
+		localDb.close();
+		String remote = defaultDb.resolve(Constants.HEAD).name();
+		assertEquals("The gitlink should be the same as remote head", remote,
+				gitlink);
 	}
 
 	@Test
@@ -370,208 +482,192 @@
 
 	@Test
 	public void testRevisionBare() throws Exception {
-		try (
-				Repository remoteDb = createBareRepository();
-				Repository tempDb = createWorkRepository()) {
-			StringBuilder xmlContent = new StringBuilder();
-			xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
-					.append("<manifest>")
-					.append("<remote name=\"remote1\" fetch=\".\" />")
-					.append("<default revision=\"").append(BRANCH)
-					.append("\" remote=\"remote1\" />")
-					.append("<project path=\"foo\" name=\"").append(defaultUri)
-					.append("\" />").append("</manifest>");
-			JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
-					xmlContent.toString());
-			RepoCommand command = new RepoCommand(remoteDb);
-			command.setPath(
-					tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
-					.setURI(rootUri).call();
-			// Clone it
-			File directory = createTempDirectory("testRevisionBare");
-			Repository localDb = Git.cloneRepository().setDirectory(directory)
-					.setURI(remoteDb.getDirectory().toURI().toString()).call()
-					.getRepository();
-			// The gitlink should be the same as oldCommitId
-			String gitlink = localDb.resolve(Constants.HEAD + ":foo").name();
-			localDb.close();
-			assertEquals("The gitlink is same as remote head",
-					oldCommitId.name(), gitlink);
-		}
+		Repository remoteDb = createBareRepository();
+		Repository tempDb = createWorkRepository();
+
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+				.append("<manifest>")
+				.append("<remote name=\"remote1\" fetch=\".\" />")
+				.append("<default revision=\"").append(BRANCH)
+				.append("\" remote=\"remote1\" />")
+				.append("<project path=\"foo\" name=\"").append(defaultUri)
+				.append("\" />").append("</manifest>");
+		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+				xmlContent.toString());
+		RepoCommand command = new RepoCommand(remoteDb);
+		command.setPath(
+				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+				.setURI(rootUri).call();
+		// Clone it
+		File directory = createTempDirectory("testRevisionBare");
+		Repository localDb = Git.cloneRepository().setDirectory(directory)
+				.setURI(remoteDb.getDirectory().toURI().toString()).call()
+				.getRepository();
+		// The gitlink should be the same as oldCommitId
+		String gitlink = localDb.resolve(Constants.HEAD + ":foo").name();
+		localDb.close();
+		assertEquals("The gitlink is same as remote head", oldCommitId.name(),
+				gitlink);
 	}
 
 	@Test
 	public void testCopyFileBare() throws Exception {
-		try (
-				Repository remoteDb = createBareRepository();
-				Repository tempDb = createWorkRepository()) {
-			StringBuilder xmlContent = new StringBuilder();
-			xmlContent
-					.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
-					.append("<manifest>")
-					.append("<remote name=\"remote1\" fetch=\".\" />")
-					.append("<default revision=\"master\" remote=\"remote1\" />")
-					.append("<project path=\"foo\" name=\"").append(defaultUri)
-					.append("\" revision=\"").append(BRANCH).append("\" >")
-					.append("<copyfile src=\"hello.txt\" dest=\"Hello\" />")
-					.append("<copyfile src=\"hello.txt\" dest=\"foo/Hello\" />")
-					.append("</project>").append("</manifest>");
-			JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
-					xmlContent.toString());
-			RepoCommand command = new RepoCommand(remoteDb);
-			command.setPath(
-					tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
-					.setURI(rootUri).call();
-			// Clone it
-			File directory = createTempDirectory("testCopyFileBare");
-			Repository localDb = Git.cloneRepository().setDirectory(directory)
-					.setURI(remoteDb.getDirectory().toURI().toString()).call()
-					.getRepository();
-			// The Hello file should exist
-			File hello = new File(localDb.getWorkTree(), "Hello");
-			assertTrue("The Hello file should exist", hello.exists());
-			// The foo/Hello file should be skipped.
-			File foohello = new File(localDb.getWorkTree(), "foo/Hello");
-			assertFalse(
-					"The foo/Hello file should be skipped", foohello.exists());
-			localDb.close();
-			// The content of Hello file should be expected
-			BufferedReader reader = new BufferedReader(new FileReader(hello));
-			String content = reader.readLine();
-			reader.close();
-			assertEquals("The Hello file should have expected content",
-					"branch world", content);
-		}
+		Repository remoteDb = createBareRepository();
+		Repository tempDb = createWorkRepository();
+
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+				.append("<manifest>")
+				.append("<remote name=\"remote1\" fetch=\".\" />")
+				.append("<default revision=\"master\" remote=\"remote1\" />")
+				.append("<project path=\"foo\" name=\"").append(defaultUri)
+				.append("\" revision=\"").append(BRANCH).append("\" >")
+				.append("<copyfile src=\"hello.txt\" dest=\"Hello\" />")
+				.append("<copyfile src=\"hello.txt\" dest=\"foo/Hello\" />")
+				.append("</project>").append("</manifest>");
+		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+				xmlContent.toString());
+		RepoCommand command = new RepoCommand(remoteDb);
+		command.setPath(
+				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+				.setURI(rootUri).call();
+		// Clone it
+		File directory = createTempDirectory("testCopyFileBare");
+		Repository localDb = Git.cloneRepository().setDirectory(directory)
+				.setURI(remoteDb.getDirectory().toURI().toString()).call()
+				.getRepository();
+		// The Hello file should exist
+		File hello = new File(localDb.getWorkTree(), "Hello");
+		assertTrue("The Hello file should exist", hello.exists());
+		// The foo/Hello file should be skipped.
+		File foohello = new File(localDb.getWorkTree(), "foo/Hello");
+		assertFalse("The foo/Hello file should be skipped", foohello.exists());
+		localDb.close();
+		// The content of Hello file should be expected
+		BufferedReader reader = new BufferedReader(new FileReader(hello));
+		String content = reader.readLine();
+		reader.close();
+		assertEquals("The Hello file should have expected content",
+				"branch world", content);
 	}
 
 	@Test
 	public void testReplaceManifestBare() throws Exception {
-		try (
-				Repository remoteDb = createBareRepository();
-				Repository tempDb = createWorkRepository()) {
-			StringBuilder xmlContent = new StringBuilder();
-			xmlContent
-					.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
-					.append("<manifest>")
-					.append("<remote name=\"remote1\" fetch=\".\" />")
-					.append("<default revision=\"master\" remote=\"remote1\" />")
-					.append("<project path=\"foo\" name=\"").append(defaultUri)
-					.append("\" revision=\"").append(BRANCH).append("\" >")
-					.append("<copyfile src=\"hello.txt\" dest=\"Hello\" />")
-					.append("</project>").append("</manifest>");
-			JGitTestUtil.writeTrashFile(tempDb, "old.xml",
-					xmlContent.toString());
-			RepoCommand command = new RepoCommand(remoteDb);
-			command.setPath(tempDb.getWorkTree().getAbsolutePath() + "/old.xml")
-					.setURI(rootUri).call();
-			xmlContent = new StringBuilder();
-			xmlContent
-					.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
-					.append("<manifest>")
-					.append("<remote name=\"remote1\" fetch=\".\" />")
-					.append("<default revision=\"master\" remote=\"remote1\" />")
-					.append("<project path=\"bar\" name=\"")
-					.append(defaultUri)
-					.append("\" revision=\"")
-					.append(BRANCH)
-					.append("\" >")
-					.append("<copyfile src=\"hello.txt\" dest=\"Hello.txt\" />")
-					.append("</project>").append("</manifest>");
-			JGitTestUtil.writeTrashFile(tempDb, "new.xml",
-					xmlContent.toString());
-			command = new RepoCommand(remoteDb);
-			command.setPath(tempDb.getWorkTree().getAbsolutePath() + "/new.xml")
-					.setURI(rootUri).call();
-			// Clone it
-			File directory = createTempDirectory("testReplaceManifestBare");
-			Repository localDb = Git.cloneRepository().setDirectory(directory)
-					.setURI(remoteDb.getDirectory().toURI().toString()).call()
-					.getRepository();
-			// The Hello file should not exist
-			File hello = new File(localDb.getWorkTree(), "Hello");
-			assertFalse("The Hello file shouldn't exist", hello.exists());
-			// The Hello.txt file should exist
-			File hellotxt = new File(localDb.getWorkTree(), "Hello.txt");
-			assertTrue("The Hello.txt file should exist", hellotxt.exists());
-			// The .gitmodules file should have 'submodule "bar"' and shouldn't
-			// have
-			// 'submodule "foo"' lines.
-			File dotmodules = new File(localDb.getWorkTree(),
-					Constants.DOT_GIT_MODULES);
-			localDb.close();
-			BufferedReader reader = new BufferedReader(new FileReader(
-					dotmodules));
-			boolean foo = false;
-			boolean bar = false;
-			while (true) {
-				String line = reader.readLine();
-				if (line == null)
-					break;
-				if (line.contains("submodule \"foo\""))
-					foo = true;
-				if (line.contains("submodule \"bar\""))
-					bar = true;
-			}
-			reader.close();
-			assertTrue("The bar submodule should exist", bar);
-			assertFalse("The foo submodule shouldn't exist", foo);
+		Repository remoteDb = createBareRepository();
+		Repository tempDb = createWorkRepository();
+
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+				.append("<manifest>")
+				.append("<remote name=\"remote1\" fetch=\".\" />")
+				.append("<default revision=\"master\" remote=\"remote1\" />")
+				.append("<project path=\"foo\" name=\"").append(defaultUri)
+				.append("\" revision=\"").append(BRANCH).append("\" >")
+				.append("<copyfile src=\"hello.txt\" dest=\"Hello\" />")
+				.append("</project>").append("</manifest>");
+		JGitTestUtil.writeTrashFile(tempDb, "old.xml", xmlContent.toString());
+		RepoCommand command = new RepoCommand(remoteDb);
+		command.setPath(tempDb.getWorkTree().getAbsolutePath() + "/old.xml")
+				.setURI(rootUri).call();
+		xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+				.append("<manifest>")
+				.append("<remote name=\"remote1\" fetch=\".\" />")
+				.append("<default revision=\"master\" remote=\"remote1\" />")
+				.append("<project path=\"bar\" name=\"").append(defaultUri)
+				.append("\" revision=\"").append(BRANCH).append("\" >")
+				.append("<copyfile src=\"hello.txt\" dest=\"Hello.txt\" />")
+				.append("</project>").append("</manifest>");
+		JGitTestUtil.writeTrashFile(tempDb, "new.xml", xmlContent.toString());
+		command = new RepoCommand(remoteDb);
+		command.setPath(tempDb.getWorkTree().getAbsolutePath() + "/new.xml")
+				.setURI(rootUri).call();
+		// Clone it
+		File directory = createTempDirectory("testReplaceManifestBare");
+		Repository localDb = Git.cloneRepository().setDirectory(directory)
+				.setURI(remoteDb.getDirectory().toURI().toString()).call()
+				.getRepository();
+		// The Hello file should not exist
+		File hello = new File(localDb.getWorkTree(), "Hello");
+		assertFalse("The Hello file shouldn't exist", hello.exists());
+		// The Hello.txt file should exist
+		File hellotxt = new File(localDb.getWorkTree(), "Hello.txt");
+		assertTrue("The Hello.txt file should exist", hellotxt.exists());
+		// The .gitmodules file should have 'submodule "bar"' and shouldn't
+		// have
+		// 'submodule "foo"' lines.
+		File dotmodules = new File(localDb.getWorkTree(),
+				Constants.DOT_GIT_MODULES);
+		localDb.close();
+		BufferedReader reader = new BufferedReader(new FileReader(dotmodules));
+		boolean foo = false;
+		boolean bar = false;
+		while (true) {
+			String line = reader.readLine();
+			if (line == null)
+				break;
+			if (line.contains("submodule \"foo\""))
+				foo = true;
+			if (line.contains("submodule \"bar\""))
+				bar = true;
 		}
+		reader.close();
+		assertTrue("The bar submodule should exist", bar);
+		assertFalse("The foo submodule shouldn't exist", foo);
 	}
 
 	@Test
 	public void testRemoveOverlappingBare() throws Exception {
-		try (
-				Repository remoteDb = createBareRepository();
-				Repository tempDb = createWorkRepository()) {
-			StringBuilder xmlContent = new StringBuilder();
-			xmlContent
-					.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
-					.append("<manifest>")
-					.append("<remote name=\"remote1\" fetch=\".\" />")
-					.append("<default revision=\"master\" remote=\"remote1\" />")
-					.append("<project path=\"foo/bar\" name=\"")
-					.append(groupBUri).append("\" />")
-					.append("<project path=\"a\" name=\"").append(groupAUri)
-					.append("\" />").append("<project path=\"foo\" name=\"")
-					.append(defaultUri).append("\" />").append("</manifest>");
-			JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
-					xmlContent.toString());
-			RepoCommand command = new RepoCommand(remoteDb);
-			command.setPath(
-					tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
-					.setURI(rootUri).call();
-			// Clone it
-			File directory = createTempDirectory("testRemoveOverlappingBare");
-			Repository localDb = Git.cloneRepository().setDirectory(directory)
-					.setURI(remoteDb.getDirectory().toURI().toString()).call()
-					.getRepository();
-			// The .gitmodules file should have 'submodule "foo"' and shouldn't
-			// have
-			// 'submodule "foo/bar"' lines.
-			File dotmodules = new File(localDb.getWorkTree(),
-					Constants.DOT_GIT_MODULES);
-			localDb.close();
-			BufferedReader reader = new BufferedReader(new FileReader(
-					dotmodules));
-			boolean foo = false;
-			boolean foobar = false;
-			boolean a = false;
-			while (true) {
-				String line = reader.readLine();
-				if (line == null)
-					break;
-				if (line.contains("submodule \"foo\""))
-					foo = true;
-				if (line.contains("submodule \"foo/bar\""))
-					foobar = true;
-				if (line.contains("submodule \"a\""))
-					a = true;
-			}
-			reader.close();
-			assertTrue("The foo submodule should exist", foo);
-			assertFalse("The foo/bar submodule shouldn't exist", foobar);
-			assertTrue("The a submodule should exist", a);
+		Repository remoteDb = createBareRepository();
+		Repository tempDb = createWorkRepository();
+
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+				.append("<manifest>")
+				.append("<remote name=\"remote1\" fetch=\".\" />")
+				.append("<default revision=\"master\" remote=\"remote1\" />")
+				.append("<project path=\"foo/bar\" name=\"").append(groupBUri)
+				.append("\" />").append("<project path=\"a\" name=\"")
+				.append(groupAUri).append("\" />")
+				.append("<project path=\"foo\" name=\"").append(defaultUri)
+				.append("\" />").append("</manifest>");
+		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+				xmlContent.toString());
+		RepoCommand command = new RepoCommand(remoteDb);
+		command.setPath(
+				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+				.setURI(rootUri).call();
+		// Clone it
+		File directory = createTempDirectory("testRemoveOverlappingBare");
+		Repository localDb = Git.cloneRepository().setDirectory(directory)
+				.setURI(remoteDb.getDirectory().toURI().toString()).call()
+				.getRepository();
+		// The .gitmodules file should have 'submodule "foo"' and shouldn't
+		// have
+		// 'submodule "foo/bar"' lines.
+		File dotmodules = new File(localDb.getWorkTree(),
+				Constants.DOT_GIT_MODULES);
+		localDb.close();
+		BufferedReader reader = new BufferedReader(new FileReader(dotmodules));
+		boolean foo = false;
+		boolean foobar = false;
+		boolean a = false;
+		while (true) {
+			String line = reader.readLine();
+			if (line == null)
+				break;
+			if (line.contains("submodule \"foo\""))
+				foo = true;
+			if (line.contains("submodule \"foo/bar\""))
+				foobar = true;
+			if (line.contains("submodule \"a\""))
+				a = true;
 		}
+		reader.close();
+		assertTrue("The foo submodule should exist", foo);
+		assertFalse("The foo/bar submodule shouldn't exist", foobar);
+		assertTrue("The a submodule should exist", a);
 	}
 
 	@Test
@@ -670,178 +766,157 @@
 
 	@Test
 	public void testTargetBranch() throws Exception {
-		try (
-				Repository remoteDb1 = createBareRepository();
-				Repository remoteDb2 = createBareRepository();
-				Repository tempDb = createWorkRepository()) {
-			StringBuilder xmlContent = new StringBuilder();
-			xmlContent
-					.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
-					.append("<manifest>")
-					.append("<remote name=\"remote1\" fetch=\".\" />")
-					.append("<default revision=\"master\" remote=\"remote1\" />")
-					.append("<project path=\"foo\" name=\"").append(defaultUri)
-					.append("\" />").append("</manifest>");
-			JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
-					xmlContent.toString());
-			RepoCommand command = new RepoCommand(remoteDb1);
-			command
-				.setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
-				.setURI(rootUri)
-				.setTargetBranch("test")
-				.call();
-			ObjectId branchId = remoteDb1.resolve(
-					Constants.R_HEADS + "test^{tree}");
-			command = new RepoCommand(remoteDb2);
-			command
-				.setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
-				.setURI(rootUri)
-				.call();
-			ObjectId defaultId = remoteDb2.resolve(Constants.HEAD + "^{tree}");
-			assertEquals(
+		Repository remoteDb1 = createBareRepository();
+		Repository remoteDb2 = createBareRepository();
+		Repository tempDb = createWorkRepository();
+
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+				.append("<manifest>")
+				.append("<remote name=\"remote1\" fetch=\".\" />")
+				.append("<default revision=\"master\" remote=\"remote1\" />")
+				.append("<project path=\"foo\" name=\"").append(defaultUri)
+				.append("\" />").append("</manifest>");
+		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+				xmlContent.toString());
+		RepoCommand command = new RepoCommand(remoteDb1);
+		command.setPath(
+				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+				.setURI(rootUri).setTargetBranch("test").call();
+		ObjectId branchId = remoteDb1
+				.resolve(Constants.R_HEADS + "test^{tree}");
+		command = new RepoCommand(remoteDb2);
+		command.setPath(
+				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+				.setURI(rootUri).call();
+		ObjectId defaultId = remoteDb2.resolve(Constants.HEAD + "^{tree}");
+		assertEquals(
 				"The tree id of branch db and default db should be the same",
 				branchId, defaultId);
-		}
 	}
 
 	@Test
 	public void testRecordRemoteBranch() throws Exception {
-		try (
-				Repository remoteDb = createBareRepository();
-				Repository tempDb = createWorkRepository()) {
-			StringBuilder xmlContent = new StringBuilder();
-			xmlContent
-				.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+		Repository remoteDb = createBareRepository();
+		Repository tempDb = createWorkRepository();
+
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
 				.append("<manifest>")
 				.append("<remote name=\"remote1\" fetch=\".\" />")
 				.append("<default revision=\"master\" remote=\"remote1\" />")
 				.append("<project path=\"with-branch\" ")
-					.append("revision=\"master\" ")
-					.append("name=\"").append(notDefaultUri).append("\" />")
+				.append("revision=\"master\" ").append("name=\"")
+				.append(notDefaultUri).append("\" />")
 				.append("<project path=\"with-long-branch\" ")
-					.append("revision=\"refs/heads/master\" ")
-					.append("name=\"").append(defaultUri).append("\" />")
-				.append("</manifest>");
-			JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+				.append("revision=\"refs/heads/master\" ").append("name=\"")
+				.append(defaultUri).append("\" />").append("</manifest>");
+		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
 				xmlContent.toString());
 
-			RepoCommand command = new RepoCommand(remoteDb);
-			command.setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
-				.setURI(rootUri)
-				.setRecordRemoteBranch(true)
-				.call();
-			// Clone it
-			File directory = createTempDirectory("testBareRepo");
-			try (Repository localDb = Git.cloneRepository()
-					.setDirectory(directory)
-					.setURI(remoteDb.getDirectory().toURI().toString()).call()
-					.getRepository();) {
-				// The .gitmodules file should exist
-				File gitmodules = new File(localDb.getWorkTree(),
-						".gitmodules");
-				assertTrue("The .gitmodules file should exist",
-						gitmodules.exists());
-				FileBasedConfig c = new FileBasedConfig(gitmodules,
-						FS.DETECTED);
-				c.load();
-				assertEquals("Recording remote branches should work for short branch descriptions", "master",
-						c.getString("submodule", "with-branch", "branch"));
-				assertEquals("Recording remote branches should work for full ref specs", "refs/heads/master",
-						c.getString("submodule", "with-long-branch", "branch"));
-			}
+		RepoCommand command = new RepoCommand(remoteDb);
+		command.setPath(
+				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+				.setURI(rootUri).setRecordRemoteBranch(true).call();
+		// Clone it
+		File directory = createTempDirectory("testBareRepo");
+		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
+				.setURI(remoteDb.getDirectory().toURI().toString()).call()
+				.getRepository();) {
+			// The .gitmodules file should exist
+			File gitmodules = new File(localDb.getWorkTree(), ".gitmodules");
+			assertTrue("The .gitmodules file should exist",
+					gitmodules.exists());
+			FileBasedConfig c = new FileBasedConfig(gitmodules, FS.DETECTED);
+			c.load();
+			assertEquals(
+					"Recording remote branches should work for short branch descriptions",
+					"master",
+					c.getString("submodule", "with-branch", "branch"));
+			assertEquals(
+					"Recording remote branches should work for full ref specs",
+					"refs/heads/master",
+					c.getString("submodule", "with-long-branch", "branch"));
 		}
 	}
 
 
 	@Test
 	public void testRecordSubmoduleLabels() throws Exception {
-		try (
-				Repository remoteDb = createBareRepository();
-				Repository tempDb = createWorkRepository()) {
-			StringBuilder xmlContent = new StringBuilder();
-			xmlContent
-				.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+		Repository remoteDb = createBareRepository();
+		Repository tempDb = createWorkRepository();
+
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
 				.append("<manifest>")
 				.append("<remote name=\"remote1\" fetch=\".\" />")
 				.append("<default revision=\"master\" remote=\"remote1\" />")
 				.append("<project path=\"test\" ")
-					.append("revision=\"master\" ")
-					.append("name=\"").append(notDefaultUri).append("\" ")
-					.append("groups=\"a1,a2\" />")
-				.append("</manifest>");
-			JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+				.append("revision=\"master\" ").append("name=\"")
+				.append(notDefaultUri).append("\" ")
+				.append("groups=\"a1,a2\" />").append("</manifest>");
+		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
 				xmlContent.toString());
 
-			RepoCommand command = new RepoCommand(remoteDb);
-			command.setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
-				.setURI(rootUri)
-				.setRecordSubmoduleLabels(true)
-				.call();
-			// Clone it
-			File directory = createTempDirectory("testBareRepo");
-			try (Repository localDb = Git.cloneRepository()
-					.setDirectory(directory)
-					.setURI(remoteDb.getDirectory().toURI().toString()).call()
-					.getRepository();) {
-				// The .gitattributes file should exist
-				File gitattributes = new File(localDb.getWorkTree(),
+		RepoCommand command = new RepoCommand(remoteDb);
+		command.setPath(
+				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+				.setURI(rootUri).setRecordSubmoduleLabels(true).call();
+		// Clone it
+		File directory = createTempDirectory("testBareRepo");
+		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
+				.setURI(remoteDb.getDirectory().toURI().toString()).call()
+				.getRepository();) {
+			// The .gitattributes file should exist
+			File gitattributes = new File(localDb.getWorkTree(),
 					".gitattributes");
-				assertTrue("The .gitattributes file should exist",
-						gitattributes.exists());
-				try (BufferedReader reader = new BufferedReader(
-						new FileReader(gitattributes));) {
-					String content = reader.readLine();
-					assertEquals(".gitattributes content should be as expected",
+			assertTrue("The .gitattributes file should exist",
+					gitattributes.exists());
+			try (BufferedReader reader = new BufferedReader(
+					new FileReader(gitattributes));) {
+				String content = reader.readLine();
+				assertEquals(".gitattributes content should be as expected",
 						"/test a1 a2", content);
-				}
 			}
 		}
 	}
 
 	@Test
 	public void testRecordShallowRecommendation() throws Exception {
-		try (
-				Repository remoteDb = createBareRepository();
-				Repository tempDb = createWorkRepository()) {
-			StringBuilder xmlContent = new StringBuilder();
-			xmlContent
-				.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+		Repository remoteDb = createBareRepository();
+		Repository tempDb = createWorkRepository();
+
+		StringBuilder xmlContent = new StringBuilder();
+		xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
 				.append("<manifest>")
 				.append("<remote name=\"remote1\" fetch=\".\" />")
 				.append("<default revision=\"master\" remote=\"remote1\" />")
-				.append("<project path=\"shallow-please\" ")
-					.append("name=\"").append(defaultUri).append("\" ")
-					.append("clone-depth=\"1\" />")
-				.append("<project path=\"non-shallow\" ")
-					.append("name=\"").append(defaultUri).append("\" />")
-				.append("</manifest>");
-			JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+				.append("<project path=\"shallow-please\" ").append("name=\"")
+				.append(defaultUri).append("\" ").append("clone-depth=\"1\" />")
+				.append("<project path=\"non-shallow\" ").append("name=\"")
+				.append(defaultUri).append("\" />").append("</manifest>");
+		JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
 				xmlContent.toString());
 
-			RepoCommand command = new RepoCommand(remoteDb);
-			command.setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
-				.setURI(rootUri)
-				.setRecommendShallow(true)
-				.call();
-			// Clone it
-			File directory = createTempDirectory("testBareRepo");
-			try (Repository localDb = Git.cloneRepository()
-					.setDirectory(directory)
-					.setURI(remoteDb.getDirectory().toURI().toString()).call()
-					.getRepository();) {
-				// The .gitmodules file should exist
-				File gitmodules = new File(localDb.getWorkTree(),
-						".gitmodules");
-				assertTrue("The .gitmodules file should exist",
-						gitmodules.exists());
-				FileBasedConfig c = new FileBasedConfig(gitmodules,
-						FS.DETECTED);
-				c.load();
-				assertEquals("Recording shallow configuration should work", "true",
-						c.getString("submodule", "shallow-please", "shallow"));
-				assertNull("Recording non shallow configuration should work",
-						c.getString("submodule", "non-shallow", "shallow"));
-			}
+		RepoCommand command = new RepoCommand(remoteDb);
+		command.setPath(
+				tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+				.setURI(rootUri).setRecommendShallow(true).call();
+		// Clone it
+		File directory = createTempDirectory("testBareRepo");
+		try (Repository localDb = Git.cloneRepository().setDirectory(directory)
+				.setURI(remoteDb.getDirectory().toURI().toString()).call()
+				.getRepository();) {
+			// The .gitmodules file should exist
+			File gitmodules = new File(localDb.getWorkTree(), ".gitmodules");
+			assertTrue("The .gitmodules file should exist",
+					gitmodules.exists());
+			FileBasedConfig c = new FileBasedConfig(gitmodules, FS.DETECTED);
+			c.load();
+			assertEquals("Recording shallow configuration should work", "true",
+					c.getString("submodule", "shallow-please", "shallow"));
+			assertNull("Recording non shallow configuration should work",
+					c.getString("submodule", "non-shallow", "shallow"));
 		}
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
index c7125bb..c70b6f2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
@@ -1,25 +1,34 @@
 package org.eclipse.jgit.internal.storage.dfs;
 
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC_REST;
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.INSERT;
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.io.IOException;
+import java.util.Collections;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
+import org.eclipse.jgit.junit.MockSystemReader;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevBlob;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.util.SystemReader;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -27,6 +36,7 @@
 	private TestRepository<InMemoryRepository> git;
 	private InMemoryRepository repo;
 	private DfsObjDatabase odb;
+	private MockSystemReader mockSystemReader;
 
 	@Before
 	public void setUp() throws IOException {
@@ -34,6 +44,13 @@
 		git = new TestRepository<>(new InMemoryRepository(desc));
 		repo = git.getRepository();
 		odb = repo.getObjectDatabase();
+		mockSystemReader = new MockSystemReader();
+		SystemReader.setInstance(mockSystemReader);
+	}
+
+	@After
+	public void tearDown() {
+		SystemReader.setInstance(null);
 	}
 
 	@Test
@@ -62,6 +79,70 @@
 	}
 
 	@Test
+	public void testRacyNoReusePrefersSmaller() throws Exception {
+		StringBuilder msg = new StringBuilder();
+		for (int i = 0; i < 100; i++) {
+			msg.append(i).append(": i am a teapot\n");
+		}
+		RevBlob a = git.blob(msg.toString());
+		RevCommit c0 = git.commit()
+				.add("tea", a)
+				.message("0")
+				.create();
+
+		msg.append("short and stout\n");
+		RevBlob b = git.blob(msg.toString());
+		RevCommit c1 = git.commit().parent(c0).tick(1)
+				.add("tea", b)
+				.message("1")
+				.create();
+		git.update("master", c1);
+
+		PackConfig cfg = new PackConfig();
+		cfg.setReuseObjects(false);
+		cfg.setReuseDeltas(false);
+		cfg.setDeltaCompress(false);
+		cfg.setThreads(1);
+		DfsGarbageCollector gc = new DfsGarbageCollector(repo);
+		gc.setGarbageTtl(0, TimeUnit.MILLISECONDS); // disable TTL
+		gc.setPackConfig(cfg);
+		run(gc);
+
+		assertEquals(1, odb.getPacks().length);
+		DfsPackDescription large = odb.getPacks()[0].getPackDescription();
+		assertSame(PackSource.GC, large.getPackSource());
+
+		cfg.setDeltaCompress(true);
+		gc = new DfsGarbageCollector(repo);
+		gc.setGarbageTtl(0, TimeUnit.MILLISECONDS); // disable TTL
+		gc.setPackConfig(cfg);
+		run(gc);
+
+		assertEquals(1, odb.getPacks().length);
+		DfsPackDescription small = odb.getPacks()[0].getPackDescription();
+		assertSame(PackSource.GC, small.getPackSource());
+		assertTrue(
+				"delta compression pack is smaller",
+				small.getFileSize(PACK) < large.getFileSize(PACK));
+		assertTrue(
+				"large pack is older",
+				large.getLastModified() < small.getLastModified());
+
+		// Forcefully reinsert the older larger GC pack.
+		odb.commitPack(Collections.singleton(large), null);
+		odb.clearCache();
+		assertEquals(2, odb.getPacks().length);
+
+		gc = new DfsGarbageCollector(repo);
+		gc.setGarbageTtl(0, TimeUnit.MILLISECONDS); // disable TTL
+		run(gc);
+
+		assertEquals(1, odb.getPacks().length);
+		DfsPackDescription rebuilt = odb.getPacks()[0].getPackDescription();
+		assertEquals(small.getFileSize(PACK), rebuilt.getFileSize(PACK));
+	}
+
+	@Test
 	public void testCollectionWithGarbage() throws Exception {
 		RevCommit commit0 = commit().message("0").create();
 		RevCommit commit1 = commit().message("1").parent(commit0).create();
@@ -169,6 +250,297 @@
 		}
 	}
 
+	@Test
+	public void testCollectionWithGarbageCoalescenceWithShortTtl()
+			throws Exception {
+		RevCommit commit0 = commit().message("0").create();
+		RevCommit commit1 = commit().message("1").parent(commit0).create();
+		git.update("master", commit0);
+
+		// Create commits at 1 minute intervals with 1 hour ttl.
+		for (int i = 0; i < 100; i++) {
+			mockSystemReader.tick(60);
+			commit1 = commit().message("g" + i).parent(commit1).create();
+
+			DfsGarbageCollector gc = new DfsGarbageCollector(repo);
+			gc.setGarbageTtl(1, TimeUnit.HOURS);
+			run(gc);
+
+			// Make sure we don't have more than 4 UNREACHABLE_GARBAGE packs
+			// because all the packs that are created in a 20 minutes interval
+			// should be coalesced and the packs older than 60 minutes should be
+			// removed due to ttl.
+			int count = countPacks(UNREACHABLE_GARBAGE);
+			assertTrue("Garbage pack count should not exceed 4, but found "
+					+ count, count <= 4);
+		}
+	}
+
+	@Test
+	public void testCollectionWithGarbageCoalescenceWithLongTtl()
+			throws Exception {
+		RevCommit commit0 = commit().message("0").create();
+		RevCommit commit1 = commit().message("1").parent(commit0).create();
+		git.update("master", commit0);
+
+		// Create commits at 1 hour intervals with 2 days ttl.
+		for (int i = 0; i < 100; i++) {
+			mockSystemReader.tick(3600);
+			commit1 = commit().message("g" + i).parent(commit1).create();
+
+			DfsGarbageCollector gc = new DfsGarbageCollector(repo);
+			gc.setGarbageTtl(2, TimeUnit.DAYS);
+			run(gc);
+
+			// Make sure we don't have more than 3 UNREACHABLE_GARBAGE packs
+			// because all the packs that are created in a single day should
+			// be coalesced and the packs older than 2 days should be
+			// removed due to ttl.
+			int count = countPacks(UNREACHABLE_GARBAGE);
+			assertTrue("Garbage pack count should not exceed 3, but found "
+					+ count, count <= 3);
+		}
+	}
+
+	@Test
+	public void testEstimateGcPackSizeInNewRepo() throws Exception {
+		RevCommit commit0 = commit().message("0").create();
+		RevCommit commit1 = commit().message("1").parent(commit0).create();
+		git.update("master", commit1);
+
+		// Packs start out as INSERT.
+		long inputPacksSize = 32;
+		assertEquals(2, odb.getPacks().length);
+		for (DfsPackFile pack : odb.getPacks()) {
+			assertEquals(INSERT, pack.getPackDescription().getPackSource());
+			inputPacksSize += pack.getPackDescription().getFileSize(PACK) - 32;
+		}
+
+		gcNoTtl();
+
+		// INSERT packs are combined into a single GC pack.
+		assertEquals(1, odb.getPacks().length);
+		DfsPackFile pack = odb.getPacks()[0];
+		assertEquals(GC, pack.getPackDescription().getPackSource());
+		assertEquals(inputPacksSize,
+				pack.getPackDescription().getEstimatedPackSize());
+	}
+
+	@Test
+	public void testEstimateGcPackSizeWithAnExistingGcPack() throws Exception {
+		RevCommit commit0 = commit().message("0").create();
+		RevCommit commit1 = commit().message("1").parent(commit0).create();
+		git.update("master", commit1);
+
+		gcNoTtl();
+
+		RevCommit commit2 = commit().message("2").parent(commit1).create();
+		git.update("master", commit2);
+
+		// There will be one INSERT pack and one GC pack.
+		assertEquals(2, odb.getPacks().length);
+		boolean gcPackFound = false;
+		boolean insertPackFound = false;
+		long inputPacksSize = 32;
+		for (DfsPackFile pack : odb.getPacks()) {
+			DfsPackDescription d = pack.getPackDescription();
+			if (d.getPackSource() == GC) {
+				gcPackFound = true;
+			} else if (d.getPackSource() == INSERT) {
+				insertPackFound = true;
+			} else {
+				fail("unexpected " + d.getPackSource());
+			}
+			inputPacksSize += d.getFileSize(PACK) - 32;
+		}
+		assertTrue(gcPackFound);
+		assertTrue(insertPackFound);
+
+		gcNoTtl();
+
+		// INSERT pack is combined into the GC pack.
+		DfsPackFile pack = odb.getPacks()[0];
+		assertEquals(GC, pack.getPackDescription().getPackSource());
+		assertEquals(inputPacksSize,
+				pack.getPackDescription().getEstimatedPackSize());
+	}
+
+	@Test
+	public void testEstimateGcRestPackSizeInNewRepo() throws Exception {
+		RevCommit commit0 = commit().message("0").create();
+		RevCommit commit1 = commit().message("1").parent(commit0).create();
+		git.update("refs/notes/note1", commit1);
+
+		// Packs start out as INSERT.
+		long inputPacksSize = 32;
+		assertEquals(2, odb.getPacks().length);
+		for (DfsPackFile pack : odb.getPacks()) {
+			assertEquals(INSERT, pack.getPackDescription().getPackSource());
+			inputPacksSize += pack.getPackDescription().getFileSize(PACK) - 32;
+		}
+
+		gcNoTtl();
+
+		// INSERT packs are combined into a single GC_REST pack.
+		assertEquals(1, odb.getPacks().length);
+		DfsPackFile pack = odb.getPacks()[0];
+		assertEquals(GC_REST, pack.getPackDescription().getPackSource());
+		assertEquals(inputPacksSize,
+				pack.getPackDescription().getEstimatedPackSize());
+	}
+
+	@Test
+	public void testEstimateGcRestPackSizeWithAnExistingGcPack()
+			throws Exception {
+		RevCommit commit0 = commit().message("0").create();
+		RevCommit commit1 = commit().message("1").parent(commit0).create();
+		git.update("refs/notes/note1", commit1);
+
+		gcNoTtl();
+
+		RevCommit commit2 = commit().message("2").parent(commit1).create();
+		git.update("refs/notes/note2", commit2);
+
+		// There will be one INSERT pack and one GC_REST pack.
+		assertEquals(2, odb.getPacks().length);
+		boolean gcRestPackFound = false;
+		boolean insertPackFound = false;
+		long inputPacksSize = 32;
+		for (DfsPackFile pack : odb.getPacks()) {
+			DfsPackDescription d = pack.getPackDescription();
+			if (d.getPackSource() == GC_REST) {
+				gcRestPackFound = true;
+			} else if (d.getPackSource() == INSERT) {
+				insertPackFound = true;
+			} else {
+				fail("unexpected " + d.getPackSource());
+			}
+			inputPacksSize += d.getFileSize(PACK) - 32;
+		}
+		assertTrue(gcRestPackFound);
+		assertTrue(insertPackFound);
+
+		gcNoTtl();
+
+		// INSERT pack is combined into the GC_REST pack.
+		DfsPackFile pack = odb.getPacks()[0];
+		assertEquals(GC_REST, pack.getPackDescription().getPackSource());
+		assertEquals(inputPacksSize,
+				pack.getPackDescription().getEstimatedPackSize());
+	}
+
+	@Test
+	public void testEstimateGcPackSizesWithGcAndGcRestPacks() throws Exception {
+		RevCommit commit0 = commit().message("0").create();
+		git.update("head", commit0);
+		RevCommit commit1 = commit().message("1").parent(commit0).create();
+		git.update("refs/notes/note1", commit1);
+
+		gcNoTtl();
+
+		RevCommit commit2 = commit().message("2").parent(commit1).create();
+		git.update("refs/notes/note2", commit2);
+
+		// There will be one INSERT, one GC and one GC_REST packs.
+		assertEquals(3, odb.getPacks().length);
+		boolean gcPackFound = false;
+		boolean gcRestPackFound = false;
+		boolean insertPackFound = false;
+		long gcPackSize = 0;
+		long gcRestPackSize = 0;
+		long insertPackSize = 0;
+		for (DfsPackFile pack : odb.getPacks()) {
+			DfsPackDescription d = pack.getPackDescription();
+			if (d.getPackSource() == GC) {
+				gcPackFound = true;
+				gcPackSize = d.getFileSize(PACK);
+			} else if (d.getPackSource() == GC_REST) {
+				gcRestPackFound = true;
+				gcRestPackSize = d.getFileSize(PACK);
+			} else if (d.getPackSource() == INSERT) {
+				insertPackFound = true;
+				insertPackSize = d.getFileSize(PACK);
+			} else {
+				fail("unexpected " + d.getPackSource());
+			}
+		}
+		assertTrue(gcPackFound);
+		assertTrue(gcRestPackFound);
+		assertTrue(insertPackFound);
+
+		gcNoTtl();
+
+		// In this test INSERT pack would be combined into the GC_REST pack.
+		// But, as there is no good heuristic to know whether the new packs will
+		// be combined into a GC pack or GC_REST packs, the new pick size is
+		// considered while estimating both the GC and GC_REST packs.
+		assertEquals(2, odb.getPacks().length);
+		gcPackFound = false;
+		gcRestPackFound = false;
+		for (DfsPackFile pack : odb.getPacks()) {
+			DfsPackDescription d = pack.getPackDescription();
+			if (d.getPackSource() == GC) {
+				gcPackFound = true;
+				assertEquals(gcPackSize + insertPackSize - 32,
+						pack.getPackDescription().getEstimatedPackSize());
+			} else if (d.getPackSource() == GC_REST) {
+				gcRestPackFound = true;
+				assertEquals(gcRestPackSize + insertPackSize - 32,
+						pack.getPackDescription().getEstimatedPackSize());
+			} else {
+				fail("unexpected " + d.getPackSource());
+			}
+		}
+		assertTrue(gcPackFound);
+		assertTrue(gcRestPackFound);
+	}
+
+	@Test
+	public void testEstimateUnreachableGarbagePackSize() throws Exception {
+		RevCommit commit0 = commit().message("0").create();
+		RevCommit commit1 = commit().message("1").parent(commit0).create();
+		git.update("master", commit0);
+
+		assertTrue("commit0 reachable", isReachable(repo, commit0));
+		assertFalse("commit1 garbage", isReachable(repo, commit1));
+
+		// Packs start out as INSERT.
+		long packSize0 = 0;
+		long packSize1 = 0;
+		assertEquals(2, odb.getPacks().length);
+		for (DfsPackFile pack : odb.getPacks()) {
+			DfsPackDescription d = pack.getPackDescription();
+			assertEquals(INSERT, d.getPackSource());
+			if (isObjectInPack(commit0, pack)) {
+				packSize0 = d.getFileSize(PACK);
+			} else if (isObjectInPack(commit1, pack)) {
+				packSize1 = d.getFileSize(PACK);
+			} else {
+				fail("expected object not found in the pack");
+			}
+		}
+
+		gcNoTtl();
+
+		assertEquals(2, odb.getPacks().length);
+		for (DfsPackFile pack : odb.getPacks()) {
+			DfsPackDescription d = pack.getPackDescription();
+			if (d.getPackSource() == GC) {
+				// Even though just commit0 will end up in GC pack, because
+				// there is no good way to know that up front, both the pack
+				// sizes are considered while computing the estimated size of GC
+				// pack.
+				assertEquals(packSize0 + packSize1 - 32,
+						d.getEstimatedPackSize());
+			} else if (d.getPackSource() == UNREACHABLE_GARBAGE) {
+				// commit1 is moved to UNREACHABLE_GARBAGE pack.
+				assertEquals(packSize1, d.getEstimatedPackSize());
+			} else {
+				fail("unexpected " + d.getPackSource());
+			}
+		}
+	}
+
 	private TestRepository<InMemoryRepository>.CommitBuilder commit() {
 		return git.commit();
 	}
@@ -179,20 +551,17 @@
 		run(gc);
 	}
 
-	private void gcWithTtl() throws InterruptedException, IOException {
-		// Wait for the system clock to move by at least 1 millisecond.
-		// This allows the DfsGarbageCollector to recognize the boundary.
-		long start = System.currentTimeMillis();
-		do {
-			Thread.sleep(10);
-		} while (System.currentTimeMillis() <= start);
-
+	private void gcWithTtl() throws IOException {
+		// Move the clock forward by 1 minute and use the same as ttl.
+		mockSystemReader.tick(60);
 		DfsGarbageCollector gc = new DfsGarbageCollector(repo);
-		gc.setGarbageTtl(1, TimeUnit.MILLISECONDS);
+		gc.setGarbageTtl(1, TimeUnit.MINUTES);
 		run(gc);
 	}
 
 	private void run(DfsGarbageCollector gc) throws IOException {
+		// adjust the current time that will be used by the gc operation.
+		mockSystemReader.tick(1);
 		assertTrue("gc repacked", gc.pack(null));
 		odb.clearCache();
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
index b738f7e..e71ee6d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
@@ -206,7 +206,7 @@
 		// Verify that we have a foo in both packs, and 1 of them is garbage.
 		DfsReader reader = new DfsReader(db.getObjectDatabase());
 		DfsPackFile packs[] = db.getObjectDatabase().getPacks();
-		Set<PackSource> pack_sources = new HashSet<PackSource>();
+		Set<PackSource> pack_sources = new HashSet<>();
 
 		assertEquals(2, packs.length);
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java
new file mode 100644
index 0000000..db5b24a
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsPackCompacterTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2017, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.COMPACT;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.INSERT;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DfsPackCompacterTest {
+	private TestRepository<InMemoryRepository> git;
+	private InMemoryRepository repo;
+	private DfsObjDatabase odb;
+
+	@Before
+	public void setUp() throws IOException {
+		DfsRepositoryDescription desc = new DfsRepositoryDescription("test");
+		git = new TestRepository<>(new InMemoryRepository(desc));
+		repo = git.getRepository();
+		odb = repo.getObjectDatabase();
+	}
+
+	@Test
+	public void testEstimateCompactPackSizeInNewRepo() throws Exception {
+		RevCommit commit0 = commit().message("0").create();
+		RevCommit commit1 = commit().message("1").parent(commit0).create();
+		git.update("master", commit1);
+
+		// Packs start out as INSERT.
+		long inputPacksSize = 32;
+		assertEquals(2, odb.getPacks().length);
+		for (DfsPackFile pack : odb.getPacks()) {
+			assertEquals(INSERT, pack.getPackDescription().getPackSource());
+			inputPacksSize += pack.getPackDescription().getFileSize(PACK) - 32;
+		}
+
+		compact();
+
+		// INSERT packs are compacted into a single COMPACT pack.
+		assertEquals(1, odb.getPacks().length);
+		DfsPackFile pack = odb.getPacks()[0];
+		assertEquals(COMPACT, pack.getPackDescription().getPackSource());
+		assertEquals(inputPacksSize,
+				pack.getPackDescription().getEstimatedPackSize());
+	}
+
+	@Test
+	public void testEstimateGcPackSizeWithAnExistingGcPack() throws Exception {
+		RevCommit commit0 = commit().message("0").create();
+		RevCommit commit1 = commit().message("1").parent(commit0).create();
+		git.update("master", commit1);
+
+		compact();
+
+		RevCommit commit2 = commit().message("2").parent(commit1).create();
+		git.update("master", commit2);
+
+		// There will be one INSERT pack and one COMPACT pack.
+		assertEquals(2, odb.getPacks().length);
+		boolean compactPackFound = false;
+		boolean insertPackFound = false;
+		long inputPacksSize = 32;
+		for (DfsPackFile pack : odb.getPacks()) {
+			DfsPackDescription packDescription = pack.getPackDescription();
+			if (packDescription.getPackSource() == COMPACT) {
+				compactPackFound = true;
+			}
+			if (packDescription.getPackSource() == INSERT) {
+				insertPackFound = true;
+			}
+			inputPacksSize += packDescription.getFileSize(PACK) - 32;
+		}
+		assertTrue(compactPackFound);
+		assertTrue(insertPackFound);
+
+		compact();
+
+		// INSERT pack is combined into the COMPACT pack.
+		DfsPackFile pack = odb.getPacks()[0];
+		assertEquals(COMPACT, pack.getPackDescription().getPackSource());
+		assertEquals(inputPacksSize,
+				pack.getPackDescription().getEstimatedPackSize());
+	}
+
+	private TestRepository<InMemoryRepository>.CommitBuilder commit() {
+		return git.commit();
+	}
+
+	private void compact() throws IOException {
+		DfsPackCompactor compactor = new DfsPackCompactor(repo);
+		compactor.autoAdd();
+		compactor.compact(null);
+		odb.clearCache();
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
index ece8f67..343120e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
@@ -81,14 +81,16 @@
 
 	private TestRepository<Repository> test;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
 		db = createBareRepository();
 		reader = db.newObjectReader();
-		test = new TestRepository<Repository>(db);
+		test = new TestRepository<>(db);
 	}
 
+	@Override
 	@After
 	public void tearDown() throws Exception {
 		if (reader != null) {
@@ -169,7 +171,7 @@
 
 		ObjectId id = id("9d5b926ed164e8ee88d3b8b1e525d699adda01ba");
 		byte[] idBuf = toByteArray(id);
-		List<PackedObjectInfo> objects = new ArrayList<PackedObjectInfo>();
+		List<PackedObjectInfo> objects = new ArrayList<>();
 		for (int i = 0; i < 256; i++) {
 			idBuf[9] = (byte) i;
 			objects.add(new PackedObjectInfo(ObjectId.fromRaw(idBuf)));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ConcurrentRepackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ConcurrentRepackTest.java
index 1d71cb3..09438e9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ConcurrentRepackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ConcurrentRepackTest.java
@@ -77,6 +77,7 @@
 import org.junit.Test;
 
 public class ConcurrentRepackTest extends RepositoryTestCase {
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		WindowCacheConfig windowCacheConfig = new WindowCacheConfig();
@@ -85,6 +86,7 @@
 		super.setUp();
 	}
 
+	@Override
 	@After
 	public void tearDown() throws Exception {
 		super.tearDown();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
index 98a9501..9998666 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileSnapshotTest.java
@@ -57,7 +57,7 @@
 
 public class FileSnapshotTest {
 
-	private List<File> files = new ArrayList<File>();
+	private List<File> files = new ArrayList<>();
 
 	private File trash;
 
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..a2e1305 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,15 +44,21 @@
 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.ConfigConstants;
+import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.eclipse.jgit.storage.pack.PackConfig;
 import org.junit.Test;
 import org.junit.experimental.theories.DataPoints;
@@ -175,14 +181,9 @@
 		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 +191,26 @@
 		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 +221,7 @@
 		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,23 +232,102 @@
 		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);
 		assertEquals(1, stats.numberOfPackFiles);
-
 	}
 
-	private void configureGc(GC myGc, boolean aggressive) {
+	@Test
+	public void testImmediatePruning() 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();
+
+		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();
+
+		// We want to immediately prune deleted objects
+		FileBasedConfig config = repo.getConfig();
+		config.setString(ConfigConstants.CONFIG_GC_SECTION, null,
+			ConfigConstants.CONFIG_KEY_PRUNEEXPIRE, "now");
+		config.save();
+
+		//And we don't want to keep packs full of dead objects
+		gc.setPackExpireAgeMillis(0);
+
+		gc.gc();
+		stats = gc.getStatistics();
+		assertEquals(0, stats.numberOfLooseObjects);
+		assertEquals(6, stats.numberOfPackedObjects);
+		assertEquals(1, stats.numberOfPackFiles);
+	}
+
+	@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 +336,6 @@
 		} else
 			pconfig = new PackConfig(repo);
 		myGc.setPackConfig(pconfig);
+		return pconfig;
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java
index 776226b..643bb49 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcConcurrentTest.java
@@ -78,6 +78,7 @@
 		class DoRepack extends EmptyProgressMonitor implements
 				Callable<Integer> {
 
+			@Override
 			public void beginTask(String title, int totalWork) {
 				if (title.equals(JGitText.get().writingObjects)) {
 					try {
@@ -91,6 +92,7 @@
 			}
 
 			/** @return 0 for success, 1 in case of error when writing pack */
+			@Override
 			public Integer call() throws Exception {
 				try {
 					gc.setProgressMonitor(this);
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 @@
 				.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/GcOrphanFilesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java
index d9317b8..5393987 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcOrphanFilesTest.java
@@ -67,6 +67,7 @@
 
 	private File packDir;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
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..11a2a22 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 @@
 		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));
 	}
@@ -104,6 +105,7 @@
 		Callable<Integer> packRefs = new Callable<Integer>() {
 
 			/** @return 0 for success, 1 in case of error when writing pack */
+			@Override
 			public Integer call() throws Exception {
 				syncPoint.await();
 				try {
@@ -157,6 +159,7 @@
 		try {
 			Future<Result> result = pool.submit(new Callable<Result>() {
 
+				@Override
 				public Result call() throws Exception {
 					RefUpdate update = new RefDirectoryUpdate(
 							(RefDirectory) repo.getRefDatabase(),
@@ -181,6 +184,7 @@
 			});
 
 			pool.submit(new Callable<Void>() {
+				@Override
 				public Void call() throws Exception {
 					refUpdateLockedRef.await();
 					gc.packRefs();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java
index 90c1152..d16998d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java
@@ -62,15 +62,17 @@
 	protected GC gc;
 	protected RepoStatistics stats;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
 		repo = createWorkRepository();
-		tr = new TestRepository<FileRepository>(repo, new RevWalk(repo),
+		tr = new TestRepository<>(repo, new RevWalk(repo),
 				mockSystemReader);
 		gc = new GC(repo);
 	}
 
+	@Override
 	@After
 	public void tearDown() throws Exception {
 		super.tearDown();
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..f1cbb99 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 @@
 					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,9 +181,44 @@
 		}
 	}
 
+	@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>() {
+			@Override
 			public ObjectId call() throws Exception {
 				return dir.newInserter().insert(Constants.OBJ_BLOB, new byte[0]);
 			}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java
index 1c10bb3..91bd523 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java
@@ -107,6 +107,7 @@
 		return rng;
 	}
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -116,10 +117,11 @@
 		cfg.install();
 
 		repo = createBareRepository();
-		tr = new TestRepository<Repository>(repo);
+		tr = new TestRepository<>(repo);
 		wc = (WindowCursor) repo.newObjectReader();
 	}
 
+	@Override
 	@After
 	public void tearDown() throws Exception {
 		if (wc != null)
@@ -289,7 +291,7 @@
 
 			f = new FileOutputStream(idxName);
 			try {
-				List<PackedObjectInfo> list = new ArrayList<PackedObjectInfo>();
+				List<PackedObjectInfo> list = new ArrayList<>();
 				list.add(a);
 				list.add(b);
 				Collections.sort(list);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
index b6aa4bc..9a43742 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackIndexTestCase.java
@@ -62,6 +62,7 @@
 
 	PackIndex denseIdx;
 
+	@Override
 	public void setUp() throws Exception {
 		super.setUp();
 		smallIdx = PackIndex.open(getFileForPack34be9032());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexTest.java
index a4aa8bc..91dac09 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackReverseIndexTest.java
@@ -65,6 +65,7 @@
 	/**
 	 * Set up tested class instance, test constructor by the way.
 	 */
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
index 2e57952..c817dc3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
@@ -131,6 +131,7 @@
 
 	private RevCommit c5;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -143,6 +144,7 @@
 		write(alt, db.getObjectDatabase().getDirectory().getAbsolutePath() + "\n");
 	}
 
+	@Override
 	@After
 	public void tearDown() throws Exception {
 		if (writer != null) {
@@ -364,8 +366,8 @@
 				ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"),
 				ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
 				ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"),
-				ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"),
-				ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") };
+				ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") ,
+				ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") };
 		try (final RevWalk parser = new RevWalk(db)) {
 			final RevObject forcedOrderRevs[] = new RevObject[forcedOrder.length];
 			for (int i = 0; i < forcedOrder.length; i++)
@@ -412,6 +414,8 @@
 	 */
 	@Test
 	public void testWritePack2SizeDeltasVsNoDeltas() throws Exception {
+		config.setReuseDeltas(false);
+		config.setDeltaCompress(false);
 		testWritePack2();
 		final long sizePack2NoDeltas = os.size();
 		tearDown();
@@ -465,7 +469,7 @@
 	public void testDeltaStatistics() throws Exception {
 		config.setDeltaCompress(true);
 		FileRepository repo = createBareRepository();
-		TestRepository<FileRepository> testRepo = new TestRepository<FileRepository>(repo);
+		TestRepository<FileRepository> testRepo = new TestRepository<>(repo);
 		ArrayList<RevObject> blobs = new ArrayList<>();
 		blobs.add(testRepo.blob(genDeltableData(1000)));
 		blobs.add(testRepo.blob(genDeltableData(1005)));
@@ -534,7 +538,7 @@
 	public void testExclude() throws Exception {
 		FileRepository repo = createBareRepository();
 
-		TestRepository<FileRepository> testRepo = new TestRepository<FileRepository>(
+		TestRepository<FileRepository> testRepo = new TestRepository<>(
 				repo);
 		BranchBuilder bb = testRepo.branch("refs/heads/master");
 		contentA = testRepo.blob("A");
@@ -659,7 +663,7 @@
 
 	private FileRepository setupRepoForShallowFetch() throws Exception {
 		FileRepository repo = createBareRepository();
-		TestRepository<Repository> r = new TestRepository<Repository>(repo);
+		TestRepository<Repository> r = new TestRepository<>(repo);
 		BranchBuilder bb = r.branch("refs/heads/master");
 		contentA = r.blob("A");
 		contentB = r.blob("B");
@@ -727,7 +731,7 @@
 	// TODO: testWritePackDeltasDepth()
 
 	private void writeVerifyPack1() throws IOException {
-		final HashSet<ObjectId> interestings = new HashSet<ObjectId>();
+		final HashSet<ObjectId> interestings = new HashSet<>();
 		interestings.add(ObjectId
 				.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
 		createVerifyOpenPack(interestings, NONE, false, false);
@@ -739,8 +743,8 @@
 				ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
 				ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"),
 				ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"),
-				ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"),
-				ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") };
+				ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3"),
+				ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") };
 
 		assertEquals(expectedOrder.length, writer.getObjectCount());
 		verifyObjectsOrder(expectedOrder);
@@ -750,10 +754,10 @@
 
 	private void writeVerifyPack2(boolean deltaReuse) throws IOException {
 		config.setReuseDeltas(deltaReuse);
-		final HashSet<ObjectId> interestings = new HashSet<ObjectId>();
+		final HashSet<ObjectId> interestings = new HashSet<>();
 		interestings.add(ObjectId
 				.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
-		final HashSet<ObjectId> uninterestings = new HashSet<ObjectId>();
+		final HashSet<ObjectId> uninterestings = new HashSet<>();
 		uninterestings.add(ObjectId
 				.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"));
 		createVerifyOpenPack(interestings, uninterestings, false, false);
@@ -763,13 +767,11 @@
 				ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"),
 				ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
 				ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"),
-				ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259"),
-				ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") };
-		if (deltaReuse) {
-			// objects order influenced (swapped) by delta-base first rule
-			ObjectId temp = expectedOrder[4];
-			expectedOrder[4] = expectedOrder[5];
-			expectedOrder[5] = temp;
+				ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") ,
+				ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") };
+		if (!config.isReuseDeltas() && !config.isDeltaCompress()) {
+			// If no deltas are in the file the final two entries swap places.
+			swap(expectedOrder, 4, 5);
 		}
 		assertEquals(expectedOrder.length, writer.getObjectCount());
 		verifyObjectsOrder(expectedOrder);
@@ -777,11 +779,17 @@
 				.computeName().name());
 	}
 
+	private static void swap(ObjectId[] arr, int a, int b) {
+		ObjectId tmp = arr[a];
+		arr[a] = arr[b];
+		arr[b] = tmp;
+	}
+
 	private void writeVerifyPack4(final boolean thin) throws IOException {
-		final HashSet<ObjectId> interestings = new HashSet<ObjectId>();
+		final HashSet<ObjectId> interestings = new HashSet<>();
 		interestings.add(ObjectId
 				.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
-		final HashSet<ObjectId> uninterestings = new HashSet<ObjectId>();
+		final HashSet<ObjectId> uninterestings = new HashSet<>();
 		uninterestings.add(ObjectId
 				.fromString("c59759f143fb1fe21c197981df75a7ee00290799"));
 		createVerifyOpenPack(interestings, uninterestings, thin, false);
@@ -870,12 +878,13 @@
 	}
 
 	private void verifyObjectsOrder(final ObjectId objectsOrder[]) {
-		final List<PackIndex.MutableEntry> entries = new ArrayList<PackIndex.MutableEntry>();
+		final List<PackIndex.MutableEntry> entries = new ArrayList<>();
 
 		for (MutableEntry me : pack) {
 			entries.add(me.cloneEntry());
 		}
 		Collections.sort(entries, new Comparator<PackIndex.MutableEntry>() {
+			@Override
 			public int compare(MutableEntry o1, MutableEntry o2) {
 				return Long.signum(o1.getOffset() - o2.getOffset());
 			}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
index 5999acf..53db123 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
@@ -100,6 +100,7 @@
 
 	private RevTag v1_0;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -107,7 +108,7 @@
 		diskRepo = createBareRepository();
 		refdir = (RefDirectory) diskRepo.getRefDatabase();
 
-		repo = new TestRepository<Repository>(diskRepo);
+		repo = new TestRepository<>(diskRepo);
 		A = repo.commit().create();
 		B = repo.commit(repo.getRevWalk().parseCommit(A));
 		v1_0 = repo.tag("v1_0", B);
@@ -547,6 +548,7 @@
 		ListenerHandle listener = Repository.getGlobalListenerList()
 				.addRefsChangedListener(new RefsChangedListener() {
 
+					@Override
 					public void onRefsChanged(RefsChangedEvent event) {
 						count[0]++;
 					}
@@ -1021,7 +1023,7 @@
 		assertEquals(v0_1.getId(), all.get("refs/tags/v0.1").getObjectId());
 
 		all = refdir.getRefs(RefDatabase.ALL);
-		refdir.pack(new ArrayList<String>(all.keySet()));
+		refdir.pack(new ArrayList<>(all.keySet()));
 
 		all = refdir.getRefs(RefDatabase.ALL);
 		assertEquals(5, all.size());
@@ -1265,12 +1267,13 @@
 		final RefDatabase refDb = newRepo.getRefDatabase();
 		File packedRefs = new File(newRepo.getDirectory(), "packed-refs");
 		assertTrue(packedRefs.createNewFile());
-		final AtomicReference<StackOverflowError> error = new AtomicReference<StackOverflowError>();
-		final AtomicReference<IOException> exception = new AtomicReference<IOException>();
+		final AtomicReference<StackOverflowError> error = new AtomicReference<>();
+		final AtomicReference<IOException> exception = new AtomicReference<>();
 		final AtomicInteger changeCount = new AtomicInteger();
 		newRepo.getListenerList().addRefsChangedListener(
 				new RefsChangedListener() {
 
+					@Override
 					public void onRefsChanged(RefsChangedEvent event) {
 						try {
 							refDb.getRefs("ref");
@@ -1440,23 +1443,28 @@
 	private static final class StrictWorkMonitor implements ProgressMonitor {
 		private int lastWork, totalWork;
 
+		@Override
 		public void start(int totalTasks) {
 			// empty
 		}
 
+		@Override
 		public void beginTask(String title, int total) {
 			this.totalWork = total;
 			lastWork = 0;
 		}
 
+		@Override
 		public void update(int completed) {
 			lastWork += completed;
 		}
 
+		@Override
 		public void endTask() {
 			assertEquals("Units of work recorded", totalWork, lastWork);
 		}
 
+		@Override
 		public boolean isCancelled() {
 			return false;
 		}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
index 4be1d6d..daef91d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
@@ -239,14 +239,14 @@
 
 	@Test
 	public void testDeleteHeadInBareRepo() throws IOException {
-		try (Repository bareRepo = createBareRepository()) {
-			RefUpdate ref = bareRepo.updateRef(Constants.HEAD);
-			ref.setNewObjectId(ObjectId.fromString("0123456789012345678901234567890123456789"));
-			// Create the HEAD ref so we can delete it.
-			assertEquals(Result.NEW, ref.update());
-			ref = bareRepo.updateRef(Constants.HEAD);
-			delete(bareRepo, ref, Result.NO_CHANGE, true, true);
-		}
+		Repository bareRepo = createBareRepository();
+		RefUpdate ref = bareRepo.updateRef(Constants.HEAD);
+		ref.setNewObjectId(ObjectId
+				.fromString("0123456789012345678901234567890123456789"));
+		// Create the HEAD ref so we can delete it.
+		assertEquals(Result.NEW, ref.update());
+		ref = bareRepo.updateRef(Constants.HEAD);
+		delete(bareRepo, ref, Result.NO_CHANGE, true, true);
 	}
 	/**
 	 * Delete a loose ref and make sure the directory in refs is deleted too,
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UnpackedObjectTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UnpackedObjectTest.java
index c6653bf..c5ab766 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UnpackedObjectTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UnpackedObjectTest.java
@@ -93,6 +93,7 @@
 		return rng;
 	}
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -105,6 +106,7 @@
 		wc = (WindowCursor) repo.newObjectReader();
 	}
 
+	@Override
 	@After
 	public void tearDown() throws Exception {
 		if (wc != null)
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
index aa061ba..cc34838 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/WindowCacheGetTest.java
@@ -73,7 +73,7 @@
 	public void setUp() throws Exception {
 		super.setUp();
 
-		toLoad = new ArrayList<TestObject>();
+		toLoad = new ArrayList<>();
 		final BufferedReader br = new BufferedReader(new InputStreamReader(
 				new FileInputStream(JGitTestUtil
 						.getTestResourceFile("all_packed_objects.txt")),
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java
index 47f70d7..67a7819 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/LocalDiskRefTreeDatabaseTest.java
@@ -77,6 +77,7 @@
 	private RevCommit A;
 	private RevCommit B;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		FileRepository init = createWorkRepository();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java
index e4d0f1d..9aef943 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java
@@ -711,6 +711,7 @@
 			RefTreeDatabaseTest.this.refdb = refs;
 		}
 
+		@Override
 		public RefDatabase getRefDatabase() {
 			return refs;
 		}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BranchTrackingStatusTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BranchTrackingStatusTest.java
index e261b96..8e56419 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BranchTrackingStatusTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BranchTrackingStatusTest.java
@@ -60,7 +60,7 @@
 	@Override
 	public void setUp() throws Exception {
 		super.setUp();
-		util = new TestRepository<Repository>(db);
+		util = new TestRepository<>(db);
 		StoredConfig config = util.getRepository().getConfig();
 		config.setString(ConfigConstants.CONFIG_BRANCH_SECTION, "master",
 				ConfigConstants.CONFIG_KEY_REMOTE, "origin");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
index aebbafe..e9505f6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
@@ -139,7 +139,7 @@
 	@Test
 	public void test005_PutGetStringList() {
 		Config c = new Config();
-		final LinkedList<String> values = new LinkedList<String>();
+		final LinkedList<String> values = new LinkedList<>();
 		values.add("value1");
 		values.add("value2");
 		c.setStringList("my", null, "somename", values);
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..75b574e 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
@@ -130,7 +130,7 @@
 		if ((args.length % 2) > 0)
 			throw new IllegalArgumentException("needs to be pairs");
 
-		HashMap<String, String> map = new HashMap<String, String>();
+		HashMap<String, String> map = new HashMap<>();
 		for (int i = 0; i < args.length; i += 2) {
 			map.put(args[i], args[i + 1]);
 		}
@@ -228,7 +228,7 @@
 	@Test
 	public void testInitialCheckout() throws Exception {
 		try (Git git = new Git(db)) {
-			TestRepository<Repository> db_t = new TestRepository<Repository>(db);
+			TestRepository<Repository> db_t = new TestRepository<>(db);
 			BranchBuilder master = db_t.branch("master");
 			master.commit().add("f", "1").message("m0").create();
 			assertFalse(new File(db.getWorkTree(), "f").exists());
@@ -377,7 +377,7 @@
 		// rules 4 and 5
 		HashMap<String, String> idxMap;
 
-		idxMap = new HashMap<String, String>();
+		idxMap = new HashMap<>();
 		idxMap.put("foo", "foo");
 		setupCase(null, null, idxMap);
 		go();
@@ -387,7 +387,7 @@
 		assertTrue(getConflicts().isEmpty());
 
 		// rules 6 and 7
-		idxMap = new HashMap<String, String>();
+		idxMap = new HashMap<>();
 		idxMap.put("foo", "foo");
 		setupCase(null, idxMap, idxMap);
 		go();
@@ -396,7 +396,7 @@
 
 		// rules 8 and 9
 		HashMap<String, String> mergeMap;
-		mergeMap = new HashMap<String, String>();
+		mergeMap = new HashMap<>();
 
 		mergeMap.put("foo", "merge");
 		setupCase(null, mergeMap, idxMap);
@@ -408,7 +408,7 @@
 
 		// rule 10
 
-		HashMap<String, String> headMap = new HashMap<String, String>();
+		HashMap<String, String> headMap = new HashMap<>();
 		headMap.put("foo", "foo");
 		setupCase(headMap, null, idxMap);
 		go();
@@ -1705,7 +1705,8 @@
 							+ " 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/IndexDiffSubmoduleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
index 3259f62..0111b94 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
@@ -90,7 +90,6 @@
 				.setPath("modules/submodule")
 				.setURI(submoduleStandalone.getDirectory().toURI().toString())
 				.call();
-		submoduleStandalone.close();
 		submodule_trash = submodule_db.getWorkTree();
 		addRepoToClose(submodule_db);
 		writeTrashFile("fileInRoot", "root");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
index 733f166..2cb8f86 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
@@ -86,6 +86,7 @@
 		final ObjectId id = inserter.insert(Constants.OBJ_BLOB,
 				IO.readFully(f));
 		return new PathEdit(path) {
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.REGULAR_FILE);
 				ent.setLength(f.length());
@@ -209,7 +210,7 @@
 		diff.diff();
 
 		assertEquals("[b]",
-				new TreeSet<String>(diff.getChanged()).toString());
+				new TreeSet<>(diff.getChanged()).toString());
 		assertEquals("[]", diff.getAdded().toString());
 		assertEquals("[]", diff.getRemoved().toString());
 		assertEquals("[]", diff.getMissing().toString());
@@ -250,7 +251,7 @@
 		IndexDiff diff = new IndexDiff(db, Constants.HEAD, iterator);
 		diff.diff();
 
-		assertEquals("[]", new TreeSet<String>(diff.getChanged()).toString());
+		assertEquals("[]", new TreeSet<>(diff.getChanged()).toString());
 		assertEquals("[]", diff.getAdded().toString());
 		assertEquals("[]", diff.getRemoved().toString());
 		assertEquals("[]", diff.getMissing().toString());
@@ -290,7 +291,7 @@
 		IndexDiff diff = new IndexDiff(db, Constants.HEAD, iterator);
 		diff.diff();
 
-		assertEquals("[]", new TreeSet<String>(diff.getChanged()).toString());
+		assertEquals("[]", new TreeSet<>(diff.getChanged()).toString());
 		assertEquals("[]", diff.getAdded().toString());
 		assertEquals("[]", diff.getRemoved().toString());
 		assertEquals("[]", diff.getMissing().toString());
@@ -443,7 +444,7 @@
 			diff = new IndexDiff(db, Constants.HEAD,
 					new FileTreeIterator(db));
 			diff.diff();
-			assertEquals(new HashSet<String>(Arrays.asList("target")),
+			assertEquals(new HashSet<>(Arrays.asList("target")),
 					diff.getUntrackedFolders());
 
 			writeTrashFile("src/tst/A.java", "");
@@ -451,7 +452,7 @@
 
 			diff = new IndexDiff(db, Constants.HEAD, new FileTreeIterator(db));
 			diff.diff();
-			assertEquals(new HashSet<String>(Arrays.asList("target", "src/tst")),
+			assertEquals(new HashSet<>(Arrays.asList("target", "src/tst")),
 					diff.getUntrackedFolders());
 
 			git.rm().addFilepattern("src/com/B.java").addFilepattern("src/org")
@@ -462,7 +463,7 @@
 			diff = new IndexDiff(db, Constants.HEAD, new FileTreeIterator(db));
 			diff.diff();
 			assertEquals(
-					new HashSet<String>(Arrays.asList("src/org", "src/tst",
+					new HashSet<>(Arrays.asList("src/org", "src/tst",
 							"target")),
 					diff.getUntrackedFolders());
 		}
@@ -496,7 +497,7 @@
 
 			diff = new IndexDiff(db, Constants.HEAD, new FileTreeIterator(db));
 			diff.diff();
-			assertEquals(new HashSet<String>(Arrays.asList("src")),
+			assertEquals(new HashSet<>(Arrays.asList("src")),
 					diff.getUntrackedFolders());
 
 			git.add().addFilepattern("src").call();
@@ -509,7 +510,7 @@
 			diff = new IndexDiff(db, Constants.HEAD, new FileTreeIterator(db));
 			diff.diff();
 			assertEquals(
-					new HashSet<String>(Arrays.asList("srcs/com", "sr", "src/tst",
+					new HashSet<>(Arrays.asList("srcs/com", "sr", "src/tst",
 							"target")),
 					diff.getUntrackedFolders());
 		}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdOwnerMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdOwnerMapTest.java
index a36a5e9..898fcb6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdOwnerMapTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdOwnerMapTest.java
@@ -73,7 +73,7 @@
 
 	@Test
 	public void testEmptyMap() {
-		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<SubId>();
+		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<>();
 		assertTrue(m.isEmpty());
 		assertEquals(0, m.size());
 
@@ -86,7 +86,7 @@
 
 	@Test
 	public void testAddGetAndContains() {
-		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<SubId>();
+		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<>();
 		m.add(id_1);
 		m.add(id_2);
 		m.add(id_3);
@@ -108,7 +108,7 @@
 
 	@Test
 	public void testClear() {
-		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<SubId>();
+		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<>();
 
 		m.add(id_1);
 		assertSame(id_1, m.get(id_1));
@@ -126,7 +126,7 @@
 
 	@Test
 	public void testAddIfAbsent() {
-		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<SubId>();
+		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<>();
 		m.add(id_1);
 
 		assertSame(id_1, m.addIfAbsent(new SubId(id_1)));
@@ -145,7 +145,7 @@
 	@Test
 	public void testAddGrowsWithObjects() {
 		int n = 16384;
-		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<SubId>();
+		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<>();
 		m.add(id_1);
 		for (int i = 32; i < n; i++)
 			m.add(new SubId(id(i)));
@@ -159,7 +159,7 @@
 	@Test
 	public void testAddIfAbsentGrowsWithObjects() {
 		int n = 16384;
-		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<SubId>();
+		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<>();
 		m.add(id_1);
 		for (int i = 32; i < n; i++)
 			m.addIfAbsent(new SubId(id(i)));
@@ -172,7 +172,7 @@
 
 	@Test
 	public void testIterator() {
-		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<SubId>();
+		ObjectIdOwnerMap<SubId> m = new ObjectIdOwnerMap<>();
 		m.add(id_1);
 		m.add(id_2);
 		m.add(id_3);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSubclassMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSubclassMapTest.java
index 07561de..adb2b12 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSubclassMapTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdSubclassMapTest.java
@@ -73,7 +73,7 @@
 
 	@Test
 	public void testEmptyMap() {
-		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<SubId>();
+		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<>();
 		assertTrue(m.isEmpty());
 		assertEquals(0, m.size());
 
@@ -86,7 +86,7 @@
 
 	@Test
 	public void testAddGetAndContains() {
-		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<SubId>();
+		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<>();
 		m.add(id_1);
 		m.add(id_2);
 		m.add(id_3);
@@ -108,7 +108,7 @@
 
 	@Test
 	public void testClear() {
-		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<SubId>();
+		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<>();
 
 		m.add(id_1);
 		assertSame(id_1, m.get(id_1));
@@ -126,7 +126,7 @@
 
 	@Test
 	public void testAddIfAbsent() {
-		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<SubId>();
+		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<>();
 		m.add(id_1);
 
 		assertSame(id_1, m.addIfAbsent(new SubId(id_1)));
@@ -144,7 +144,7 @@
 
 	@Test
 	public void testAddGrowsWithObjects() {
-		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<SubId>();
+		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<>();
 		m.add(id_1);
 		for (int i = 32; i < 8000; i++)
 			m.add(new SubId(id(i)));
@@ -157,7 +157,7 @@
 
 	@Test
 	public void testAddIfAbsentGrowsWithObjects() {
-		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<SubId>();
+		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<>();
 		m.add(id_1);
 		for (int i = 32; i < 8000; i++)
 			m.addIfAbsent(new SubId(id(i)));
@@ -170,7 +170,7 @@
 
 	@Test
 	public void testIterator() {
-		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<SubId>();
+		ObjectIdSubclassMap<SubId> m = new ObjectIdSubclassMap<>();
 		m.add(id_1);
 		m.add(id_2);
 		m.add(id_3);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
index 80ece1c..0adea0b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
@@ -49,6 +49,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import java.util.Locale;
+
 import org.eclipse.jgit.errors.InvalidObjectIdException;
 import org.junit.Test;
 
@@ -122,7 +124,7 @@
 	public void test011_toString() {
 		final String x = "0123456789ABCDEFabcdef1234567890abcdefAB";
 		final ObjectId oid = ObjectId.fromString(x);
-		assertEquals(x.toLowerCase(), oid.name());
+		assertEquals(x.toLowerCase(Locale.ROOT), oid.name());
 	}
 
 	@Test(expected = InvalidObjectIdException.class)
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java
index aa46d1a..9236b4e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RacyGitTests.java
@@ -51,16 +51,19 @@
 import java.io.IOException;
 import java.util.TreeSet;
 
+import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.junit.RepositoryTestCase;
 import org.eclipse.jgit.treewalk.FileTreeIterator;
 import org.eclipse.jgit.treewalk.FileTreeIteratorWithTimeControl;
 import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
 import org.eclipse.jgit.util.FileUtils;
+import org.junit.Test;
 
 public class RacyGitTests extends RepositoryTestCase {
+	@Test
 	public void testIterator() throws IllegalStateException, IOException,
 			InterruptedException {
-		TreeSet<Long> modTimes = new TreeSet<Long>();
+		TreeSet<Long> modTimes = new TreeSet<>();
 		File lastFile = null;
 		for (int i = 0; i < 10; i++) {
 			lastFile = new File(db.getWorkTree(), "0." + i);
@@ -121,11 +124,16 @@
 		}
 	}
 
-	public void testRacyGitDetection() throws IOException,
-			IllegalStateException, InterruptedException {
-		TreeSet<Long> modTimes = new TreeSet<Long>();
+	@Test
+	public void testRacyGitDetection() throws Exception {
+		TreeSet<Long> modTimes = new TreeSet<>();
 		File lastFile;
 
+		// Reset to force creation of index file
+		try (Git git = new Git(db)) {
+			git.reset().call();
+		}
+
 		// wait to ensure that modtimes of the file doesn't match last index
 		// file modtime
 		modTimes.add(valueOf(fsTick(db.getIndexFile())));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java
index c88eb3b..1c21194 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefDatabaseConflictingNamesTest.java
@@ -63,7 +63,7 @@
 		@Override
 		public Map<String, Ref> getRefs(String prefix) throws IOException {
 			if (ALL.equals(prefix)) {
-				Map<String, Ref> existing = new HashMap<String, Ref>();
+				Map<String, Ref> existing = new HashMap<>();
 				existing.put("refs/heads/a/b", null /* not used */);
 				existing.put("refs/heads/q", null /* not used */);
 				return existing;
@@ -141,8 +141,8 @@
 
 	private void assertConflictingNames(String proposed, String... conflicts)
 			throws IOException {
-		Set<String> expected = new HashSet<String>(Arrays.asList(conflicts));
+		Set<String> expected = new HashSet<>(Arrays.asList(conflicts));
 		assertEquals(expected,
-				new HashSet<String>(refDatabase.getConflictingNames(proposed)));
+				new HashSet<>(refDatabase.getConflictingNames(proposed)));
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
index 707757b..7fb3309 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
@@ -99,7 +99,7 @@
 				"ab/c", "dummy", true);
 		config.save();
 		assertEquals("[ab/c, origin]",
-				new TreeSet<String>(db.getRemoteNames()).toString());
+				new TreeSet<>(db.getRemoteNames()).toString());
 
 		// one-level deep remote branch
 		assertEquals("master",
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java
index b44b4c3..1107c2c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java
@@ -109,7 +109,7 @@
 
 	@Test
 	public void testFileKeyOpenNew() throws IOException {
-		final Repository n = createBareRepository();
+		final Repository n = createRepository(true, false);
 		final File gitdir = n.getDirectory();
 		n.close();
 		recursiveDelete(gitdir);
@@ -187,12 +187,14 @@
 	}
 
 	@Test
-	public void testRepositoryUsageCountWithRegisteredRepository() {
-		assertEquals(1, ((Repository) db).useCnt.get());
-		RepositoryCache.register(db);
-		assertEquals(1, ((Repository) db).useCnt.get());
-		db.close();
-		assertEquals(0, ((Repository) db).useCnt.get());
+	public void testRepositoryUsageCountWithRegisteredRepository()
+			throws IOException {
+		Repository repo = createRepository(false, false);
+		assertEquals(1, repo.useCnt.get());
+		RepositoryCache.register(repo);
+		assertEquals(1, repo.useCnt.get());
+		repo.close();
+		assertEquals(0, repo.useCnt.get());
 	}
 
 	@Test
@@ -232,8 +234,8 @@
 
 	@Test
 	public void testRepositoryUnregisteringWhenExpired() throws Exception {
-		Repository repoA = createBareRepository();
-		Repository repoB = createBareRepository();
+		Repository repoA = createRepository(true, false);
+		Repository repoB = createRepository(true, false);
 		Repository repoC = createBareRepository();
 		RepositoryCache.register(repoA);
 		RepositoryCache.register(repoB);
@@ -265,11 +267,12 @@
 	}
 
 	@Test
-	public void testReconfigure() throws InterruptedException {
-		RepositoryCache.register(db);
-		assertTrue(RepositoryCache.isCached(db));
-		db.close();
-		assertTrue(RepositoryCache.isCached(db));
+	public void testReconfigure() throws InterruptedException, IOException {
+		Repository repo = createRepository(false, false);
+		RepositoryCache.register(repo);
+		assertTrue(RepositoryCache.isCached(repo));
+		repo.close();
+		assertTrue(RepositoryCache.isCached(repo));
 
 		// Actually, we would only need to validate that
 		// WorkQueue.getExecutor().scheduleWithFixedDelay is called with proper
@@ -287,7 +290,7 @@
 		// This wait will time out after 2048 ms
 		for (int i = 0; i <= 10; i++) {
 			Thread.sleep(1 << i);
-			if (!RepositoryCache.isCached(db)) {
+			if (!RepositoryCache.isCached(repo)) {
 				return;
 			}
 		}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
index 2845f8a..545a188 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ThreadSafeProgressMonitorTest.java
@@ -62,6 +62,7 @@
 		final ThreadSafeProgressMonitor pm = new ThreadSafeProgressMonitor(mock);
 
 		runOnThread(new Runnable() {
+			@Override
 			public void run() {
 				try {
 					pm.start(1);
@@ -128,6 +129,7 @@
 		final CountDownLatch doEndWorker = new CountDownLatch(1);
 
 		final Thread bg = new Thread() {
+			@Override
 			public void run() {
 				assertFalse(pm.isCancelled());
 
@@ -175,24 +177,29 @@
 
 		int value;
 
+		@Override
 		public void update(int completed) {
 			value += completed;
 		}
 
+		@Override
 		public void start(int totalTasks) {
 			value = totalTasks;
 		}
 
+		@Override
 		public void beginTask(String title, int totalWork) {
 			taskTitle = title;
 			value = totalWork;
 		}
 
+		@Override
 		public void endTask() {
 			taskTitle = null;
 			value = 0;
 		}
 
+		@Override
 		public boolean isCancelled() {
 			return false;
 		}
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..d431a89 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
@@ -44,6 +44,7 @@
 package org.eclipse.jgit.lib;
 
 import static org.eclipse.jgit.junit.Assert.assertEquals;
+import static org.junit.Assert.assertEquals;
 
 import org.eclipse.jgit.junit.MockSystemReader;
 import org.eclipse.jgit.util.SystemReader;
@@ -94,6 +95,25 @@
 		}
 	}
 
+	private static void assertNormalized(final String name,
+			final String expected) {
+		SystemReader instance = SystemReader.getInstance();
+		try {
+			setUnixSystemReader();
+			String normalized = Repository.normalizeBranchName(name);
+			assertEquals("Normalization of " + name, expected, normalized);
+			assertEquals("\"" + normalized + "\"", true,
+					Repository.isValidRefName(Constants.R_HEADS + normalized));
+			setWindowsSystemReader();
+			normalized = Repository.normalizeBranchName(name);
+			assertEquals("Normalization of " + name, expected, normalized);
+			assertEquals("\"" + normalized + "\"", true,
+					Repository.isValidRefName(Constants.R_HEADS + normalized));
+		} finally {
+			SystemReader.setInstance(instance);
+		}
+	}
+
 	@Test
 	public void testEmptyString() {
 		assertValid(false, "");
@@ -247,4 +267,59 @@
 		assertValid(true, "refs/heads/conx");
 		assertValid(true, "refs/heads/xcon");
 	}
+
+	@Test
+	public void testNormalizeBranchName() {
+		assertEquals(true, Repository.normalizeBranchName(null) == "");
+		assertEquals(true, Repository.normalizeBranchName("").equals(""));
+		assertNormalized("Bug 12345::::Hello World", "Bug_12345-Hello_World");
+		assertNormalized("Bug 12345 :::: Hello World", "Bug_12345_Hello_World");
+		assertNormalized("Bug 12345 :::: Hello::: World",
+				"Bug_12345_Hello-World");
+		assertNormalized(":::Bug 12345 - Hello World", "Bug_12345_Hello_World");
+		assertNormalized("---Bug 12345 - Hello World", "Bug_12345_Hello_World");
+		assertNormalized("Bug 12345 ---- Hello --- World",
+				"Bug_12345_Hello_World");
+		assertNormalized("Bug 12345 - Hello World!", "Bug_12345_Hello_World!");
+		assertNormalized("Bug 12345 : Hello World!", "Bug_12345_Hello_World!");
+		assertNormalized("Bug 12345 _ Hello World!", "Bug_12345_Hello_World!");
+		assertNormalized("Bug 12345   -       Hello World!",
+				"Bug_12345_Hello_World!");
+		assertNormalized(" Bug 12345   -   Hello World! ",
+				"Bug_12345_Hello_World!");
+		assertNormalized(" Bug 12345   -   Hello World!   ",
+				"Bug_12345_Hello_World!");
+		assertNormalized("Bug 12345   -   Hello______ World!",
+				"Bug_12345_Hello_World!");
+		assertNormalized("_Bug 12345 - Hello World!", "Bug_12345_Hello_World!");
+	}
+
+	@Test
+	public void testNormalizeWithSlashes() {
+		assertNormalized("foo/bar/baz", "foo/bar/baz");
+		assertNormalized("foo/bar.lock/baz.lock", "foo/bar_lock/baz_lock");
+		assertNormalized("foo/.git/.git~1/bar", "foo/git/git-1/bar");
+		assertNormalized(".foo/aux/con/com3.txt/com0/prn/lpt1",
+				"foo/+aux/+con/+com3.txt/com0/+prn/+lpt1");
+		assertNormalized("foo/../bar///.--ba...z", "foo/bar/ba.z");
+	}
+
+	@Test
+	public void testNormalizeWithUnicode() {
+		assertNormalized("f\u00f6\u00f6/.b\u00e0r/*[<>|^~/b\u00e9\\z",
+				"f\u00f6\u00f6/b\u00e0r/b\u00e9-z");
+		assertNormalized("\u5165\u53e3 entrance;/.\u51fa\u53e3_*ex*it*/",
+				"\u5165\u53e3_entrance;/\u51fa\u53e3_ex-it");
+	}
+
+	@Test
+	public void testNormalizeAlreadyValidRefName() {
+		assertNormalized("refs/heads/m.a.s.t.e.r", "refs/heads/m.a.s.t.e.r");
+		assertNormalized("refs/tags/v1.0-20170223", "refs/tags/v1.0-20170223");
+	}
+
+	@Test
+	public void testNormalizeTrimmedUnicodeAlreadyValidRefName() {
+		assertNormalized(" \u00e5ngstr\u00f6m\t", "\u00e5ngstr\u00f6m");
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java
index 0e7109c..039a6e8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java
@@ -110,7 +110,7 @@
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
-		db_t = new TestRepository<FileRepository>(db);
+		db_t = new TestRepository<>(db);
 	}
 
 	@Theory
@@ -778,7 +778,7 @@
 			db.close();
 			file.delete();
 			db = new FileRepository(db.getDirectory());
-			db_t = new TestRepository<FileRepository>(db);
+			db_t = new TestRepository<>(db);
 			break;
 		}
 	}
@@ -846,7 +846,7 @@
 				db.getConfig().setBoolean("core", null, "bare", true);
 				db.getDirectory().renameTo(new File(workTreeFile, "test.git"));
 				db = new FileRepository(new File(workTreeFile, "test.git"));
-				db_t = new TestRepository<FileRepository>(db);
+				db_t = new TestRepository<>(db);
 			}
 		} finally {
 			if (fos != null)
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/NLSTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/NLSTest.java
index a74ea98..65f2d09 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/NLSTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/NLSTest.java
@@ -123,6 +123,7 @@
 				this.locale = locale;
 			}
 
+			@Override
 			public TranslationBundle call() throws Exception {
 				NLS.setLocale(locale);
 				barrier.await(); // wait for the other thread to set its locale
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/DefaultNoteMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/DefaultNoteMergerTest.java
index d3a6f18..a8d3904 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/DefaultNoteMergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/DefaultNoteMergerTest.java
@@ -75,7 +75,7 @@
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
-		tr = new TestRepository<Repository>(db);
+		tr = new TestRepository<>(db);
 		reader = db.newObjectReader();
 		inserter = db.newObjectInserter();
 		merger = new DefaultNoteMerger();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapMergerTest.java
index be7bead..117ea0c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapMergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapMergerTest.java
@@ -97,7 +97,7 @@
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
-		tr = new TestRepository<Repository>(db);
+		tr = new TestRepository<>(db);
 		reader = db.newObjectReader();
 		inserter = db.newObjectInserter();
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapTest.java
index 325c9e2..a4a4909 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapTest.java
@@ -83,7 +83,7 @@
 	public void setUp() throws Exception {
 		super.setUp();
 
-		tr = new TestRepository<Repository>(db);
+		tr = new TestRepository<>(db);
 		reader = db.newObjectReader();
 		inserter = db.newObjectInserter();
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/AbstractPlotRendererTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/AbstractPlotRendererTest.java
index d32172a..f265315 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/AbstractPlotRendererTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/AbstractPlotRendererTest.java
@@ -96,7 +96,7 @@
 			throws IOException {
 		TestPlotWalk walk = new TestPlotWalk(db);
 		walk.markStart(walk.parseCommit(start));
-		PlotCommitList<PlotLane> commitList = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> commitList = new PlotCommitList<>();
 		commitList.source(walk);
 		commitList.fillTo(1000);
 		return commitList;
@@ -116,7 +116,7 @@
 	private static class TestPlotRenderer extends
 			AbstractPlotRenderer<PlotLane, Object> {
 
-		List<Integer> indentations = new LinkedList<Integer>();
+		List<Integer> indentations = new LinkedList<>();
 
 		@Override
 		protected int drawLabel(int x, int y, Ref ref) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java
index ecc119b..9a6043f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revplot/PlotCommitListTest.java
@@ -123,7 +123,7 @@
 	}
 
 	private static Set<Integer> asSet(int... numbers) {
-		Set<Integer> result = new HashSet<Integer>();
+		Set<Integer> result = new HashSet<>();
 		for (int n : numbers)
 			result.add(Integer.valueOf(n));
 		return result;
@@ -138,7 +138,7 @@
 		PlotWalk pw = new PlotWalk(db);
 		pw.markStart(pw.lookupCommit(c.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 
@@ -159,7 +159,7 @@
 		PlotWalk pw = new PlotWalk(db);
 		pw.markStart(pw.lookupCommit(d.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 
@@ -181,7 +181,7 @@
 		pw.markStart(pw.lookupCommit(b.getId()));
 		pw.markStart(pw.lookupCommit(c.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 
@@ -205,7 +205,7 @@
 		pw.markStart(pw.lookupCommit(c.getId()));
 		pw.markStart(pw.lookupCommit(d.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 
@@ -240,7 +240,7 @@
 		// pw.markStart(pw.lookupCommit(f.getId()));
 		pw.markStart(pw.lookupCommit(g.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 
@@ -274,7 +274,7 @@
 		pw.markStart(pw.lookupCommit(i.getId()));
 		pw.markStart(pw.lookupCommit(g.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 		Set<Integer> childPositions = asSet(0, 1);
@@ -333,7 +333,7 @@
 		PlotWalk pw = new PlotWalk(db);
 		pw.markStart(pw.lookupCommit(merge_fixed_logged_npe.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 
@@ -406,7 +406,7 @@
 		PlotWalk pw = new PlotWalk(db);
 		pw.markStart(pw.lookupCommit(m3));
 		pw.markStart(pw.lookupCommit(s2));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 
@@ -471,7 +471,7 @@
 		pw.markStart(pw.lookupCommit(e.getId()));
 		pw.markStart(pw.lookupCommit(a5.getId()));
 
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 
@@ -520,7 +520,7 @@
 		PlotWalk pw = new PlotWalk(db);
 		pw.markStart(pw.lookupCommit(a4));
 		pw.markStart(pw.lookupCommit(b3));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 
@@ -565,7 +565,7 @@
 		PlotWalk pw = new PlotWalk(db);
 		pw.markStart(pw.lookupCommit(a4));
 		pw.markStart(pw.lookupCommit(b3));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 
@@ -615,7 +615,7 @@
 		pw.markStart(pw.lookupCommit(a4));
 		pw.markStart(pw.lookupCommit(b2));
 		pw.markStart(pw.lookupCommit(c));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 
@@ -654,7 +654,7 @@
 		PlotWalk pw = new PlotWalk(db);
 		pw.markStart(pw.lookupCommit(a3));
 		pw.markStart(pw.lookupCommit(b1));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(2); // don't process a1
 
@@ -677,7 +677,7 @@
 		PlotWalk pw = new PlotWalk(db);
 		pw.markStart(pw.lookupCommit(a));
 		pw.markStart(pw.lookupCommit(b));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 
@@ -696,7 +696,7 @@
 		PlotWalk pw = new PlotWalk(db);
 		pw.markStart(pw.lookupCommit(a));
 		pw.markStart(pw.lookupCommit(b2));
-		PlotCommitList<PlotLane> pcl = new PlotCommitList<PlotLane>();
+		PlotCommitList<PlotLane> pcl = new PlotCommitList<>();
 		pcl.source(pw);
 		pcl.fillTo(Integer.MAX_VALUE);
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java
index 766a279..e5e51f7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/DateRevQueueTest.java
@@ -50,10 +50,12 @@
 import org.junit.Test;
 
 public class DateRevQueueTest extends RevQueueTestCase<DateRevQueue> {
+	@Override
 	protected DateRevQueue create() {
 		return new DateRevQueue();
 	}
 
+	@Override
 	@Test
 	public void testEmpty() throws Exception {
 		super.testEmpty();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FIFORevQueueTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FIFORevQueueTest.java
index 8877c38..a280045 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FIFORevQueueTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FIFORevQueueTest.java
@@ -52,10 +52,12 @@
 import org.junit.Test;
 
 public class FIFORevQueueTest extends RevQueueTestCase<FIFORevQueue> {
+	@Override
 	protected FIFORevQueue create() {
 		return new FIFORevQueue();
 	}
 
+	@Override
 	@Test
 	public void testEmpty() throws Exception {
 		super.testEmpty();
@@ -70,7 +72,7 @@
 
 	@Test
 	public void testAddLargeBlocks() throws Exception {
-		final ArrayList<RevCommit> lst = new ArrayList<RevCommit>();
+		final ArrayList<RevCommit> lst = new ArrayList<>();
 		for (int i = 0; i < 3 * BlockRevQueue.Block.BLOCK_SIZE; i++) {
 			final RevCommit c = commit();
 			lst.add(c);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/LIFORevQueueTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/LIFORevQueueTest.java
index 1b2c5e2..ed8bc70 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/LIFORevQueueTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/LIFORevQueueTest.java
@@ -53,10 +53,12 @@
 import org.junit.Test;
 
 public class LIFORevQueueTest extends RevQueueTestCase<LIFORevQueue> {
+	@Override
 	protected LIFORevQueue create() {
 		return new LIFORevQueue();
 	}
 
+	@Override
 	@Test
 	public void testEmpty() throws Exception {
 		super.testEmpty();
@@ -71,7 +73,7 @@
 
 	@Test
 	public void testAddLargeBlocks() throws Exception {
-		final ArrayList<RevCommit> lst = new ArrayList<RevCommit>();
+		final ArrayList<RevCommit> lst = new ArrayList<>();
 		for (int i = 0; i < 3 * BlockRevQueue.Block.BLOCK_SIZE; i++) {
 			final RevCommit c = commit();
 			lst.add(c);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitListTest.java
index 4d75322..c069ba8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitListTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitListTest.java
@@ -62,7 +62,7 @@
 			for (int i = 0; i < count; i++)
 				git.commit().setCommitter(committer).setAuthor(author)
 						.setMessage("commit " + i).call();
-			list = new RevCommitList<RevCommit>();
+			list = new RevCommitList<>();
 			w.markStart(w.lookupCommit(db.resolve(Constants.HEAD)));
 			list.source(w);
 		}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevFlagSetTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevFlagSetTest.java
index 154fcdf..2b5fc00 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevFlagSetTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevFlagSetTest.java
@@ -146,6 +146,5 @@
 		set.add(flag1);
 		assertTrue(set.contains(flag1));
 		assertFalse(set.contains(flag2));
-		assertFalse(set.contains("bob"));
 	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevQueueTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevQueueTestCase.java
index 51cf2f1..bedbedd 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevQueueTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevQueueTestCase.java
@@ -53,6 +53,7 @@
 		RevWalkTestCase {
 	protected T q;
 
+	@Override
 	public void setUp() throws Exception {
 		super.setUp();
 		q = create();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFollowFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFollowFilterTest.java
index 05e552e..1860185 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFollowFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFollowFilterTest.java
@@ -58,7 +58,7 @@
 public class RevWalkFollowFilterTest extends RevWalkTestCase {
 
 	private static class DiffCollector extends RenameCallback {
-		List<DiffEntry> diffs = new ArrayList<DiffEntry>();
+		List<DiffEntry> diffs = new ArrayList<>();
 
 		@Override
 		public void renamed(DiffEntry diff) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkPathFilter6012Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkPathFilter6012Test.java
index db19f00..631e395 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkPathFilter6012Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkPathFilter6012Test.java
@@ -69,6 +69,7 @@
 
 	private HashMap<RevCommit, String> byName;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -94,7 +95,7 @@
 		h = commit(f.getTree(), g, f);
 		i = commit(tree(file(pA, zS), file(pE, zY), file(pF, zF)), h);
 
-		byName = new HashMap<RevCommit, String>();
+		byName = new HashMap<>();
 		for (Field z : RevWalkPathFilter6012Test.class.getDeclaredFields()) {
 			if (z.getType() == RevCommit.class)
 				byName.put((RevCommit) z.get(this), z.getName());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
index 30586ee..f12cc08 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
@@ -62,7 +62,7 @@
 	@Override
 	public void setUp() throws Exception {
 		super.setUp();
-		util = new TestRepository<Repository>(db, createRevWalk());
+		util = new TestRepository<>(db, createRevWalk());
 		rw = util.getRevWalk();
 	}
 
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java
index a131e5e..5c46659 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleAddTest.java
@@ -161,6 +161,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleInitTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleInitTest.java
index 2b46498..c7a009c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleInitTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleInitTest.java
@@ -321,6 +321,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleStatusTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleStatusTest.java
index 51c180c..61df9d9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleStatusTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleStatusTest.java
@@ -92,6 +92,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
@@ -124,6 +125,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
@@ -164,6 +166,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
@@ -215,6 +218,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
@@ -261,6 +265,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
@@ -316,6 +321,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleSyncTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleSyncTest.java
index 54a6f77..13db44a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleSyncTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleSyncTest.java
@@ -95,6 +95,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
@@ -156,6 +157,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java
index bcdd5e2..7064f60 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleUpdateTest.java
@@ -93,6 +93,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(commit);
@@ -136,6 +137,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
@@ -171,6 +173,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
index 72b4611..8998a85 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
@@ -87,10 +87,11 @@
 public class SubmoduleWalkTest extends RepositoryTestCase {
 	private TestRepository<Repository> testDb;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
-		testDb = new TestRepository<Repository>(db);
+		testDb = new TestRepository<>(db);
 	}
 
 	@Test
@@ -118,6 +119,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
@@ -165,6 +167,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
@@ -217,6 +220,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
@@ -253,6 +257,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id);
@@ -286,6 +291,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path1) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id1);
@@ -293,6 +299,7 @@
 		});
 		editor.add(new PathEdit(path2) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(id2);
@@ -330,6 +337,7 @@
 		DirCacheEditor editor = cache.editor();
 		editor.add(new PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.GITLINK);
 				ent.setObjectId(subId);
@@ -337,6 +345,7 @@
 		});
 		editor.add(new PathEdit(DOT_GIT_MODULES) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.REGULAR_FILE);
 				ent.setObjectId(gitmodulesBlob);
@@ -375,6 +384,7 @@
 				.add(DOT_GIT_MODULES, gitmodules.toText())
 				.edit(new PathEdit(path) {
 
+							@Override
 							public void apply(DirCacheEntry ent) {
 								ent.setFileMode(FileMode.GITLINK);
 								ent.setObjectId(subId);
@@ -412,6 +422,7 @@
 				.add(DOT_GIT_MODULES, gitmodules.toText())
 				.edit(new PathEdit(path) {
 
+							@Override
 							public void apply(DirCacheEntry ent) {
 								ent.setFileMode(FileMode.GITLINK);
 								ent.setObjectId(subId);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java
index 3dc022d..ea15ebe 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HttpAuthTest.java
@@ -113,7 +113,7 @@
 	}
 
 	private static class AuthHeadersResponse extends JDKHttpConnection {
-		Map<String, List<String>> headerFields = new HashMap<String, List<String>>();
+		Map<String, List<String>> headerFields = new HashMap<>();
 
 		public AuthHeadersResponse(String[] authHeaders)
 				throws MalformedURLException, IOException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/LongMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/LongMapTest.java
index 3e427e5..1a86aaf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/LongMapTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/LongMapTest.java
@@ -57,7 +57,7 @@
 
 	@Before
 	public void setUp() throws Exception {
-		map = new LongMap<Long>();
+		map = new LongMap<>();
 	}
 
 	@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/NetRCTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/NetRCTest.java
index ba44950..2fea8a9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/NetRCTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/NetRCTest.java
@@ -62,6 +62,7 @@
 
 	private File configFile;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java
index 8ec39bf..fc520ab 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java
@@ -68,6 +68,7 @@
 
 	private OpenSshConfig osc;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
index c829be9..b2497b8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
@@ -169,7 +169,7 @@
 	@Test
 	public void testPackWithDuplicateBlob() throws Exception {
 		final byte[] data = Constants.encode("0123456789abcdefg");
-		TestRepository<Repository> d = new TestRepository<Repository>(db);
+		TestRepository<Repository> d = new TestRepository<>(db);
 		assertTrue(db.hasObject(d.blob(data)));
 
 		TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java
index 33a9105..0e4e9cc 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java
@@ -173,4 +173,27 @@
 			}
 		}
 	}
+
+	@Test
+	public void limitCommandBytes() throws IOException {
+		Map<String, RemoteRefUpdate> updates = new HashMap<>();
+		for (int i = 0; i < 4; i++) {
+			RemoteRefUpdate rru = new RemoteRefUpdate(
+					null, null, obj2, "refs/test/T" + i,
+					false, null, ObjectId.zeroId());
+			updates.put(rru.getRemoteName(), rru);
+		}
+
+		server.getConfig().setInt("receive", null, "maxCommandBytes", 190);
+		try (Transport tn = testProtocol.open(uri, client, "server");
+				PushConnection connection = tn.openPush()) {
+			try {
+				connection.push(NullProgressMonitor.INSTANCE, updates);
+				fail("server did not abort");
+			} catch (TransportException e) {
+				String msg = e.getMessage();
+				assertEquals("remote: Too many commands", msg);
+			}
+		}
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushOptionsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushOptionsTest.java
index c346d79..f26201d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushOptionsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushOptionsTest.java
@@ -86,6 +86,7 @@
 	private ObjectId obj2;
 	private ReceivePack receivePack;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -115,6 +116,7 @@
 		}
 	}
 
+	@Override
 	@After
 	public void tearDown() {
 		Transport.unregister(testProtocol);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java
index 28473c7..104a69c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushProcessTest.java
@@ -83,8 +83,8 @@
 	public void setUp() throws Exception {
 		super.setUp();
 		transport = new MockTransport(db, new URIish());
-		refUpdates = new HashSet<RemoteRefUpdate>();
-		advertisedRefs = new HashSet<Ref>();
+		refUpdates = new HashSet<>();
+		advertisedRefs = new HashSet<>();
 		connectionUpdateStatus = Status.OK;
 	}
 
@@ -421,7 +421,7 @@
 	private class MockPushConnection extends BaseConnection implements
 			PushConnection {
 		MockPushConnection() {
-			final Map<String, Ref> refsMap = new HashMap<String, Ref>();
+			final Map<String, Ref> refsMap = new HashMap<>();
 			for (final Ref r : advertisedRefs)
 				refsMap.put(r.getName(), r);
 			available(refsMap);
@@ -432,12 +432,14 @@
 			// nothing here
 		}
 
+		@Override
 		public void push(ProgressMonitor monitor,
 				Map<String, RemoteRefUpdate> refsToUpdate, OutputStream out)
 				throws TransportException {
 			push(monitor, refsToUpdate);
 		}
 
+		@Override
 		public void push(ProgressMonitor monitor,
 				Map<String, RemoteRefUpdate> refsToUpdate)
 				throws TransportException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
index 94bc383..3411122 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
@@ -108,7 +108,7 @@
 
 		// Fill dst with a some common history.
 		//
-		TestRepository<Repository> d = new TestRepository<Repository>(dst);
+		TestRepository<Repository> d = new TestRepository<>(dst);
 		a = d.blob("a");
 		A = d.commit(d.tree(d.file("a", a)));
 		B = d.commit().parent(A).create();
@@ -128,16 +128,6 @@
 		d.update(R_PRIVATE, P);
 	}
 
-	@Override
-	@After
-	public void tearDown() throws Exception {
-		if (src != null)
-			src.close();
-		if (dst != null)
-			dst.close();
-		super.tearDown();
-	}
-
 	@Test
 	public void testFilterHidesPrivate() throws Exception {
 		Map<String, Ref> refs;
@@ -193,7 +183,7 @@
 
 		// Now use b but in a different commit than what is hidden.
 		//
-		TestRepository<Repository> s = new TestRepository<Repository>(src);
+		TestRepository<Repository> s = new TestRepository<>(src);
 		RevCommit N = s.commit().parent(B).add("q", b).create();
 		s.update(R_MASTER, N);
 
@@ -284,7 +274,7 @@
 	@Test
 	public void testUsingHiddenDeltaBaseFails() throws Exception {
 		byte[] delta = { 0x1, 0x1, 0x1, 'c' };
-		TestRepository<Repository> s = new TestRepository<Repository>(src);
+		TestRepository<Repository> s = new TestRepository<>(src);
 		RevCommit N = s.commit().parent(B).add("q",
 				s.blob(BinaryDelta.apply(dst.open(b).getCachedBytes(), delta)))
 				.create();
@@ -337,7 +327,7 @@
 	public void testUsingHiddenCommonBlobFails() throws Exception {
 		// Try to use the 'b' blob that is hidden.
 		//
-		TestRepository<Repository> s = new TestRepository<Repository>(src);
+		TestRepository<Repository> s = new TestRepository<>(src);
 		RevCommit N = s.commit().parent(B).add("q", s.blob("b")).create();
 
 		// But don't include it in the pack.
@@ -387,7 +377,7 @@
 	public void testUsingUnknownBlobFails() throws Exception {
 		// Try to use the 'n' blob that is not on the server.
 		//
-		TestRepository<Repository> s = new TestRepository<Repository>(src);
+		TestRepository<Repository> s = new TestRepository<>(src);
 		RevBlob n = s.blob("n");
 		RevCommit N = s.commit().parent(B).add("q", n).create();
 
@@ -436,7 +426,7 @@
 
 	@Test
 	public void testUsingUnknownTreeFails() throws Exception {
-		TestRepository<Repository> s = new TestRepository<Repository>(src);
+		TestRepository<Repository> s = new TestRepository<>(src);
 		RevCommit N = s.commit().parent(B).add("q", s.blob("a")).create();
 		RevTree t = s.parseBody(N).getTree();
 
@@ -556,8 +546,9 @@
 	}
 
 	private static final class HidePrivateHook extends AbstractAdvertiseRefsHook {
+		@Override
 		public Map<String, Ref> getAdvertisedRefs(Repository r, RevWalk revWalk) {
-			Map<String, Ref> refs = new HashMap<String, Ref>(r.getAllRefs());
+			Map<String, Ref> refs = new HashMap<>(r.getAllRefs());
 			assertNotNull(refs.remove(R_PRIVATE));
 			return refs;
 		}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SpiTransport.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SpiTransport.java
index bb6c738..71b909e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SpiTransport.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SpiTransport.java
@@ -64,14 +64,17 @@
 	 */
 	public static final TransportProtocol PROTO = new TransportProtocol() {
 
+		@Override
 		public String getName() {
 			return "Test SPI Transport Protocol";
 		}
 
+		@Override
 		public Set<String> getSchemes() {
 			return Collections.singleton(SCHEME);
 		}
 
+		@Override
 		public Transport open(URIish uri, Repository local, String remoteName)
 				throws NotSupportedException, TransportException {
 			throw new NotSupportedException("not supported");
@@ -82,16 +85,19 @@
 		super(local, uri);
 	}
 
+	@Override
 	public FetchConnection openFetch() throws NotSupportedException,
 			TransportException {
 		throw new NotSupportedException("not supported");
 	}
 
+	@Override
 	public PushConnection openPush() throws NotSupportedException,
 			TransportException {
 		throw new NotSupportedException("not supported");
 	}
 
+	@Override
 	public void close() {
 		// Intentionally left blank
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
index 31b6418..b926e48 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
@@ -98,10 +98,10 @@
 
   @Before
 	public void setUp() throws Exception {
-		protos = new ArrayList<TransportProtocol>();
-		local = new TestRepository<InMemoryRepository>(
+		protos = new ArrayList<>();
+		local = new TestRepository<>(
 				new InMemoryRepository(new DfsRepositoryDescription("local")));
-		remote = new TestRepository<InMemoryRepository>(
+		remote = new TestRepository<>(
 				new InMemoryRepository(new DfsRepositoryDescription("remote")));
   }
 
@@ -240,7 +240,7 @@
 
 	private TestProtocol<User> registerProto(UploadPackFactory<User> upf,
 			ReceivePackFactory<User> rpf) {
-		TestProtocol<User> proto = new TestProtocol<User>(upf, rpf);
+		TestProtocol<User> proto = new TestProtocol<>(upf, rpf);
 		protos.add(proto);
 		Transport.register(proto);
 		return proto;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
index 5519f61..d4c47d3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
@@ -53,7 +53,9 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
@@ -209,6 +211,45 @@
 		assertEquals(ObjectId.zeroId(), tru.getOldObjectId());
 	}
 
+	/**
+	 * Test RefSpec to RemoteRefUpdate conversion with leases.
+	 *
+	 * @throws IOException
+	 */
+	@Test
+	public void testFindRemoteRefUpdatesWithLeases() throws IOException {
+		final RefSpec specA = new RefSpec("+refs/heads/a:refs/heads/b");
+		final RefSpec specC = new RefSpec("+refs/heads/c:refs/heads/d");
+		final Collection<RefSpec> specs = Arrays.asList(specA, specC);
+		final Map<String, RefLeaseSpec> leases = new HashMap<>();
+		leases.put("refs/heads/b",
+				new RefLeaseSpec("refs/heads/b", "refs/heads/c"));
+
+		Collection<RemoteRefUpdate> result;
+		try (Transport transport = Transport.open(db, remoteConfig)) {
+			result = transport.findRemoteRefUpdatesFor(specs, leases);
+		}
+
+		assertEquals(2, result.size());
+		boolean foundA = false;
+		boolean foundC = false;
+		for (final RemoteRefUpdate rru : result) {
+			if ("refs/heads/a".equals(rru.getSrcRef())
+					&& "refs/heads/b".equals(rru.getRemoteName())) {
+				foundA = true;
+				assertEquals(db.exactRef("refs/heads/c").getObjectId(),
+						rru.getExpectedOldObjectId());
+			}
+			if ("refs/heads/c".equals(rru.getSrcRef())
+					&& "refs/heads/d".equals(rru.getRemoteName())) {
+				foundC = true;
+				assertNull(rru.getExpectedOldObjectId());
+			}
+		}
+		assertTrue(foundA);
+		assertTrue(foundC);
+	}
+
 	@Test
 	public void testLocalTransportWithRelativePath() throws Exception {
 		Repository other = createWorkRepository();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
index cc5870e..1b434d3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
@@ -85,12 +85,12 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Locale;
 import java.util.Properties;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.UUID;
 
-import javax.crypto.Cipher;
 import javax.crypto.SecretKeyFactory;
 
 import org.eclipse.jgit.api.Git;
@@ -459,14 +459,14 @@
 
 		static List<String> cryptoCipherList(String regex) {
 			Set<String> source = Security.getAlgorithms("Cipher");
-			Set<String> target = new TreeSet<String>();
+			Set<String> target = new TreeSet<>();
 			for (String algo : source) {
-				algo = algo.toUpperCase();
+				algo = algo.toUpperCase(Locale.ROOT);
 				if (algo.matches(regex)) {
 					target.add(algo);
 				}
 			}
-			return new ArrayList<String>(target);
+			return new ArrayList<>(target);
 		}
 
 		/**
@@ -598,7 +598,7 @@
 		}
 
 		static List<Object[]> product(List<String> one, List<String> two) {
-			List<Object[]> result = new ArrayList<Object[]>();
+			List<Object[]> result = new ArrayList<>();
 			for (String s1 : one) {
 				for (String s2 : two) {
 					result.add(new Object[] { s1, s2 });
@@ -759,7 +759,7 @@
 			for (String source : cipherSet) {
 				// Standard names are not case-sensitive.
 				// http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
-				String target = algorithm.toUpperCase();
+				String target = algorithm.toUpperCase(Locale.ROOT);
 				if (source.equalsIgnoreCase(target)) {
 					return true;
 				}
@@ -771,16 +771,16 @@
 			String profile = props.getProperty(AmazonS3.Keys.CRYPTO_ALG);
 			String version = props.getProperty(AmazonS3.Keys.CRYPTO_VER,
 					WalkEncryption.Vals.DEFAULT_VERS);
-			String crytoAlgo;
+			String cryptoAlgo;
 			String keyAlgo;
 			switch (version) {
 			case WalkEncryption.Vals.DEFAULT_VERS:
 			case WalkEncryption.JGitV1.VERSION:
-				crytoAlgo = profile;
+				cryptoAlgo = profile;
 				keyAlgo = profile;
 				break;
 			case WalkEncryption.JGitV2.VERSION:
-				crytoAlgo = props
+				cryptoAlgo = props
 						.getProperty(profile + WalkEncryption.Keys.X_ALGO);
 				keyAlgo = props
 						.getProperty(profile + WalkEncryption.Keys.X_KEY_ALGO);
@@ -789,7 +789,7 @@
 				return false;
 			}
 			try {
-				Cipher.getInstance(crytoAlgo);
+				InsecureCipherFactory.create(cryptoAlgo);
 				SecretKeyFactory.getInstance(keyAlgo);
 				return true;
 			} catch (Throwable e) {
@@ -1240,10 +1240,10 @@
 
 		@Parameters(name = "Profile: {0}   Version: {1}")
 		public static Collection<Object[]> argsList() {
-			List<String> algorithmList = new ArrayList<String>();
+			List<String> algorithmList = new ArrayList<>();
 			algorithmList.addAll(cryptoCipherListPBE());
 
-			List<String> versionList = new ArrayList<String>();
+			List<String> versionList = new ArrayList<>();
 			versionList.add("0");
 			versionList.add("1");
 
@@ -1283,10 +1283,10 @@
 
 		@Parameters(name = "Profile: {0}   Version: {1}")
 		public static Collection<Object[]> argsList() {
-			List<String> algorithmList = new ArrayList<String>();
+			List<String> algorithmList = new ArrayList<>();
 			algorithmList.addAll(cryptoCipherListTrans());
 
-			List<String> versionList = new ArrayList<String>();
+			List<String> versionList = new ArrayList<>();
 			versionList.add("1");
 
 			return product(algorithmList, versionList);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java
deleted file mode 100644
index cd55cba..0000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2012-2013, Robin Rosenberg <robin.rosenberg@dewire.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.treewalk;
-
-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 org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.ResetCommand.ResetType;
-import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.dircache.DirCacheEditor;
-import org.eclipse.jgit.dircache.DirCacheEntry;
-import org.eclipse.jgit.dircache.DirCacheIterator;
-import org.eclipse.jgit.junit.RepositoryTestCase;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.util.FS;
-import org.eclipse.jgit.util.FileUtils;
-import org.junit.Test;
-
-public class FileTreeIteratorJava7Test extends RepositoryTestCase {
-	@Test
-	public void testFileModeSymLinkIsNotATree() throws IOException {
-		org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
-		FS fs = db.getFS();
-		// mål = target in swedish, just to get som unicode in here
-		writeTrashFile("mål/data", "targetdata");
-		fs.createSymLink(new File(trash, "länk"), "mål");
-		FileTreeIterator fti = new FileTreeIterator(db);
-		assertFalse(fti.eof());
-		assertEquals("länk", fti.getEntryPathString());
-		assertEquals(FileMode.SYMLINK, fti.getEntryFileMode());
-		fti.next(1);
-		assertFalse(fti.eof());
-		assertEquals("mål", fti.getEntryPathString());
-		assertEquals(FileMode.TREE, fti.getEntryFileMode());
-		fti.next(1);
-		assertTrue(fti.eof());
-	}
-
-	@Test
-	public void testSymlinkNotModifiedThoughNormalized() throws Exception {
-		DirCache dc = db.lockDirCache();
-		DirCacheEditor dce = dc.editor();
-		final String UNNORMALIZED = "target/";
-		final byte[] UNNORMALIZED_BYTES = Constants.encode(UNNORMALIZED);
-		try (ObjectInserter oi = db.newObjectInserter()) {
-			final ObjectId linkid = oi.insert(Constants.OBJ_BLOB,
-					UNNORMALIZED_BYTES, 0, UNNORMALIZED_BYTES.length);
-			dce.add(new DirCacheEditor.PathEdit("link") {
-				@Override
-				public void apply(DirCacheEntry ent) {
-					ent.setFileMode(FileMode.SYMLINK);
-					ent.setObjectId(linkid);
-					ent.setLength(UNNORMALIZED_BYTES.length);
-				}
-			});
-			assertTrue(dce.commit());
-		}
-		try (Git git = new Git(db)) {
-			git.commit().setMessage("Adding link").call();
-			git.reset().setMode(ResetType.HARD).call();
-			DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
-			FileTreeIterator fti = new FileTreeIterator(db);
-
-			// self-check
-			assertEquals("link", fti.getEntryPathString());
-			assertEquals("link", dci.getEntryPathString());
-
-			// test
-			assertFalse(fti.isModified(dci.getDirCacheEntry(), true,
-					db.newObjectReader()));
-		}
-	}
-
-	/**
-	 * Like #testSymlinkNotModifiedThoughNormalized but there is no
-	 * normalization being done.
-	 *
-	 * @throws Exception
-	 */
-	@Test
-	public void testSymlinkModifiedNotNormalized() throws Exception {
-		DirCache dc = db.lockDirCache();
-		DirCacheEditor dce = dc.editor();
-		final String NORMALIZED = "target";
-		final byte[] NORMALIZED_BYTES = Constants.encode(NORMALIZED);
-		try (ObjectInserter oi = db.newObjectInserter()) {
-			final ObjectId linkid = oi.insert(Constants.OBJ_BLOB,
-					NORMALIZED_BYTES, 0, NORMALIZED_BYTES.length);
-			dce.add(new DirCacheEditor.PathEdit("link") {
-				@Override
-				public void apply(DirCacheEntry ent) {
-					ent.setFileMode(FileMode.SYMLINK);
-					ent.setObjectId(linkid);
-					ent.setLength(NORMALIZED_BYTES.length);
-				}
-			});
-			assertTrue(dce.commit());
-		}
-		try (Git git = new Git(db)) {
-			git.commit().setMessage("Adding link").call();
-			git.reset().setMode(ResetType.HARD).call();
-			DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
-			FileTreeIterator fti = new FileTreeIterator(db);
-
-			// self-check
-			assertEquals("link", fti.getEntryPathString());
-			assertEquals("link", dci.getEntryPathString());
-
-			// test
-			assertFalse(fti.isModified(dci.getDirCacheEntry(), true,
-					db.newObjectReader()));
-		}
-	}
-
-	/**
-	 * Like #testSymlinkNotModifiedThoughNormalized but here the link is
-	 * modified.
-	 *
-	 * @throws Exception
-	 */
-	@Test
-	public void testSymlinkActuallyModified() throws Exception {
-		org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
-		final String NORMALIZED = "target";
-		final byte[] NORMALIZED_BYTES = Constants.encode(NORMALIZED);
-		try (ObjectInserter oi = db.newObjectInserter()) {
-			final ObjectId linkid = oi.insert(Constants.OBJ_BLOB,
-					NORMALIZED_BYTES, 0, NORMALIZED_BYTES.length);
-			DirCache dc = db.lockDirCache();
-			DirCacheEditor dce = dc.editor();
-			dce.add(new DirCacheEditor.PathEdit("link") {
-				@Override
-				public void apply(DirCacheEntry ent) {
-					ent.setFileMode(FileMode.SYMLINK);
-					ent.setObjectId(linkid);
-					ent.setLength(NORMALIZED_BYTES.length);
-				}
-			});
-			assertTrue(dce.commit());
-		}
-		try (Git git = new Git(db)) {
-			git.commit().setMessage("Adding link").call();
-			git.reset().setMode(ResetType.HARD).call();
-
-			FileUtils.delete(new File(trash, "link"), FileUtils.NONE);
-			FS.DETECTED.createSymLink(new File(trash, "link"), "newtarget");
-			DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
-			FileTreeIterator fti = new FileTreeIterator(db);
-
-			// self-check
-			assertEquals("link", fti.getEntryPathString());
-			assertEquals("link", dci.getEntryPathString());
-
-			// test
-			assertTrue(fti.isModified(dci.getDirCacheEntry(), true,
-					db.newObjectReader()));
-		}
-	}
-}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
index ef0f2d9..e34cb97 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, Google Inc.
+ * Copyright (C) 2008, 2017, Google Inc.
  * and other copyright owners as documented in the project's IP log.
  *
  * This program and the accompanying materials are made available
@@ -53,6 +53,7 @@
 import java.security.MessageDigest;
 
 import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.ResetCommand.ResetType;
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheCheckout;
 import org.eclipse.jgit.dircache.DirCacheEditor;
@@ -68,6 +69,7 @@
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -85,6 +87,7 @@
 
 	private long[] mtime;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
@@ -381,6 +384,7 @@
 			DirCacheEditor editor = cache.editor();
 			editor.add(new PathEdit(path) {
 
+				@Override
 				public void apply(DirCacheEntry ent) {
 					ent.setFileMode(FileMode.GITLINK);
 					ent.setObjectId(id);
@@ -415,6 +419,7 @@
 			DirCacheEditor editor = cache.editor();
 			editor.add(new PathEdit(path) {
 
+				@Override
 				public void apply(DirCacheEntry ent) {
 					ent.setFileMode(FileMode.GITLINK);
 					ent.setObjectId(id);
@@ -450,6 +455,7 @@
 			DirCacheEditor editor = cache.editor();
 			editor.add(new PathEdit(path) {
 
+				@Override
 				public void apply(DirCacheEntry ent) {
 					ent.setFileMode(FileMode.GITLINK);
 					ent.setObjectId(id);
@@ -484,6 +490,7 @@
 			DirCacheEditor editor = cache.editor();
 			editor.add(new PathEdit(path) {
 
+				@Override
 				public void apply(DirCacheEntry ent) {
 					ent.setFileMode(FileMode.GITLINK);
 					ent.setObjectId(id);
@@ -519,6 +526,7 @@
 			DirCacheEditor editor = cache.editor();
 			editor.add(new PathEdit(path) {
 
+				@Override
 				public void apply(DirCacheEntry ent) {
 					ent.setFileMode(FileMode.GITLINK);
 					ent.setObjectId(id);
@@ -653,6 +661,157 @@
 		}
 	}
 
+	@Test
+	public void testFileModeSymLinkIsNotATree() throws IOException {
+		org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
+		FS fs = db.getFS();
+		// mål = target in swedish, just to get som unicode in here
+		writeTrashFile("mål/data", "targetdata");
+		fs.createSymLink(new File(trash, "länk"), "mål");
+		FileTreeIterator fti = new FileTreeIterator(db);
+		assertFalse(fti.eof());
+		while (!fti.getEntryPathString().equals("länk")) {
+			fti.next(1);
+		}
+		assertEquals("länk", fti.getEntryPathString());
+		assertEquals(FileMode.SYMLINK, fti.getEntryFileMode());
+		fti.next(1);
+		assertFalse(fti.eof());
+		assertEquals("mål", fti.getEntryPathString());
+		assertEquals(FileMode.TREE, fti.getEntryFileMode());
+		fti.next(1);
+		assertTrue(fti.eof());
+	}
+
+	@Test
+	public void testSymlinkNotModifiedThoughNormalized() throws Exception {
+		DirCache dc = db.lockDirCache();
+		DirCacheEditor dce = dc.editor();
+		final String UNNORMALIZED = "target/";
+		final byte[] UNNORMALIZED_BYTES = Constants.encode(UNNORMALIZED);
+		try (ObjectInserter oi = db.newObjectInserter()) {
+			final ObjectId linkid = oi.insert(Constants.OBJ_BLOB,
+					UNNORMALIZED_BYTES, 0, UNNORMALIZED_BYTES.length);
+			dce.add(new DirCacheEditor.PathEdit("link") {
+				@Override
+				public void apply(DirCacheEntry ent) {
+					ent.setFileMode(FileMode.SYMLINK);
+					ent.setObjectId(linkid);
+					ent.setLength(UNNORMALIZED_BYTES.length);
+				}
+			});
+			assertTrue(dce.commit());
+		}
+		try (Git git = new Git(db)) {
+			git.commit().setMessage("Adding link").call();
+			git.reset().setMode(ResetType.HARD).call();
+			DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
+			FileTreeIterator fti = new FileTreeIterator(db);
+
+			// self-check
+			while (!fti.getEntryPathString().equals("link")) {
+				fti.next(1);
+			}
+			assertEquals("link", fti.getEntryPathString());
+			assertEquals("link", dci.getEntryPathString());
+
+			// test
+			assertFalse(fti.isModified(dci.getDirCacheEntry(), true,
+					db.newObjectReader()));
+		}
+	}
+
+	/**
+	 * Like #testSymlinkNotModifiedThoughNormalized but there is no
+	 * normalization being done.
+	 *
+	 * @throws Exception
+	 */
+	@Test
+	public void testSymlinkModifiedNotNormalized() throws Exception {
+		DirCache dc = db.lockDirCache();
+		DirCacheEditor dce = dc.editor();
+		final String NORMALIZED = "target";
+		final byte[] NORMALIZED_BYTES = Constants.encode(NORMALIZED);
+		try (ObjectInserter oi = db.newObjectInserter()) {
+			final ObjectId linkid = oi.insert(Constants.OBJ_BLOB,
+					NORMALIZED_BYTES, 0, NORMALIZED_BYTES.length);
+			dce.add(new DirCacheEditor.PathEdit("link") {
+				@Override
+				public void apply(DirCacheEntry ent) {
+					ent.setFileMode(FileMode.SYMLINK);
+					ent.setObjectId(linkid);
+					ent.setLength(NORMALIZED_BYTES.length);
+				}
+			});
+			assertTrue(dce.commit());
+		}
+		try (Git git = new Git(db)) {
+			git.commit().setMessage("Adding link").call();
+			git.reset().setMode(ResetType.HARD).call();
+			DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
+			FileTreeIterator fti = new FileTreeIterator(db);
+
+			// self-check
+			while (!fti.getEntryPathString().equals("link")) {
+				fti.next(1);
+			}
+			assertEquals("link", fti.getEntryPathString());
+			assertEquals("link", dci.getEntryPathString());
+
+			// test
+			assertFalse(fti.isModified(dci.getDirCacheEntry(), true,
+					db.newObjectReader()));
+		}
+	}
+
+	/**
+	 * Like #testSymlinkNotModifiedThoughNormalized but here the link is
+	 * modified.
+	 *
+	 * @throws Exception
+	 */
+	@Test
+	public void testSymlinkActuallyModified() throws Exception {
+		org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
+		final String NORMALIZED = "target";
+		final byte[] NORMALIZED_BYTES = Constants.encode(NORMALIZED);
+		try (ObjectInserter oi = db.newObjectInserter()) {
+			final ObjectId linkid = oi.insert(Constants.OBJ_BLOB,
+					NORMALIZED_BYTES, 0, NORMALIZED_BYTES.length);
+			DirCache dc = db.lockDirCache();
+			DirCacheEditor dce = dc.editor();
+			dce.add(new DirCacheEditor.PathEdit("link") {
+				@Override
+				public void apply(DirCacheEntry ent) {
+					ent.setFileMode(FileMode.SYMLINK);
+					ent.setObjectId(linkid);
+					ent.setLength(NORMALIZED_BYTES.length);
+				}
+			});
+			assertTrue(dce.commit());
+		}
+		try (Git git = new Git(db)) {
+			git.commit().setMessage("Adding link").call();
+			git.reset().setMode(ResetType.HARD).call();
+
+			FileUtils.delete(new File(trash, "link"), FileUtils.NONE);
+			FS.DETECTED.createSymLink(new File(trash, "link"), "newtarget");
+			DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
+			FileTreeIterator fti = new FileTreeIterator(db);
+
+			// self-check
+			while (!fti.getEntryPathString().equals("link")) {
+				fti.next(1);
+			}
+			assertEquals("link", fti.getEntryPathString());
+			assertEquals("link", dci.getEntryPathString());
+
+			// test
+			assertTrue(fti.isModified(dci.getDirCacheEntry(), true,
+					db.newObjectReader()));
+		}
+	}
 
 	private static void assertEntry(String sha1string, String path, TreeWalk tw)
 			throws MissingObjectException, IncorrectObjectTypeException,
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/IndexDiffFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/IndexDiffFilterTest.java
index bb4b066..2f797e3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/IndexDiffFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/IndexDiffFilterTest.java
@@ -89,6 +89,7 @@
 
 	private Git git;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java
index 9f0f067..cba35d8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/InterIndexDiffFilterTest.java
@@ -66,6 +66,7 @@
 
 	private Repository db;
 
+	@Override
 	@Before
 	public void setUp() throws Exception {
 		super.setUp();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java
index 5edc192..d921aab 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java
@@ -259,6 +259,7 @@
 		DirCacheEditor dce = dc.editor();
 		dce.add(new DirCacheEditor.PathEdit(path) {
 
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.REGULAR_FILE);
 			}
@@ -277,6 +278,7 @@
 		DirCache dc = DirCache.newInCore();
 		DirCacheEditor dce = dc.editor();
 		dce.add(new DirCacheEditor.PathEdit(path + "/README") {
+			@Override
 			public void apply(DirCacheEntry ent) {
 				ent.setFileMode(FileMode.REGULAR_FILE);
 			}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterLogicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterLogicTest.java
new file mode 100644
index 0000000..7c819c5
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterLogicTest.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2017 Magnus Vigerlöf (magnus.vigerlof@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.treewalk.filter;
+
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class PathFilterLogicTest extends RepositoryTestCase {
+
+	private ObjectId treeId;
+
+	@Before
+	public void setup() throws IOException {
+		String[] paths = new String[] {
+				"a.txt",
+				"sub1.txt",
+				"sub1/suba/a.txt",
+				"sub1/subb/b.txt",
+				"sub2/suba/a.txt"
+		};
+		treeId = createTree(paths);
+	}
+
+	@Test
+	public void testSinglePath() throws IOException {
+		List<String> expected = Arrays.asList("sub1/suba/a.txt",
+				"sub1/subb/b.txt");
+
+		TreeFilter tf = PathFilter.create("sub1");
+		List<String> paths = getMatchingPaths(treeId, tf);
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testSingleSubPath() throws IOException {
+		List<String> expected = Collections.singletonList("sub1/suba/a.txt");
+
+		TreeFilter tf = PathFilter.create("sub1/suba");
+		List<String> paths = getMatchingPaths(treeId, tf);
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testSinglePathNegate() throws IOException {
+		List<String> expected = Arrays.asList("a.txt", "sub1.txt",
+				"sub2/suba/a.txt");
+
+		TreeFilter tf = PathFilter.create("sub1").negate();
+		List<String> paths = getMatchingPaths(treeId, tf);
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testSingleSubPathNegate() throws IOException {
+		List<String> expected = Arrays.asList("a.txt", "sub1.txt",
+				"sub1/subb/b.txt", "sub2/suba/a.txt");
+
+		TreeFilter tf = PathFilter.create("sub1/suba").negate();
+		List<String> paths = getMatchingPaths(treeId, tf);
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testOrMultiTwoPath() throws IOException {
+		List<String> expected = Arrays.asList("sub1/suba/a.txt",
+				"sub1/subb/b.txt", "sub2/suba/a.txt");
+
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1"),
+				PathFilter.create("sub2")};
+		List<String> paths = getMatchingPaths(treeId, OrTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testOrMultiThreePath() throws IOException {
+		List<String> expected = Arrays.asList("sub1.txt", "sub1/suba/a.txt",
+				"sub1/subb/b.txt", "sub2/suba/a.txt");
+
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1"),
+				PathFilter.create("sub2"), PathFilter.create("sub1.txt")};
+		List<String> paths = getMatchingPaths(treeId, OrTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testOrMultiTwoSubPath() throws IOException {
+		List<String> expected = Arrays.asList("sub1/subb/b.txt",
+				"sub2/suba/a.txt");
+
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1/subb"),
+				PathFilter.create("sub2/suba")};
+		List<String> paths = getMatchingPaths(treeId, OrTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testOrMultiTwoMixSubPath() throws IOException {
+		List<String> expected = Arrays.asList("sub1/subb/b.txt",
+				"sub2/suba/a.txt");
+
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1/subb"),
+				PathFilter.create("sub2")};
+		List<String> paths = getMatchingPaths(treeId, OrTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testOrMultiTwoMixSubPathNegate() throws IOException {
+		List<String> expected = Arrays.asList("a.txt", "sub1.txt",
+				"sub1/suba/a.txt", "sub2/suba/a.txt");
+
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1").negate(),
+				PathFilter.create("sub1/suba")};
+		List<String> paths = getMatchingPaths(treeId, OrTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testOrMultiThreeMixSubPathNegate() throws IOException {
+		List<String> expected = Arrays.asList("a.txt", "sub1.txt",
+				"sub1/suba/a.txt", "sub2/suba/a.txt");
+
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1").negate(),
+				PathFilter.create("sub1/suba"), PathFilter.create("no/path")};
+		List<String> paths = getMatchingPaths(treeId, OrTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testPatternParentFileMatch() throws IOException {
+		List<String> expected = Collections.emptyList();
+
+		TreeFilter tf = PathFilter.create("a.txt/test/path");
+		List<String> paths = getMatchingPaths(treeId, tf);
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testAndMultiPath() throws IOException {
+		List<String> expected = Collections.emptyList();
+
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1"),
+				PathFilter.create("sub2")};
+		List<String> paths = getMatchingPaths(treeId, AndTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testAndMultiPathNegate() throws IOException {
+		List<String> expected = Arrays.asList("sub1/suba/a.txt",
+				"sub1/subb/b.txt");
+
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1"),
+				PathFilter.create("sub2").negate()};
+		List<String> paths = getMatchingPaths(treeId, AndTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testAndMultiSubPathDualNegate() throws IOException {
+		List<String> expected = Arrays.asList("a.txt", "sub1.txt",
+				"sub1/subb/b.txt");
+
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1/suba").negate(),
+				PathFilter.create("sub2").negate()};
+		List<String> paths = getMatchingPaths(treeId, AndTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testAndMultiSubPath() throws IOException {
+		List<String> expected = Collections.emptyList();
+
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1"),
+				PathFilter.create("sub2/suba")};
+		List<String> paths = getMatchingPaths(treeId, AndTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testAndMultiSubPathNegate() throws IOException {
+		List<String> expected = Collections.singletonList("sub1/subb/b.txt");
+
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1"),
+				PathFilter.create("sub1/suba").negate()};
+		List<String> paths = getMatchingPaths(treeId, AndTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testAndMultiThreeSubPathNegate() throws IOException {
+		List<String> expected = Collections.singletonList("sub1/subb/b.txt");
+
+		TreeFilter[] tf = new TreeFilter[]{PathFilter.create("sub1"),
+				PathFilter.create("sub1/suba").negate(),
+				PathFilter.create("no/path").negate()};
+		List<String> paths = getMatchingPaths(treeId, AndTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testTopAndMultiPathDualNegate() throws IOException {
+		List<String> expected = Arrays.asList("a.txt", "sub1.txt");
+
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1").negate(),
+				PathFilter.create("sub2").negate()};
+		List<String> paths = getMatchingPathsFlat(treeId, AndTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testTopAndMultiSubPathDualNegate() throws IOException {
+		List<String> expected = Arrays.asList("a.txt", "sub1.txt", "sub1");
+
+		// Filter on 'sub1/suba' is kind of silly for a non-recursive walk.
+		// The result is interesting though as the 'sub1' path should be
+		// returned, due to the fact that there may be hits once the pattern
+		// is tested with one of the leaf paths.
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1/suba").negate(),
+				PathFilter.create("sub2").negate()};
+		List<String> paths = getMatchingPathsFlat(treeId, AndTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testTopOrMultiPathDual() throws IOException {
+		List<String> expected = Arrays.asList("sub1.txt", "sub2");
+
+		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1.txt"),
+				PathFilter.create("sub2")};
+		List<String> paths = getMatchingPathsFlat(treeId, OrTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	@Test
+	public void testTopNotPath() throws IOException {
+		List<String> expected = Arrays.asList("a.txt", "sub1.txt", "sub2");
+
+		TreeFilter tf = PathFilter.create("sub1");
+		List<String> paths = getMatchingPathsFlat(treeId, NotTreeFilter.create(tf));
+
+		assertEquals(expected, paths);
+	}
+
+	private List<String> getMatchingPaths(final ObjectId objId,
+			TreeFilter tf) throws IOException {
+		return getMatchingPaths(objId, tf, true);
+	}
+
+	private List<String> getMatchingPathsFlat(final ObjectId objId,
+			TreeFilter tf) throws IOException {
+		return getMatchingPaths(objId, tf, false);
+	}
+
+	private List<String> getMatchingPaths(final ObjectId objId,
+			TreeFilter tf, boolean recursive) throws IOException {
+		try (TreeWalk tw = new TreeWalk(db)) {
+			tw.setFilter(tf);
+			tw.setRecursive(recursive);
+			tw.addTree(objId);
+
+			List<String> paths = new ArrayList<>();
+			while (tw.next()) {
+				paths.add(tw.getPathString());
+			}
+			return paths;
+		}
+	}
+
+	private ObjectId createTree(String... paths) throws IOException {
+		final ObjectInserter odi = db.newObjectInserter();
+		final DirCache dc = db.readDirCache();
+		final DirCacheBuilder builder = dc.builder();
+		for (String path : paths) {
+			DirCacheEntry entry = createEntry(path, FileMode.REGULAR_FILE);
+			builder.add(entry);
+		}
+		builder.finish();
+		final ObjectId objId = dc.writeTree(odi);
+		odi.flush();
+		return objId;
+	}
+}
+
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTest.java
index 3885c41..38adda3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathSuffixFilterTest.java
@@ -118,7 +118,7 @@
 			tw.setRecursive(recursiveWalk);
 			tw.addTree(treeId);
 
-			List<String> paths = new ArrayList<String>();
+			List<String> paths = new ArrayList<>();
 			while (tw.next())
 				paths.add(tw.getPathString());
 			return paths;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/BlockListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/BlockListTest.java
index 8b042bd..0a3de85 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/BlockListTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/BlockListTest.java
@@ -57,22 +57,22 @@
 	public void testEmptyList() {
 		BlockList<String> empty;
 
-		empty = new BlockList<String>();
+		empty = new BlockList<>();
 		assertEquals(0, empty.size());
 		assertTrue(empty.isEmpty());
 		assertFalse(empty.iterator().hasNext());
 
-		empty = new BlockList<String>(0);
+		empty = new BlockList<>(0);
 		assertEquals(0, empty.size());
 		assertTrue(empty.isEmpty());
 		assertFalse(empty.iterator().hasNext());
 
-		empty = new BlockList<String>(1);
+		empty = new BlockList<>(1);
 		assertEquals(0, empty.size());
 		assertTrue(empty.isEmpty());
 		assertFalse(empty.iterator().hasNext());
 
-		empty = new BlockList<String>(64);
+		empty = new BlockList<>(64);
 		assertEquals(0, empty.size());
 		assertTrue(empty.isEmpty());
 		assertFalse(empty.iterator().hasNext());
@@ -80,7 +80,7 @@
 
 	@Test
 	public void testGet() {
-		BlockList<String> list = new BlockList<String>(4);
+		BlockList<String> list = new BlockList<>(4);
 
 		try {
 			list.get(-1);
@@ -121,7 +121,7 @@
 
 	@Test
 	public void testSet() {
-		BlockList<String> list = new BlockList<String>(4);
+		BlockList<String> list = new BlockList<>(4);
 
 		try {
 			list.set(-1, "foo");
@@ -168,7 +168,7 @@
 
 	@Test
 	public void testAddToEnd() {
-		BlockList<Integer> list = new BlockList<Integer>(4);
+		BlockList<Integer> list = new BlockList<>(4);
 		int cnt = BlockList.BLOCK_SIZE * 3;
 
 		for (int i = 0; i < cnt; i++)
@@ -192,7 +192,7 @@
 
 	@Test
 	public void testAddSlowPath() {
-		BlockList<String> list = new BlockList<String>(4);
+		BlockList<String> list = new BlockList<>(4);
 
 		String fooStr = "foo";
 		String barStr = "bar";
@@ -223,7 +223,7 @@
 
 	@Test
 	public void testRemoveFromEnd() {
-		BlockList<String> list = new BlockList<String>(4);
+		BlockList<String> list = new BlockList<>(4);
 
 		String fooStr = "foo";
 		String barStr = "bar";
@@ -245,7 +245,7 @@
 
 	@Test
 	public void testRemoveSlowPath() {
-		BlockList<String> list = new BlockList<String>(4);
+		BlockList<String> list = new BlockList<>(4);
 
 		String fooStr = "foo";
 		String barStr = "bar";
@@ -270,7 +270,7 @@
 
 	@Test
 	public void testAddRemoveAdd() {
-		BlockList<Integer> list = new BlockList<Integer>();
+		BlockList<Integer> list = new BlockList<>();
 		for (int i = 0; i < BlockList.BLOCK_SIZE + 1; i++)
 			list.add(Integer.valueOf(i));
 		assertEquals(Integer.valueOf(BlockList.BLOCK_SIZE),
@@ -283,14 +283,14 @@
 
 	@Test
 	public void testAddAllFromOtherList() {
-		BlockList<Integer> src = new BlockList<Integer>(4);
+		BlockList<Integer> src = new BlockList<>(4);
 		int cnt = BlockList.BLOCK_SIZE * 2;
 
 		for (int i = 0; i < cnt; i++)
 			src.add(Integer.valueOf(42 + i));
 		src.add(Integer.valueOf(1));
 
-		BlockList<Integer> dst = new BlockList<Integer>(4);
+		BlockList<Integer> dst = new BlockList<>(4);
 		dst.add(Integer.valueOf(255));
 		dst.addAll(src);
 		assertEquals(cnt + 2, dst.size());
@@ -301,7 +301,7 @@
 
 	@Test
 	public void testFastIterator() {
-		BlockList<Integer> list = new BlockList<Integer>(4);
+		BlockList<Integer> list = new BlockList<>(4);
 		int cnt = BlockList.BLOCK_SIZE * 3;
 
 		for (int i = 0; i < cnt; i++)
@@ -318,7 +318,7 @@
 
 	@Test
 	public void testAddRejectsBadIndexes() {
-		BlockList<Integer> list = new BlockList<Integer>(4);
+		BlockList<Integer> list = new BlockList<>(4);
 		list.add(Integer.valueOf(41));
 
 		try {
@@ -336,7 +336,7 @@
 
 	@Test
 	public void testRemoveRejectsBadIndexes() {
-		BlockList<Integer> list = new BlockList<Integer>(4);
+		BlockList<Integer> list = new BlockList<>(4);
 		list.add(Integer.valueOf(41));
 
 		try {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java
deleted file mode 100644
index cc1fdc2..0000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2013, Robin Rosenberg <robin.rosenberg@dewire.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.util;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class FileUtils7Test {
-
-	private final File trash = new File(new File("target"), "trash");
-
-	@Before
-	public void setUp() throws Exception {
-		FileUtils.delete(trash, FileUtils.RECURSIVE | FileUtils.RETRY | FileUtils.SKIP_MISSING);
-		assertTrue(trash.mkdirs());
-	}
-
-	@After
-	public void tearDown() throws Exception {
-		FileUtils.delete(trash, FileUtils.RECURSIVE | FileUtils.RETRY);
-	}
-
-	@Test
-	public void testDeleteSymlinkToDirectoryDoesNotDeleteTarget()
-			throws IOException {
-		org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
-		FS fs = FS.DETECTED;
-		File dir = new File(trash, "dir");
-		File file = new File(dir, "file");
-		File link = new File(trash, "link");
-		FileUtils.mkdirs(dir);
-		FileUtils.createNewFile(file);
-		fs.createSymLink(link, "dir");
-		FileUtils.delete(link, FileUtils.RECURSIVE);
-		assertFalse(link.exists());
-		assertTrue(dir.exists());
-		assertTrue(file.exists());
-	}
-
-	@Test
-	public void testAtomicMove() throws IOException {
-		File src = new File(trash, "src");
-		Files.createFile(src.toPath());
-		File dst = new File(trash, "dst");
-		FileUtils.rename(src, dst, StandardCopyOption.ATOMIC_MOVE);
-		assertFalse(Files.exists(src.toPath()));
-		assertTrue(Files.exists(dst.toPath()));
-	}
-}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java
similarity index 87%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java
index 1f78e02..109d0e6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilsTest.java
@@ -50,15 +50,32 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.rmi.RemoteException;
 import java.util.regex.Matcher;
 
+import javax.management.remote.JMXProviderException;
+
 import org.eclipse.jgit.junit.JGitTestUtil;
 import org.junit.After;
 import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 
-public class FileUtilTest {
+public class FileUtilsTest {
+	private static final String MSG = "Stale file handle";
+
+	private static final String SOME_ERROR_MSG = "some error message";
+
+	private static final IOException IO_EXCEPTION = new UnsupportedEncodingException(
+			MSG);
+
+	private static final IOException IO_EXCEPTION_WITH_CAUSE = new RemoteException(
+			SOME_ERROR_MSG,
+			new JMXProviderException(SOME_ERROR_MSG, IO_EXCEPTION));
+
 	private File trash;
 
 	@Before
@@ -508,8 +525,60 @@
 		assertEquals(expected, actual);
 	}
 
+	@Test
+	public void testDeleteSymlinkToDirectoryDoesNotDeleteTarget()
+			throws IOException {
+		org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
+		FS fs = FS.DETECTED;
+		File dir = new File(trash, "dir");
+		File file = new File(dir, "file");
+		File link = new File(trash, "link");
+		FileUtils.mkdirs(dir);
+		FileUtils.createNewFile(file);
+		fs.createSymLink(link, "dir");
+		FileUtils.delete(link, FileUtils.RECURSIVE);
+		assertFalse(link.exists());
+		assertTrue(dir.exists());
+		assertTrue(file.exists());
+	}
+
+	@Test
+	public void testAtomicMove() throws IOException {
+		File src = new File(trash, "src");
+		Files.createFile(src.toPath());
+		File dst = new File(trash, "dst");
+		FileUtils.rename(src, dst, StandardCopyOption.ATOMIC_MOVE);
+		assertFalse(Files.exists(src.toPath()));
+		assertTrue(Files.exists(dst.toPath()));
+	}
+
 	private String toOSPathString(String path) {
 		return path.replaceAll("/|\\\\",
 				Matcher.quoteReplacement(File.separator));
 	}
+
+	@Test
+	public void testIsStaleFileHandleWithDirectCause() throws Exception {
+		assertTrue(FileUtils.isStaleFileHandle(IO_EXCEPTION));
+	}
+
+	@Test
+	public void testIsStaleFileHandleWithIndirectCause() throws Exception {
+		assertFalse(
+				FileUtils.isStaleFileHandle(IO_EXCEPTION_WITH_CAUSE));
+	}
+
+	@Test
+	public void testIsStaleFileHandleInCausalChainWithDirectCause()
+			throws Exception {
+		assertTrue(
+				FileUtils.isStaleFileHandleInCausalChain(IO_EXCEPTION));
+	}
+
+	@Test
+	public void testIsStaleFileHandleInCausalChainWithIndirectCause()
+			throws Exception {
+		assertTrue(FileUtils
+				.isStaleFileHandleInCausalChain(IO_EXCEPTION_WITH_CAUSE));
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ReadLinesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ReadLinesTest.java
index 1fe3dbd..796af99 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ReadLinesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ReadLinesTest.java
@@ -51,7 +51,7 @@
 import org.junit.Test;
 
 public class ReadLinesTest {
-	List<String> l = new ArrayList<String>();
+	List<String> l = new ArrayList<>();
 
 	@Before
 	public void clearList() {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefListTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefListTest.java
index 4b7ab26..5a1468a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefListTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefListTest.java
@@ -89,7 +89,7 @@
 
 	@Test
 	public void testEmptyBuilder() {
-		RefList<Ref> list = new RefList.Builder<Ref>().toRefList();
+		RefList<Ref> list = new RefList.Builder<>().toRefList();
 		assertEquals(0, list.size());
 		assertFalse(list.iterator().hasNext());
 		assertEquals(-1, list.find("a"));
@@ -111,7 +111,7 @@
 
 	@Test
 	public void testBuilder_AddThenSort() {
-		RefList.Builder<Ref> builder = new RefList.Builder<Ref>(1);
+		RefList.Builder<Ref> builder = new RefList.Builder<>(1);
 		builder.add(REF_B);
 		builder.add(REF_A);
 
@@ -129,7 +129,7 @@
 
 	@Test
 	public void testBuilder_AddAll() {
-		RefList.Builder<Ref> builder = new RefList.Builder<Ref>(1);
+		RefList.Builder<Ref> builder = new RefList.Builder<>(1);
 		Ref[] src = { REF_A, REF_B, REF_c, REF_A };
 		builder.addAll(src, 1, 2);
 
@@ -141,7 +141,7 @@
 
 	@Test
 	public void testBuilder_Set() {
-		RefList.Builder<Ref> builder = new RefList.Builder<Ref>();
+		RefList.Builder<Ref> builder = new RefList.Builder<>();
 		builder.add(REF_A);
 		builder.add(REF_A);
 
@@ -163,7 +163,7 @@
 
 	@Test
 	public void testBuilder_Remove() {
-		RefList.Builder<Ref> builder = new RefList.Builder<Ref>();
+		RefList.Builder<Ref> builder = new RefList.Builder<>();
 		builder.add(REF_A);
 		builder.add(REF_B);
 		builder.remove(0);
@@ -364,7 +364,7 @@
 		exp.append(REF_B);
 		exp.append("]");
 
-		RefList.Builder<Ref> list = new RefList.Builder<Ref>();
+		RefList.Builder<Ref> list = new RefList.Builder<>();
 		list.add(REF_A);
 		list.add(REF_B);
 		assertEquals(exp.toString(), list.toString());
@@ -442,16 +442,16 @@
 
 	@Test
 	public void testCopyConstructorReusesArray() {
-		RefList.Builder<Ref> one = new RefList.Builder<Ref>();
+		RefList.Builder<Ref> one = new RefList.Builder<>();
 		one.add(REF_A);
 
-		RefList<Ref> two = new RefList<Ref>(one.toRefList());
+		RefList<Ref> two = new RefList<>(one.toRefList());
 		one.set(0, REF_B);
 		assertSame(REF_B, two.get(0));
 	}
 
 	private static RefList<Ref> toList(Ref... refs) {
-		RefList.Builder<Ref> b = new RefList.Builder<Ref>(refs.length);
+		RefList.Builder<Ref> b = new RefList.Builder<>(refs.length);
 		b.addAll(refs, 0, refs.length);
 		return b.toRefList();
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefMapTest.java
index 78ab27a..e6402a4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefMapTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RefMapTest.java
@@ -481,7 +481,7 @@
 	}
 
 	private static RefList<Ref> toList(Ref... refs) {
-		RefList.Builder<Ref> b = new RefList.Builder<Ref>(refs.length);
+		RefList.Builder<Ref> b = new RefList.Builder<>(refs.length);
 		b.addAll(refs, 0, refs.length);
 		return b.toRefList();
 	}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/UnionInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/UnionInputStreamTest.java
index c213157..6b5ef7e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/UnionInputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/UnionInputStreamTest.java
@@ -166,6 +166,7 @@
 		assertEquals(-1, u.read());
 
 		u.add(new ByteArrayInputStream(new byte[] { 20, 30 }) {
+			@Override
 			public long skip(long n) {
 				return 0;
 			}
@@ -180,11 +181,13 @@
 		final UnionInputStream u = new UnionInputStream();
 		final boolean closed[] = new boolean[2];
 		u.add(new ByteArrayInputStream(new byte[] { 1 }) {
+			@Override
 			public void close() {
 				closed[0] = true;
 			}
 		});
 		u.add(new ByteArrayInputStream(new byte[] { 2 }) {
+			@Override
 			public void close() {
 				closed[1] = true;
 			}
@@ -211,11 +214,13 @@
 		final UnionInputStream u = new UnionInputStream();
 		final boolean closed[] = new boolean[2];
 		u.add(new ByteArrayInputStream(new byte[] { 1 }) {
+			@Override
 			public void close() {
 				closed[0] = true;
 			}
 		});
 		u.add(new ByteArrayInputStream(new byte[] { 2 }) {
+			@Override
 			public void close() {
 				closed[1] = true;
 			}
@@ -234,6 +239,7 @@
 	public void testExceptionDuringClose() {
 		final UnionInputStream u = new UnionInputStream();
 		u.add(new ByteArrayInputStream(new byte[] { 1 }) {
+			@Override
 			public void close() throws IOException {
 				throw new IOException("I AM A TEST");
 			}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/sha1/SHA1Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/sha1/SHA1Test.java
new file mode 100644
index 0000000..0778989
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/sha1/SHA1Test.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2017, Google Inc.
+ * 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.util.sha1;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.util.IO;
+import org.junit.Test;
+
+public class SHA1Test {
+	private static final String TEST1 = "abc";
+
+	private static final String TEST2a = "abcdbcdecdefdefgefghfghighijhi";
+	private static final String TEST2b = "jkijkljklmklmnlmnomnopnopq";
+	private static final String TEST2 = TEST2a + TEST2b;
+
+	@Test
+	public void test0() throws NoSuchAlgorithmException {
+		ObjectId exp = ObjectId
+				.fromString("da39a3ee5e6b4b0d3255bfef95601890afd80709");
+
+		MessageDigest m = MessageDigest.getInstance("SHA-1");
+		m.update(new byte[] {});
+		ObjectId m1 = ObjectId.fromRaw(m.digest());
+
+		SHA1 s = SHA1.newInstance();
+		s.update(new byte[] {});
+		ObjectId s1 = ObjectId.fromRaw(s.digest());
+
+		s.reset();
+		s.update(new byte[] {});
+		ObjectId s2 = s.toObjectId();
+
+		assertEquals(m1, s1);
+		assertEquals(exp, s1);
+		assertEquals(exp, s2);
+	}
+
+	@Test
+	public void test1() throws NoSuchAlgorithmException {
+		ObjectId exp = ObjectId
+				.fromString("a9993e364706816aba3e25717850c26c9cd0d89d");
+
+		MessageDigest m = MessageDigest.getInstance("SHA-1");
+		m.update(TEST1.getBytes(StandardCharsets.UTF_8));
+		ObjectId m1 = ObjectId.fromRaw(m.digest());
+
+		SHA1 s = SHA1.newInstance();
+		s.update(TEST1.getBytes(StandardCharsets.UTF_8));
+		ObjectId s1 = ObjectId.fromRaw(s.digest());
+
+		s.reset();
+		s.update(TEST1.getBytes(StandardCharsets.UTF_8));
+		ObjectId s2 = s.toObjectId();
+
+		assertEquals(m1, s1);
+		assertEquals(exp, s1);
+		assertEquals(exp, s2);
+	}
+
+	@Test
+	public void test2() throws NoSuchAlgorithmException {
+		ObjectId exp = ObjectId
+				.fromString("84983e441c3bd26ebaae4aa1f95129e5e54670f1");
+
+		MessageDigest m = MessageDigest.getInstance("SHA-1");
+		m.update(TEST2.getBytes(StandardCharsets.UTF_8));
+		ObjectId m1 = ObjectId.fromRaw(m.digest());
+
+		SHA1 s = SHA1.newInstance();
+		s.update(TEST2.getBytes(StandardCharsets.UTF_8));
+		ObjectId s1 = ObjectId.fromRaw(s.digest());
+
+		s.reset();
+		s.update(TEST2.getBytes(StandardCharsets.UTF_8));
+		ObjectId s2 = s.toObjectId();
+
+		assertEquals(m1, s1);
+		assertEquals(exp, s1);
+		assertEquals(exp, s2);
+	}
+
+	@Test
+	public void shatteredCollision()
+			throws IOException, NoSuchAlgorithmException {
+		byte[] pdf1 = read("shattered-1.pdf", 422435);
+		byte[] pdf2 = read("shattered-2.pdf", 422435);
+		MessageDigest md;
+		SHA1 s;
+
+		// SHAttered attack generated these PDFs to have identical SHA-1.
+		ObjectId bad = ObjectId
+				.fromString("38762cf7f55934b34d179ae6a4c80cadccbb7f0a");
+		md = MessageDigest.getInstance("SHA-1");
+		md.update(pdf1);
+		assertEquals("shattered-1 collides", bad,
+				ObjectId.fromRaw(md.digest()));
+		s = SHA1.newInstance().setDetectCollision(false);
+		s.update(pdf1);
+		assertEquals("shattered-1 collides", bad, s.toObjectId());
+
+		md = MessageDigest.getInstance("SHA-1");
+		md.update(pdf2);
+		assertEquals("shattered-2 collides", bad,
+				ObjectId.fromRaw(md.digest()));
+		s = SHA1.newInstance().setDetectCollision(false);
+		s.update(pdf2);
+		assertEquals("shattered-2 collides", bad, s.toObjectId());
+
+		// SHA1 with detectCollision shouldn't be fooled.
+		s = SHA1.newInstance().setDetectCollision(true);
+		s.update(pdf1);
+		try {
+			s.digest();
+			fail("expected " + Sha1CollisionException.class.getSimpleName());
+		} catch (Sha1CollisionException e) {
+			assertEquals(e.getMessage(),
+					"SHA-1 collision detected on " + bad.name());
+		}
+
+		s = SHA1.newInstance().setDetectCollision(true);
+		s.update(pdf2);
+		try {
+			s.digest();
+			fail("expected " + Sha1CollisionException.class.getSimpleName());
+		} catch (Sha1CollisionException e) {
+			assertEquals(e.getMessage(),
+					"SHA-1 collision detected on " + bad.name());
+		}
+	}
+
+	@Test
+	public void shatteredStoredInGitBlob() throws IOException {
+		byte[] pdf1 = read("shattered-1.pdf", 422435);
+		byte[] pdf2 = read("shattered-2.pdf", 422435);
+
+		// Although the prior test detects the chance of a collision, adding
+		// the Git blob header permutes the data enough for this specific
+		// attack example to not be detected as a collision. (A different file
+		// pair that takes the Git header into account however, would.)
+		ObjectId id1 = blob(pdf1, SHA1.newInstance().setDetectCollision(true));
+		ObjectId id2 = blob(pdf2, SHA1.newInstance().setDetectCollision(true));
+
+		assertEquals(
+				ObjectId.fromString("ba9aaa145ccd24ef760cf31c74d8f7ca1a2e47b0"),
+				id1);
+		assertEquals(
+				ObjectId.fromString("b621eeccd5c7edac9b7dcba35a8d5afd075e24f2"),
+				id2);
+	}
+
+	@Test
+	public void detectsShatteredByDefault() throws IOException {
+		assumeTrue(System.getProperty("org.eclipse.jgit.util.sha1.detectCollision") == null);
+		assumeTrue(System.getProperty("org.eclipse.jgit.util.sha1.safeHash") == null);
+
+		byte[] pdf1 = read("shattered-1.pdf", 422435);
+		SHA1 s = SHA1.newInstance();
+		s.update(pdf1);
+		try {
+			s.digest();
+			fail("expected " + Sha1CollisionException.class.getSimpleName());
+		} catch (Sha1CollisionException e) {
+			assertTrue("shattered-1 detected", true);
+		}
+	}
+
+	private static ObjectId blob(byte[] pdf1, SHA1 s) {
+		s.update(Constants.encodedTypeString(Constants.OBJ_BLOB));
+		s.update((byte) ' ');
+		s.update(Constants.encodeASCII(pdf1.length));
+		s.update((byte) 0);
+		s.update(pdf1);
+		return s.toObjectId();
+	}
+
+	private byte[] read(String name, int sizeHint) throws IOException {
+		try (InputStream in = getClass().getResourceAsStream(name)) {
+			ByteBuffer buf = IO.readWholeStream(in, sizeHint);
+			byte[] r = new byte[buf.remaining()];
+			buf.get(r);
+			return r;
+		}
+	}
+}
diff --git a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
index 808ec3a..ede0f7d 100644
--- a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit.ui/BUCK b/org.eclipse.jgit.ui/BUCK
deleted file mode 100644
index fcd87cf..0000000
--- a/org.eclipse.jgit.ui/BUCK
+++ /dev/null
@@ -1,7 +0,0 @@
-java_library(
-  name = 'ui',
-  srcs = glob(['src/**']),
-  resources = glob(['resources/**']),
-  deps = ['//org.eclipse.jgit:jgit'],
-  visibility = ['PUBLIC'],
-)
diff --git a/org.eclipse.jgit.ui/BUILD b/org.eclipse.jgit.ui/BUILD
new file mode 100644
index 0000000..85ae5c0
--- /dev/null
+++ b/org.eclipse.jgit.ui/BUILD
@@ -0,0 +1,9 @@
+package(default_visibility = ["//visibility:public"])
+
+java_library(
+    name = "ui",
+    srcs = glob(["src/**"]),
+    resource_strip_prefix = "org.eclipse.jgit.ui/resources",
+    resources = glob(["resources/**"]),
+    deps = ["//org.eclipse.jgit:jgit"],
+)
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index 667e3b2..e4834a5 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.2.qualifier
+Bundle-Version: 4.7.2.qualifier
 Bundle-Vendor: %provider_name
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Export-Package: org.eclipse.jgit.awtui;version="4.6.2"
-Import-Package: org.eclipse.jgit.errors;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.lib;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.nls;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.revplot;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.revwalk;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.transport;version="[4.6.2,4.7.0)",
- org.eclipse.jgit.util;version="[4.6.2,4.7.0)"
+Export-Package: org.eclipse.jgit.awtui;version="4.7.2"
+Import-Package: org.eclipse.jgit.errors;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.lib;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.nls;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revplot;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.revwalk;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.transport;version="[4.7.2,4.8.0)",
+ org.eclipse.jgit.util;version="[4.7.2,4.8.0)"
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index 280399b..655e13b 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.2-SNAPSHOT</version>
+    <version>4.7.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>org.eclipse.jgit.ui</artifactId>
diff --git a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/CommitGraphPane.java b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/CommitGraphPane.java
index 4d32235..4c8cf53 100644
--- a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/CommitGraphPane.java
+++ b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/CommitGraphPane.java
@@ -146,14 +146,17 @@
 
 		PersonIdent lastAuthor;
 
+		@Override
 		public int getColumnCount() {
 			return 3;
 		}
 
+		@Override
 		public int getRowCount() {
 			return allCommits != null ? allCommits.size() : 0;
 		}
 
+		@Override
 		public Object getValueAt(final int rowIndex, final int columnIndex) {
 			final PlotCommit<SwingLane> c = allCommits.get(rowIndex);
 			switch (columnIndex) {
@@ -180,6 +183,7 @@
 	static class NameCellRender extends DefaultTableCellRenderer {
 		private static final long serialVersionUID = 1L;
 
+		@Override
 		public Component getTableCellRendererComponent(final JTable table,
 				final Object value, final boolean isSelected,
 				final boolean hasFocus, final int row, final int column) {
@@ -201,6 +205,7 @@
 		private final DateFormat fmt = new SimpleDateFormat(
 				"yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$
 
+		@Override
 		public Component getTableCellRendererComponent(final JTable table,
 				final Object value, final boolean isSelected,
 				final boolean hasFocus, final int row, final int column) {
@@ -223,6 +228,7 @@
 
 		PlotCommit<SwingLane> commit;
 
+		@Override
 		@SuppressWarnings("unchecked")
 		public Component getTableCellRendererComponent(final JTable table,
 				final Object value, final boolean isSelected,
diff --git a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/SwingCommitList.java b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/SwingCommitList.java
index 7359093..fe0c486 100644
--- a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/SwingCommitList.java
+++ b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/SwingCommitList.java
@@ -53,7 +53,7 @@
 	final LinkedList<Color> colors;
 
 	SwingCommitList() {
-		colors = new LinkedList<Color>();
+		colors = new LinkedList<>();
 		repackColors();
 	}
 
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 2c27b05..822f121 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -1,10 +1,61 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <component id="org.eclipse.jgit" version="2">
     <resource path="META-INF/MANIFEST.MF">
-        <filter comment="non-breaking addition of exception classes needed to cleanly fix error handling in PackFile" id="924844039">
+        <filter id="924844039">
             <message_arguments>
-                <message_argument value="4.5.2"/>
-                <message_argument value="4.5.0"/>
+                <message_argument value="4.7.1"/>
+                <message_argument value="4.7.0"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/api/ArchiveCommand.java" type="org.eclipse.jgit.api.ArchiveCommand$Format">
+        <filter comment="OSGi semver allows to break implementors in minor releases" id="403804204">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.api.ArchiveCommand.Format"/>
+                <message_argument value="putEntry(T, ObjectId, String, FileMode, ObjectLoader)"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/errors/NoPackSignatureException.java" type="org.eclipse.jgit.errors.NoPackSignatureException">
+        <filter comment="non-breaking addition of exception classes needed to cleanly fix error handling in PackFile" id="1108344834">
+            <message_arguments>
+                <message_argument value="4.5"/>
+                <message_argument value="4.7"/>
+                <message_argument value="org.eclipse.jgit.errors.NoPackSignatureException"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/errors/UnsupportedPackIndexVersionException.java" type="org.eclipse.jgit.errors.UnsupportedPackIndexVersionException">
+        <filter comment="non-breaking addition of exception classes needed to cleanly fix error handling in PackFile" id="1108344834">
+            <message_arguments>
+                <message_argument value="4.5"/>
+                <message_argument value="4.7"/>
+                <message_argument value="org.eclipse.jgit.errors.UnsupportedPackIndexVersionException"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/errors/UnsupportedPackVersionException.java" type="org.eclipse.jgit.errors.UnsupportedPackVersionException">
+        <filter comment="non-breaking addition of exception classes needed to cleanly fix error handling in PackFile" id="1108344834">
+            <message_arguments>
+                <message_argument value="4.5"/>
+                <message_argument value="4.7"/>
+                <message_argument value="org.eclipse.jgit.errors.UnsupportedPackVersionException"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/lib/ObjectInserter.java" type="org.eclipse.jgit.lib.ObjectInserter">
+        <filter comment="changed return type of digest() breaking implementors which is ok in minor releases according to OSGi semantic versioning" id="338792546">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.lib.ObjectInserter"/>
+                <message_argument value="digest()"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/transport/http/HttpConnection.java" type="org.eclipse.jgit.transport.http.HttpConnection">
+        <filter comment="OSGi semantic versioning rules allow to break implementors in minor releases" id="403767336">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.transport.http.HttpConnection"/>
+                <message_argument value="HTTP_MOVED_PERM"/>
             </message_arguments>
         </filter>
     </resource>
diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
index 4f1759f..06ddbab 100644
--- a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
@@ -56,7 +56,7 @@
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
 org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
 org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
 org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
@@ -76,7 +76,7 @@
 org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
 org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
 org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning
 org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
 org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
diff --git a/org.eclipse.jgit/BUCK b/org.eclipse.jgit/BUCK
deleted file mode 100644
index 2bae6dc..0000000
--- a/org.eclipse.jgit/BUCK
+++ /dev/null
@@ -1,21 +0,0 @@
-SRCS = glob(['src/**'])
-RESOURCES = glob(['resources/**'])
-
-java_library(
-  name = 'jgit',
-  srcs = SRCS,
-  resources = RESOURCES,
-  deps = [
-    '//lib:javaewah',
-    '//lib:jsch',
-    '//lib:httpcomponents',
-    '//lib:servlet-api',
-    '//lib:slf4j-api',
-  ],
-  visibility = ['PUBLIC'],
-)
-
-java_sources(
-  name = 'jgit_src',
-  srcs = SRCS + RESOURCES,
-)
diff --git a/org.eclipse.jgit/BUILD b/org.eclipse.jgit/BUILD
new file mode 100644
index 0000000..75f4fe6
--- /dev/null
+++ b/org.eclipse.jgit/BUILD
@@ -0,0 +1,31 @@
+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",
+        "//lib:javaewah",
+        "//lib:jsch",
+        "//lib:slf4j-api",
+    ],
+)
+
+java_library(
+    name = "insecure_cipher_factory",
+    srcs = INSECURE_CIPHER_FACTORY,
+    javacopts = ["-Xep:InsecureCryptoUsage:OFF"],
+)
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 701efcf..aa88e21 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.2.qualifier
+Bundle-Version: 4.7.2.qualifier
 Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.annotations;version="4.6.2",
- org.eclipse.jgit.api;version="4.6.2";
+Export-Package: org.eclipse.jgit.annotations;version="4.7.2",
+ org.eclipse.jgit.api;version="4.7.2";
   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.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="4.6.2",
- org.eclipse.jgit.blame;version="4.6.2";
+ org.eclipse.jgit.api.errors;version="4.7.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
+ org.eclipse.jgit.attributes;version="4.7.2",
+ org.eclipse.jgit.blame;version="4.7.2";
   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.2";
+ org.eclipse.jgit.diff;version="4.7.2";
   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.2";
+ org.eclipse.jgit.dircache;version="4.7.2";
   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.2";
+ org.eclipse.jgit.errors;version="4.7.2";
   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.2";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="4.6.2",
- org.eclipse.jgit.gitrepo;version="4.6.2";
+ org.eclipse.jgit.events;version="4.7.2";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.fnmatch;version="4.7.2",
+ org.eclipse.jgit.gitrepo;version="4.7.2";
   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.2";x-internal:=true,
- org.eclipse.jgit.hooks;version="4.6.2";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="4.6.2",
- org.eclipse.jgit.ignore.internal;version="4.6.2";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="4.6.2";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.ketch;version="4.6.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.dfs;version="4.6.2";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.2";
+ org.eclipse.jgit.gitrepo.internal;version="4.7.2";x-internal:=true,
+ org.eclipse.jgit.hooks;version="4.7.2";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="4.7.2",
+ org.eclipse.jgit.ignore.internal;version="4.7.2";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal;version="4.7.2";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.ketch;version="4.7.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.dfs;version="4.7.2";
+  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.2";
   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.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftree;version="4.6.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.lib;version="4.6.2";
+ org.eclipse.jgit.internal.storage.pack;version="4.7.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftree;version="4.7.2";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.lib;version="4.7.2";
   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.2";
+ org.eclipse.jgit.merge;version="4.7.2";
   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.2",
- org.eclipse.jgit.notes;version="4.6.2";
+ org.eclipse.jgit.nls;version="4.7.2",
+ org.eclipse.jgit.notes;version="4.7.2";
   uses:="org.eclipse.jgit.lib,
    org.eclipse.jgit.treewalk,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="4.6.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="4.6.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="4.6.2";
+ org.eclipse.jgit.patch;version="4.7.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="4.7.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.revwalk;version="4.7.2";
   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.2";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="4.6.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="4.6.2";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="4.6.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
- org.eclipse.jgit.transport;version="4.6.2";
+ org.eclipse.jgit.revwalk.filter;version="4.7.2";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="4.7.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="4.7.2";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="4.7.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.transport;version="4.7.2";
   uses:="org.eclipse.jgit.transport.resolver,
    org.eclipse.jgit.revwalk,
    org.eclipse.jgit.internal.storage.pack,
@@ -118,29 +122,29 @@
    org.eclipse.jgit.transport.http,
    org.eclipse.jgit.errors,
    org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="4.6.2";uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="4.6.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
- org.eclipse.jgit.treewalk;version="4.6.2";
+ org.eclipse.jgit.transport.http;version="4.7.2";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="4.7.2";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
+ org.eclipse.jgit.treewalk;version="4.7.2";
   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.2";uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="4.6.2";
+ org.eclipse.jgit.treewalk.filter;version="4.7.2";uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.util;version="4.7.2";
   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.2",
- org.eclipse.jgit.util.time;version="4.6.2"
+ org.eclipse.jgit.util.io;version="4.7.2",
+ org.eclipse.jgit.util.sha1;version="4.7.2",
+ org.eclipse.jgit.util.time;version="4.7.2"
 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 c863b04..d849a0e 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.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="4.6.2.qualifier";roots="."
+Bundle-Version: 4.7.2.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="4.7.2.qualifier";roots="."
diff --git a/org.eclipse.jgit/about.html b/org.eclipse.jgit/about.html
index 01a2671..f971af1 100644
--- a/org.eclipse.jgit/about.html
+++ b/org.eclipse.jgit/about.html
@@ -21,6 +21,10 @@
     margin-top:  0.05em;
     margin-bottom: 0.05em;
     }
+  .ubc-name {
+    margin-left: 0.5in;
+    white-space: pre;
+  }
   </style>
 
 </head>
@@ -54,6 +58,39 @@
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 POSSIBILITY OF SUCH DAMAGE.</p>
 
+<hr>
+<p><b>SHA-1 UbcCheck - MIT</b></p>
+
+<p>Copyright (c) 2017:</p>
+<div class="ubc-name">
+Marc Stevens
+Cryptology Group
+Centrum Wiskunde & Informatica
+P.O. Box 94079, 1090 GB Amsterdam, Netherlands
+marc@marc-stevens.nl
+</div>
+<div class="ubc-name">
+Dan Shumow
+Microsoft Research
+danshu@microsoft.com
+</div>
+<p>Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+</p>
+<ul><li>The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.</li></ul>
+<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.</p>
+
 </body>
 
 </html>
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index cf935ae..3feb523 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.2-SNAPSHOT</version>
+    <version>4.7.2-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/gitrepo/internal/RepoText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/gitrepo/internal/RepoText.properties
index 7443ad3..e942038 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/gitrepo/internal/RepoText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/gitrepo/internal/RepoText.properties
@@ -3,6 +3,7 @@
 errorIncludeNotImplemented=Error: <include> tag not supported as no callback defined.
 errorNoDefault=Error: no default remote in manifest file.
 errorNoDefaultFilename=Error: no default remote in manifest file {0}.
+errorNoFetch=Error: remote {0} is missing fetch attribute.
 errorParsingManifestFile=Error occurred during parsing manifest file.
 errorRemoteUnavailable=Error remote {0} is unavailable.
 invalidManifest=Invalid manifest.
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 992b80b..03065da 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
@@ -76,7 +77,7 @@
 cannotListObjectsPath=Cannot ls {0}/{1}: {2}
 cannotListPackPath=Cannot ls {0}/pack: {1}
 cannotListRefs=cannot list refs
-cannotLock=Cannot lock {0}
+cannotLock=Cannot lock {0}. Ensure that no other process has an open file handle on the lock file {0}.lock, then you may delete the lock file and retry.
 cannotLockPackIn=Cannot lock pack in {0}
 cannotMatchOnEmptyString=Cannot match on empty string.
 cannotMkdirObjectPath=Cannot mkdir {0}/{1}: {2}
@@ -187,7 +188,7 @@
 corruptObjectTruncatedInName=truncated in name
 corruptObjectTruncatedInObjectId=truncated in object id
 corruptObjectZeroId=entry points to null SHA-1
-corruptUseCnt=close() called when useCnt is already zero
+corruptUseCnt=close() called when useCnt is already zero for {0}
 couldNotCheckOutBecauseOfConflicts=Could not check out because of conflicts
 couldNotDeleteLockFileShouldNotHappen=Could not delete lock file. Should not happen
 couldNotDeleteTemporaryIndexFileShouldNotHappen=Could not delete temporary index file. Should not happen
@@ -298,6 +299,8 @@
 flagsAlreadyCreated={0} flags already created.
 funnyRefname=funny refname
 gcFailed=Garbage collection failed.
+gcLogExists=A previous GC run reported an error: ''{0}''.  Automatic gc will fail until ''{1}'' is removed.
+gcTooManyUnpruned=Too many loose, unpruneable objects after garbage collection.  Consider adjusting gc.auto or gc.pruneExpire.
 gitmodulesNotFound=.gitmodules not found in tree.
 headRequiredToStash=HEAD required to stash local changes
 hoursAgo={0} hours ago
@@ -563,6 +566,7 @@
 sequenceTooLargeForDiffAlgorithm=Sequence too large for difference algorithm.
 serviceNotEnabledNoName=Service not enabled
 serviceNotPermitted={0} not permitted
+sha1CollisionDetected1=SHA-1 collision detected on {0}
 shallowCommitsAlreadyInitialized=Shallow commits have already been initialized
 shallowPacksRequireDepthWalk=Shallow packs require a DepthWalk
 shortCompressedStreamAt=Short compressed stream at {0}
@@ -608,6 +612,7 @@
 theFactoryMustNotBeNull=The factory must not be null
 timeIsUncertain=Time is uncertain
 timerAlreadyTerminated=Timer already terminated
+tooManyCommands=Too many commands
 tooManyIncludeRecursions=Too many recursions; circular includes in config file(s)?
 topologicalSortRequired=Topological sort required.
 transactionAborted=transaction aborted
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/util/sha1/SHA1.compress b/org.eclipse.jgit/resources/org/eclipse/jgit/util/sha1/SHA1.compress
new file mode 100644
index 0000000..3a80fd2
--- /dev/null
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/util/sha1/SHA1.compress
@@ -0,0 +1,92 @@
+/* Template for compress method; run through cpp. */
+
+#define ROUND1_STEP(a, b, c, d, e, T) e += s1(a,b,c,d,w[T]); b = rotateLeft(b, 30)
+#define ROUND2_STEP(a, b, c, d, e, T) e += s2(a,b,c,d,w[T]); b = rotateLeft(b, 30)
+#define ROUND3_STEP(a, b, c, d, e, T) e += s3(a,b,c,d,w[T]); b = rotateLeft(b, 30)
+#define ROUND4_STEP(a, b, c, d, e, T) e += s4(a,b,c,d,w[T]); b = rotateLeft(b, 30)
+
+	ROUND1_STEP(a, b, c, d, e, 0);
+	ROUND1_STEP(e, a, b, c, d, 1);
+	ROUND1_STEP(d, e, a, b, c, 2);
+	ROUND1_STEP(c, d, e, a, b, 3);
+	ROUND1_STEP(b, c, d, e, a, 4);
+	ROUND1_STEP(a, b, c, d, e, 5);
+	ROUND1_STEP(e, a, b, c, d, 6);
+	ROUND1_STEP(d, e, a, b, c, 7);
+	ROUND1_STEP(c, d, e, a, b, 8);
+	ROUND1_STEP(b, c, d, e, a, 9);
+	ROUND1_STEP(a, b, c, d, e, 10);
+	ROUND1_STEP(e, a, b, c, d, 11);
+	ROUND1_STEP(d, e, a, b, c, 12);
+	ROUND1_STEP(c, d, e, a, b, 13);
+	ROUND1_STEP(b, c, d, e, a, 14);
+	ROUND1_STEP(a, b, c, d, e, 15);
+	ROUND1_STEP(e, a, b, c, d, 16);
+	ROUND1_STEP(d, e, a, b, c, 17);
+	ROUND1_STEP(c, d, e, a, b, 18);
+	ROUND1_STEP(b, c, d, e, a, 19);
+
+	ROUND2_STEP(a, b, c, d, e, 20);
+	ROUND2_STEP(e, a, b, c, d, 21);
+	ROUND2_STEP(d, e, a, b, c, 22);
+	ROUND2_STEP(c, d, e, a, b, 23);
+	ROUND2_STEP(b, c, d, e, a, 24);
+	ROUND2_STEP(a, b, c, d, e, 25);
+	ROUND2_STEP(e, a, b, c, d, 26);
+	ROUND2_STEP(d, e, a, b, c, 27);
+	ROUND2_STEP(c, d, e, a, b, 28);
+	ROUND2_STEP(b, c, d, e, a, 29);
+	ROUND2_STEP(a, b, c, d, e, 30);
+	ROUND2_STEP(e, a, b, c, d, 31);
+	ROUND2_STEP(d, e, a, b, c, 32);
+	ROUND2_STEP(c, d, e, a, b, 33);
+	ROUND2_STEP(b, c, d, e, a, 34);
+	ROUND2_STEP(a, b, c, d, e, 35);
+	ROUND2_STEP(e, a, b, c, d, 36);
+	ROUND2_STEP(d, e, a, b, c, 37);
+	ROUND2_STEP(c, d, e, a, b, 38);
+	ROUND2_STEP(b, c, d, e, a, 39);
+
+	ROUND3_STEP(a, b, c, d, e, 40);
+	ROUND3_STEP(e, a, b, c, d, 41);
+	ROUND3_STEP(d, e, a, b, c, 42);
+	ROUND3_STEP(c, d, e, a, b, 43);
+	ROUND3_STEP(b, c, d, e, a, 44);
+	ROUND3_STEP(a, b, c, d, e, 45);
+	ROUND3_STEP(e, a, b, c, d, 46);
+	ROUND3_STEP(d, e, a, b, c, 47);
+	ROUND3_STEP(c, d, e, a, b, 48);
+	ROUND3_STEP(b, c, d, e, a, 49);
+	ROUND3_STEP(a, b, c, d, e, 50);
+	ROUND3_STEP(e, a, b, c, d, 51);
+	ROUND3_STEP(d, e, a, b, c, 52);
+	ROUND3_STEP(c, d, e, a, b, 53);
+	ROUND3_STEP(b, c, d, e, a, 54);
+	ROUND3_STEP(a, b, c, d, e, 55);
+	ROUND3_STEP(e, a, b, c, d, 56);
+	ROUND3_STEP(d, e, a, b, c, 57);
+	state58.save(a, b, c, d, e);
+	ROUND3_STEP(c, d, e, a, b, 58);
+	ROUND3_STEP(b, c, d, e, a, 59);
+
+	ROUND4_STEP(a, b, c, d, e, 60);
+	ROUND4_STEP(e, a, b, c, d, 61);
+	ROUND4_STEP(d, e, a, b, c, 62);
+	ROUND4_STEP(c, d, e, a, b, 63);
+	ROUND4_STEP(b, c, d, e, a, 64);
+	state65.save(a, b, c, d, e);
+	ROUND4_STEP(a, b, c, d, e, 65);
+	ROUND4_STEP(e, a, b, c, d, 66);
+	ROUND4_STEP(d, e, a, b, c, 67);
+	ROUND4_STEP(c, d, e, a, b, 68);
+	ROUND4_STEP(b, c, d, e, a, 69);
+	ROUND4_STEP(a, b, c, d, e, 70);
+	ROUND4_STEP(e, a, b, c, d, 71);
+	ROUND4_STEP(d, e, a, b, c, 72);
+	ROUND4_STEP(c, d, e, a, b, 73);
+	ROUND4_STEP(b, c, d, e, a, 74);
+	ROUND4_STEP(a, b, c, d, e, 75);
+	ROUND4_STEP(e, a, b, c, d, 76);
+	ROUND4_STEP(d, e, a, b, c, 77);
+	ROUND4_STEP(c, d, e, a, b, 78);
+	ROUND4_STEP(b, c, d, e, a, 79);
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/util/sha1/SHA1.recompress b/org.eclipse.jgit/resources/org/eclipse/jgit/util/sha1/SHA1.recompress
new file mode 100644
index 0000000..44d3d6d
--- /dev/null
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/util/sha1/SHA1.recompress
@@ -0,0 +1,192 @@
+/* Template for recompress method; run through cpp. */
+
+#define ROUND1_STEP(a, b, c, d, e, T) {e += s1(a,b,c,d,w2[T]); b = rotateLeft(b, 30);}
+#define ROUND2_STEP(a, b, c, d, e, T) {e += s2(a,b,c,d,w2[T]); b = rotateLeft(b, 30);}
+#define ROUND3_STEP(a, b, c, d, e, T) {e += s3(a,b,c,d,w2[T]); b = rotateLeft(b, 30);}
+#define ROUND4_STEP(a, b, c, d, e, T) {e += s4(a,b,c,d,w2[T]); b = rotateLeft(b, 30);}
+
+#define ROUND1_STEP_BW(a, b, c, d, e, T) {b = rotateRight(b, 30); e -= s1(a,b,c,d,w2[T]);}
+#define ROUND2_STEP_BW(a, b, c, d, e, T) {b = rotateRight(b, 30); e -= s2(a,b,c,d,w2[T]);}
+#define ROUND3_STEP_BW(a, b, c, d, e, T) {b = rotateRight(b, 30); e -= s3(a,b,c,d,w2[T]);}
+#define ROUND4_STEP_BW(a, b, c, d, e, T) {b = rotateRight(b, 30); e -= s4(a,b,c,d,w2[T]);}
+
+	/* Condition to go backwards: if (t > step) */
+  /* t=80-66 have no identified DV; skip.
+	ROUND4_STEP_BW(b, c, d, e, a, 79)
+	ROUND4_STEP_BW(c, d, e, a, b, 78)
+	ROUND4_STEP_BW(d, e, a, b, c, 77)
+	ROUND4_STEP_BW(e, a, b, c, d, 76)
+	ROUND4_STEP_BW(a, b, c, d, e, 75)
+	ROUND4_STEP_BW(b, c, d, e, a, 74)
+	ROUND4_STEP_BW(c, d, e, a, b, 73)
+	ROUND4_STEP_BW(d, e, a, b, c, 72)
+	ROUND4_STEP_BW(e, a, b, c, d, 71)
+	ROUND4_STEP_BW(a, b, c, d, e, 70)
+	ROUND4_STEP_BW(b, c, d, e, a, 69)
+	ROUND4_STEP_BW(c, d, e, a, b, 68)
+	ROUND4_STEP_BW(d, e, a, b, c, 67)
+	ROUND4_STEP_BW(e, a, b, c, d, 66)
+	ROUND4_STEP_BW(a, b, c, d, e, 65)
+  */
+  if (t == 65) {
+	ROUND4_STEP_BW(b, c, d, e, a, 64)
+	ROUND4_STEP_BW(c, d, e, a, b, 63)
+	ROUND4_STEP_BW(d, e, a, b, c, 62)
+	ROUND4_STEP_BW(e, a, b, c, d, 61)
+	ROUND4_STEP_BW(a, b, c, d, e, 60)
+
+	ROUND3_STEP_BW(b, c, d, e, a, 59)
+	ROUND3_STEP_BW(c, d, e, a, b, 58)
+  }
+	ROUND3_STEP_BW(d, e, a, b, c, 57)
+	ROUND3_STEP_BW(e, a, b, c, d, 56)
+	ROUND3_STEP_BW(a, b, c, d, e, 55)
+	ROUND3_STEP_BW(b, c, d, e, a, 54)
+	ROUND3_STEP_BW(c, d, e, a, b, 53)
+	ROUND3_STEP_BW(d, e, a, b, c, 52)
+	ROUND3_STEP_BW(e, a, b, c, d, 51)
+	ROUND3_STEP_BW(a, b, c, d, e, 50)
+	ROUND3_STEP_BW(b, c, d, e, a, 49)
+	ROUND3_STEP_BW(c, d, e, a, b, 48)
+	ROUND3_STEP_BW(d, e, a, b, c, 47)
+	ROUND3_STEP_BW(e, a, b, c, d, 46)
+	ROUND3_STEP_BW(a, b, c, d, e, 45)
+	ROUND3_STEP_BW(b, c, d, e, a, 44)
+	ROUND3_STEP_BW(c, d, e, a, b, 43)
+	ROUND3_STEP_BW(d, e, a, b, c, 42)
+	ROUND3_STEP_BW(e, a, b, c, d, 41)
+	ROUND3_STEP_BW(a, b, c, d, e, 40)
+
+	ROUND2_STEP_BW(b, c, d, e, a, 39)
+	ROUND2_STEP_BW(c, d, e, a, b, 38)
+	ROUND2_STEP_BW(d, e, a, b, c, 37)
+	ROUND2_STEP_BW(e, a, b, c, d, 36)
+	ROUND2_STEP_BW(a, b, c, d, e, 35)
+	ROUND2_STEP_BW(b, c, d, e, a, 34)
+	ROUND2_STEP_BW(c, d, e, a, b, 33)
+	ROUND2_STEP_BW(d, e, a, b, c, 32)
+	ROUND2_STEP_BW(e, a, b, c, d, 31)
+	ROUND2_STEP_BW(a, b, c, d, e, 30)
+	ROUND2_STEP_BW(b, c, d, e, a, 29)
+	ROUND2_STEP_BW(c, d, e, a, b, 28)
+	ROUND2_STEP_BW(d, e, a, b, c, 27)
+	ROUND2_STEP_BW(e, a, b, c, d, 26)
+	ROUND2_STEP_BW(a, b, c, d, e, 25)
+	ROUND2_STEP_BW(b, c, d, e, a, 24)
+	ROUND2_STEP_BW(c, d, e, a, b, 23)
+	ROUND2_STEP_BW(d, e, a, b, c, 22)
+	ROUND2_STEP_BW(e, a, b, c, d, 21)
+	ROUND2_STEP_BW(a, b, c, d, e, 20)
+
+	ROUND1_STEP_BW(b, c, d, e, a, 19)
+	ROUND1_STEP_BW(c, d, e, a, b, 18)
+	ROUND1_STEP_BW(d, e, a, b, c, 17)
+	ROUND1_STEP_BW(e, a, b, c, d, 16)
+	ROUND1_STEP_BW(a, b, c, d, e, 15)
+	ROUND1_STEP_BW(b, c, d, e, a, 14)
+	ROUND1_STEP_BW(c, d, e, a, b, 13)
+	ROUND1_STEP_BW(d, e, a, b, c, 12)
+	ROUND1_STEP_BW(e, a, b, c, d, 11)
+	ROUND1_STEP_BW(a, b, c, d, e, 10)
+	ROUND1_STEP_BW(b, c, d, e, a, 9)
+	ROUND1_STEP_BW(c, d, e, a, b, 8)
+	ROUND1_STEP_BW(d, e, a, b, c, 7)
+	ROUND1_STEP_BW(e, a, b, c, d, 6)
+	ROUND1_STEP_BW(a, b, c, d, e, 5)
+	ROUND1_STEP_BW(b, c, d, e, a, 4)
+	ROUND1_STEP_BW(c, d, e, a, b, 3)
+	ROUND1_STEP_BW(d, e, a, b, c, 2)
+	ROUND1_STEP_BW(e, a, b, c, d, 1)
+	ROUND1_STEP_BW(a, b, c, d, e, 0)
+
+	hIn.save(a, b, c, d, e);
+	a = s.a; b = s.b; c = s.c; d = s.d; e = s.e;
+
+	/* Condition to go fowards: if (t <= step) */
+  /* Earliest restart is T=58; skip.
+	ROUND1_STEP(a, b, c, d, e, 0)
+	ROUND1_STEP(e, a, b, c, d, 1)
+	ROUND1_STEP(d, e, a, b, c, 2)
+	ROUND1_STEP(c, d, e, a, b, 3)
+	ROUND1_STEP(b, c, d, e, a, 4)
+	ROUND1_STEP(a, b, c, d, e, 5)
+	ROUND1_STEP(e, a, b, c, d, 6)
+	ROUND1_STEP(d, e, a, b, c, 7)
+	ROUND1_STEP(c, d, e, a, b, 8)
+	ROUND1_STEP(b, c, d, e, a, 9)
+	ROUND1_STEP(a, b, c, d, e, 10)
+	ROUND1_STEP(e, a, b, c, d, 11)
+	ROUND1_STEP(d, e, a, b, c, 12)
+	ROUND1_STEP(c, d, e, a, b, 13)
+	ROUND1_STEP(b, c, d, e, a, 14)
+	ROUND1_STEP(a, b, c, d, e, 15)
+	ROUND1_STEP(e, a, b, c, d, 16)
+	ROUND1_STEP(d, e, a, b, c, 17)
+	ROUND1_STEP(c, d, e, a, b, 18)
+	ROUND1_STEP(b, c, d, e, a, 19)
+
+	ROUND2_STEP(a, b, c, d, e, 20)
+	ROUND2_STEP(e, a, b, c, d, 21)
+	ROUND2_STEP(d, e, a, b, c, 22)
+	ROUND2_STEP(c, d, e, a, b, 23)
+	ROUND2_STEP(b, c, d, e, a, 24)
+	ROUND2_STEP(a, b, c, d, e, 25)
+	ROUND2_STEP(e, a, b, c, d, 26)
+	ROUND2_STEP(d, e, a, b, c, 27)
+	ROUND2_STEP(c, d, e, a, b, 28)
+	ROUND2_STEP(b, c, d, e, a, 29)
+	ROUND2_STEP(a, b, c, d, e, 30)
+	ROUND2_STEP(e, a, b, c, d, 31)
+	ROUND2_STEP(d, e, a, b, c, 32)
+	ROUND2_STEP(c, d, e, a, b, 33)
+	ROUND2_STEP(b, c, d, e, a, 34)
+	ROUND2_STEP(a, b, c, d, e, 35)
+	ROUND2_STEP(e, a, b, c, d, 36)
+	ROUND2_STEP(d, e, a, b, c, 37)
+	ROUND2_STEP(c, d, e, a, b, 38)
+	ROUND2_STEP(b, c, d, e, a, 39)
+
+	ROUND3_STEP(a, b, c, d, e, 40)
+	ROUND3_STEP(e, a, b, c, d, 41)
+	ROUND3_STEP(d, e, a, b, c, 42)
+	ROUND3_STEP(c, d, e, a, b, 43)
+	ROUND3_STEP(b, c, d, e, a, 44)
+	ROUND3_STEP(a, b, c, d, e, 45)
+	ROUND3_STEP(e, a, b, c, d, 46)
+	ROUND3_STEP(d, e, a, b, c, 47)
+	ROUND3_STEP(c, d, e, a, b, 48)
+	ROUND3_STEP(b, c, d, e, a, 49)
+	ROUND3_STEP(a, b, c, d, e, 50)
+	ROUND3_STEP(e, a, b, c, d, 51)
+	ROUND3_STEP(d, e, a, b, c, 52)
+	ROUND3_STEP(c, d, e, a, b, 53)
+	ROUND3_STEP(b, c, d, e, a, 54)
+	ROUND3_STEP(a, b, c, d, e, 55)
+	ROUND3_STEP(e, a, b, c, d, 56)
+	ROUND3_STEP(d, e, a, b, c, 57)
+  */
+  if (t == 58) {
+	ROUND3_STEP(c, d, e, a, b, 58)
+	ROUND3_STEP(b, c, d, e, a, 59)
+
+	ROUND4_STEP(a, b, c, d, e, 60)
+	ROUND4_STEP(e, a, b, c, d, 61)
+	ROUND4_STEP(d, e, a, b, c, 62)
+	ROUND4_STEP(c, d, e, a, b, 63)
+	ROUND4_STEP(b, c, d, e, a, 64)
+  }
+	ROUND4_STEP(a, b, c, d, e, 65)
+	ROUND4_STEP(e, a, b, c, d, 66)
+	ROUND4_STEP(d, e, a, b, c, 67)
+	ROUND4_STEP(c, d, e, a, b, 68)
+	ROUND4_STEP(b, c, d, e, a, 69)
+	ROUND4_STEP(a, b, c, d, e, 70)
+	ROUND4_STEP(e, a, b, c, d, 71)
+	ROUND4_STEP(d, e, a, b, c, 72)
+	ROUND4_STEP(c, d, e, a, b, 73)
+	ROUND4_STEP(b, c, d, e, a, 74)
+	ROUND4_STEP(a, b, c, d, e, 75)
+	ROUND4_STEP(e, a, b, c, d, 76)
+	ROUND4_STEP(d, e, a, b, c, 77)
+	ROUND4_STEP(c, d, e, a, b, 78)
+	ROUND4_STEP(b, c, d, e, a, 79)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
index 16ec146..1ed7944 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
@@ -96,7 +96,7 @@
 	 */
 	public AddCommand(Repository repo) {
 		super(repo);
-		filepatterns = new LinkedList<String>();
+		filepatterns = new LinkedList<>();
 	}
 
 	/**
@@ -134,6 +134,7 @@
 	 *
 	 * @return the DirCache after Add
 	 */
+	@Override
 	public DirCache call() throws GitAPIException, NoFilepatternException {
 
 		if (filepatterns.isEmpty())
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java
index 4235e37..fa88fb7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java
@@ -81,6 +81,7 @@
 		super(repo);
 	}
 
+	@Override
 	public Note call() throws GitAPIException {
 		checkCallable();
 		NoteMap map = NoteMap.newEmptyMap();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
index d74e991..ba5673d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
@@ -109,6 +109,7 @@
 	 * @throws PatchFormatException
 	 * @throws PatchApplyException
 	 */
+	@Override
 	public ApplyResult call() throws GitAPIException, PatchFormatException,
 			PatchApplyException {
 		checkCallable();
@@ -197,10 +198,10 @@
 	private void apply(File f, FileHeader fh)
 			throws IOException, PatchApplyException {
 		RawText rt = new RawText(f);
-		List<String> oldLines = new ArrayList<String>(rt.size());
+		List<String> oldLines = new ArrayList<>(rt.size());
 		for (int i = 0; i < rt.size(); i++)
 			oldLines.add(rt.getString(i));
-		List<String> newLines = new ArrayList<String>(oldLines);
+		List<String> newLines = new ArrayList<>(oldLines);
 		for (HunkHeader hh : fh.getHunks()) {
 
 			byte[] b = new byte[hh.getEndOffset() - hh.getStartOffset()];
@@ -208,7 +209,7 @@
 					b.length);
 			RawText hrt = new RawText(b);
 
-			List<String> hunkLines = new ArrayList<String>(hrt.size());
+			List<String> hunkLines = new ArrayList<>(hrt.size());
 			for (int i = 0; i < hrt.size(); i++)
 				hunkLines.add(hrt.getString(i));
 			int pos = 0;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyResult.java
index 558ef0f..2ef6650 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyResult.java
@@ -53,7 +53,7 @@
  */
 public class ApplyResult {
 
-	private List<File> updatedFiles = new ArrayList<File>();
+	private List<File> updatedFiles = new ArrayList<>();
 
 	/**
 	 * @param f
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
index 8543bd5..7ea8e73 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
@@ -162,8 +162,8 @@
 		 * @param out
 		 *            archive object from createArchiveOutputStream
 		 * @param path
-		 *            full filename relative to the root of the archive
-		 *            (with trailing '/' for directories)
+		 *            full filename relative to the root of the archive (with
+		 *            trailing '/' for directories)
 		 * @param mode
 		 *            mode (for example FileMode.REGULAR_FILE or
 		 *            FileMode.SYMLINK)
@@ -171,9 +171,36 @@
 		 *            blob object with data for this entry (null for
 		 *            directories)
 		 * @throws IOException
-		 *            thrown by the underlying output stream for I/O errors
+		 *             thrown by the underlying output stream for I/O errors
+		 * @deprecated use
+		 *             {@link #putEntry(Closeable, ObjectId, String, FileMode, ObjectLoader)}
+		 *             instead
 		 */
+		@Deprecated
 		void putEntry(T out, String path, FileMode mode,
+					  ObjectLoader loader) throws IOException;
+
+		/**
+		 * Write an entry to an archive.
+		 *
+		 * @param out
+		 *            archive object from createArchiveOutputStream
+		 * @param tree
+		 *            the tag, commit, or tree object to produce an archive for
+		 * @param path
+		 *            full filename relative to the root of the archive (with
+		 *            trailing '/' for directories)
+		 * @param mode
+		 *            mode (for example FileMode.REGULAR_FILE or
+		 *            FileMode.SYMLINK)
+		 * @param loader
+		 *            blob object with data for this entry (null for
+		 *            directories)
+		 * @throws IOException
+		 *             thrown by the underlying output stream for I/O errors
+		 * @since 4.7
+		 */
+		void putEntry(T out, ObjectId tree, String path, FileMode mode,
 				ObjectLoader loader) throws IOException;
 
 		/**
@@ -232,7 +259,7 @@
 	 * the --format= option)
 	 */
 	private static final ConcurrentMap<String, FormatEntry> formats =
-			new ConcurrentHashMap<String, FormatEntry>();
+			new ConcurrentHashMap<>();
 
 	/**
 	 * Replaces the entry for a key only if currently mapped to a given
@@ -350,7 +377,7 @@
 	private String prefix;
 	private String format;
 	private Map<String, Object> formatOptions = new HashMap<>();
-	private List<String> paths = new ArrayList<String>();
+	private List<String> paths = new ArrayList<>();
 
 	/** Filename suffix, for automatically choosing a format. */
 	private String suffix;
@@ -376,6 +403,12 @@
 				if (!paths.isEmpty())
 					walk.setFilter(PathFilterGroup.createFromStrings(paths));
 
+				// Put base directory into archive
+				if (pfx.endsWith("/")) { //$NON-NLS-1$
+					fmt.putEntry(outa, tree, pfx.replaceAll("[/]+$", "/"), //$NON-NLS-1$ //$NON-NLS-2$
+							FileMode.TREE, null);
+				}
+
 				while (walk.next()) {
 					String name = pfx + walk.getPathString();
 					FileMode mode = walk.getFileMode(0);
@@ -389,11 +422,11 @@
 						mode = FileMode.TREE;
 
 					if (mode == FileMode.TREE) {
-						fmt.putEntry(outa, name + "/", mode, null); //$NON-NLS-1$
+						fmt.putEntry(outa, tree, name + "/", mode, null); //$NON-NLS-1$
 						continue;
 					}
 					walk.getObjectId(idBuf, 0);
-					fmt.putEntry(outa, name, mode, reader.open(idBuf));
+					fmt.putEntry(outa, tree, name, mode, reader.open(idBuf));
 				}
 				outa.close();
 				return out;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
index 2a2e07d..b1c81ff 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
@@ -188,7 +188,7 @@
 	public BlameCommand reverse(AnyObjectId start, Collection<ObjectId> end)
 			throws IOException {
 		startCommit = start.toObjectId();
-		reverseEndCommits = new ArrayList<ObjectId>(end);
+		reverseEndCommits = new ArrayList<>(end);
 		return this;
 	}
 
@@ -198,6 +198,7 @@
 	 *
 	 * @return list of lines
 	 */
+	@Override
 	public BlameResult call() throws GitAPIException {
 		checkCallable();
 		try (BlameGenerator gen = new BlameGenerator(repo, path)) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
index c17ae5c..21d6283 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -180,7 +180,7 @@
 	 */
 	protected CheckoutCommand(Repository repo) {
 		super(repo);
-		this.paths = new LinkedList<String>();
+		this.paths = new LinkedList<>();
 	}
 
 	/**
@@ -196,6 +196,7 @@
 	 *             if the checkout results in a conflict
 	 * @return the newly created branch
 	 */
+	@Override
 	public Ref call() throws GitAPIException, RefAlreadyExistsException,
 			RefNotFoundException, InvalidRefNameException,
 			CheckoutConflictException {
@@ -319,10 +320,10 @@
 			if (!dco.getToBeDeleted().isEmpty()) {
 				status = new CheckoutResult(Status.NONDELETED,
 						dco.getToBeDeleted(),
-						new ArrayList<String>(dco.getUpdated().keySet()),
+						new ArrayList<>(dco.getUpdated().keySet()),
 						dco.getRemoved());
 			} else
-				status = new CheckoutResult(new ArrayList<String>(dco
+				status = new CheckoutResult(new ArrayList<>(dco
 						.getUpdated().keySet()), dco.getRemoved());
 
 			return ref;
@@ -455,6 +456,7 @@
 			final String filterCommand = treeWalk
 					.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE);
 			editor.add(new PathEdit(path) {
+				@Override
 				public void apply(DirCacheEntry ent) {
 					int stage = ent.getStage();
 					if (stage > DirCacheEntry.STAGE_0) {
@@ -491,6 +493,7 @@
 			final String filterCommand = treeWalk
 					.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE);
 			editor.add(new PathEdit(treeWalk.getPathString()) {
+				@Override
 				public void apply(DirCacheEntry ent) {
 					ent.setObjectId(blobId);
 					ent.setFileMode(mode);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutResult.java
index 92a67f4..2186eb4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutResult.java
@@ -139,11 +139,11 @@
 		if (status == Status.CONFLICTS)
 			this.conflictList = fileList;
 		else
-			this.conflictList = new ArrayList<String>(0);
+			this.conflictList = new ArrayList<>(0);
 		if (status == Status.NONDELETED)
 			this.undeletedList = fileList;
 		else
-			this.undeletedList = new ArrayList<String>(0);
+			this.undeletedList = new ArrayList<>(0);
 
 		this.modifiedList = modified;
 		this.removedList = removed;
@@ -160,8 +160,8 @@
 	CheckoutResult(List<String> modified, List<String> removed) {
 		myStatus = Status.OK;
 
-		this.conflictList = new ArrayList<String>(0);
-		this.undeletedList = new ArrayList<String>(0);
+		this.conflictList = new ArrayList<>(0);
+		this.undeletedList = new ArrayList<>(0);
 
 		this.modifiedList = modified;
 		this.removedList = removed;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
index 276bf96..eed7b2a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
@@ -85,7 +85,7 @@
 public class CherryPickCommand extends GitCommand<CherryPickResult> {
 	private String reflogPrefix = "cherry-pick:"; //$NON-NLS-1$
 
-	private List<Ref> commits = new LinkedList<Ref>();
+	private List<Ref> commits = new LinkedList<>();
 
 	private String ourCommitName = null;
 
@@ -116,11 +116,12 @@
 	 * @throws NoMessageException
 	 * @throws NoHeadException
 	 */
+	@Override
 	public CherryPickResult call() throws GitAPIException, NoMessageException,
 			UnmergedPathsException, ConcurrentRefUpdateException,
 			WrongRepositoryStateException, NoHeadException {
 		RevCommit newHead = null;
-		List<Ref> cherryPickedRefs = new LinkedList<Ref>();
+		List<Ref> cherryPickedRefs = new LinkedList<>();
 		checkCallable();
 
 		try (RevWalk revWalk = new RevWalk(repo)) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
index 7e331fd..c58efb1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
@@ -94,15 +94,16 @@
 	 * @throws GitAPIException
 	 * @throws NoWorkTreeException
 	 */
+	@Override
 	public Set<String> call() throws NoWorkTreeException, GitAPIException {
-		Set<String> files = new TreeSet<String>();
+		Set<String> files = new TreeSet<>();
 		try {
 			StatusCommand command = new StatusCommand(repo);
 			Status status = command.call();
 
-			Set<String> untrackedAndIgnoredFiles = new TreeSet<String>(
+			Set<String> untrackedAndIgnoredFiles = new TreeSet<>(
 					status.getUntracked());
-			Set<String> untrackedAndIgnoredDirs = new TreeSet<String>(
+			Set<String> untrackedAndIgnoredDirs = new TreeSet<>(
 					status.getUntrackedFolders());
 
 			FS fs = getRepository().getFS();
@@ -191,7 +192,7 @@
 	private Set<String> filterIgnorePaths(Set<String> inputPaths,
 			Set<String> ignoredNotInIndex, boolean exact) {
 		if (ignore) {
-			Set<String> filtered = new TreeSet<String>(inputPaths);
+			Set<String> filtered = new TreeSet<>(inputPaths);
 			for (String path : inputPaths)
 				for (String ignored : ignoredNotInIndex)
 					if ((exact && path.equals(ignored))
@@ -207,7 +208,7 @@
 
 	private Set<String> filterFolders(Set<String> untracked,
 			Set<String> untrackedFolders) {
-		Set<String> filtered = new TreeSet<String>(untracked);
+		Set<String> filtered = new TreeSet<>(untracked);
 		for (String file : untracked)
 			for (String folder : untrackedFolders)
 				if (file.startsWith(folder)) {
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..4b815b4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -127,6 +127,7 @@
 	 * @throws org.eclipse.jgit.api.errors.TransportException
 	 * @throws GitAPIException
 	 */
+	@Override
 	public Git call() throws GitAPIException, InvalidRemoteException,
 			org.eclipse.jgit.api.errors.TransportException {
 		Repository repository = null;
@@ -151,23 +152,35 @@
 		}
 	}
 
+	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();
 	}
 
@@ -207,7 +220,7 @@
 		RefSpec wcrs = new RefSpec();
 		wcrs = wcrs.setForceUpdate(true);
 		wcrs = wcrs.setSourceDestination(Constants.R_HEADS + "*", dst); //$NON-NLS-1$
-		List<RefSpec> specs = new ArrayList<RefSpec>();
+		List<RefSpec> specs = new ArrayList<>();
 		if (cloneAllBranches)
 			specs.add(wcrs);
 		else if (branchesToClone != null
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
index e1793f3..274ece6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -112,7 +112,7 @@
 
 	private boolean all;
 
-	private List<String> only = new ArrayList<String>();
+	private List<String> only = new ArrayList<>();
 
 	private boolean[] onlyProcessed;
 
@@ -124,7 +124,7 @@
 	 * parents this commit should have. The current HEAD will be in this list
 	 * and also all commits mentioned in .git/MERGE_HEAD
 	 */
-	private List<ObjectId> parents = new LinkedList<ObjectId>();
+	private List<ObjectId> parents = new LinkedList<>();
 
 	private String reflogComment;
 
@@ -168,6 +168,7 @@
 	 *             if there are either pre-commit or commit-msg hooks present in
 	 *             the repository and one of them rejects the commit.
 	 */
+	@Override
 	public RevCommit call() throws GitAPIException, NoHeadException,
 			NoMessageException, UnmergedPathsException,
 			ConcurrentRefUpdateException, WrongRepositoryStateException,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CreateBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CreateBranchCommand.java
index 69d82bc..39420d0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CreateBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CreateBranchCommand.java
@@ -120,6 +120,7 @@
 	 *             invalid
 	 * @return the newly created branch
 	 */
+	@Override
 	public Ref call() throws GitAPIException, RefAlreadyExistsException,
 			RefNotFoundException, InvalidRefNameException {
 		checkCallable();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java
index ecc1ce5..d7e7e5c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java
@@ -79,7 +79,7 @@
  *      >Git documentation about Branch</a>
  */
 public class DeleteBranchCommand extends GitCommand<List<String>> {
-	private final Set<String> branchNames = new HashSet<String>();
+	private final Set<String> branchNames = new HashSet<>();
 
 	private boolean force;
 
@@ -97,10 +97,11 @@
 	 * @throws CannotDeleteCurrentBranchException
 	 * @return the list with the (full) names of the deleted branches
 	 */
+	@Override
 	public List<String> call() throws GitAPIException,
 			NotMergedException, CannotDeleteCurrentBranchException {
 		checkCallable();
-		List<String> result = new ArrayList<String>();
+		List<String> result = new ArrayList<>();
 		if (branchNames.isEmpty())
 			return result;
 		try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteTagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteTagCommand.java
index 3aeec48..77e3539 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteTagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteTagCommand.java
@@ -68,7 +68,7 @@
  */
 public class DeleteTagCommand extends GitCommand<List<String>> {
 
-	private final Set<String> tags = new HashSet<String>();
+	private final Set<String> tags = new HashSet<>();
 
 	/**
 	 * @param repo
@@ -80,9 +80,10 @@
 	/**
 	 * @return the list with the full names of the deleted tags
 	 */
+	@Override
 	public List<String> call() throws GitAPIException {
 		checkCallable();
-		List<String> result = new ArrayList<String>();
+		List<String> result = new ArrayList<>();
 		if (tags.isEmpty())
 			return result;
 		try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
index be45666..389c511 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
@@ -189,7 +189,7 @@
 			if (target == null)
 				setTarget(Constants.HEAD);
 
-			Map<ObjectId, Ref> tags = new HashMap<ObjectId, Ref>();
+			Map<ObjectId, Ref> tags = new HashMap<>();
 
 			for (Ref r : repo.getRefDatabase().getRefs(R_TAGS).values()) {
 				ObjectId key = repo.peel(r).getPeeledObjectId();
@@ -240,7 +240,7 @@
 				}
 
 			}
-			List<Candidate> candidates = new ArrayList<Candidate>();    // all the candidates we find
+			List<Candidate> candidates = new ArrayList<>();    // all the candidates we find
 
 			// is the target already pointing to a tag? if so, we are done!
 			Ref lucky = tags.get(target);
@@ -305,6 +305,7 @@
 				return null;
 
 			Candidate best = Collections.min(candidates, new Comparator<Candidate>() {
+				@Override
 				public int compare(Candidate o1, Candidate o2) {
 					return o1.depth - o2.depth;
 				}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java
index 3e3a7a8..b137fc5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java
@@ -109,6 +109,7 @@
 	 *
 	 * @return a DiffEntry for each path which is different
 	 */
+	@Override
 	public List<DiffEntry> call() throws GitAPIException {
 		final DiffFormatter diffFmt;
 		if (out != null && !showNameAndStatusOnly)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
index de51276..cc3302b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -42,14 +42,17 @@
  */
 package org.eclipse.jgit.api;
 
+import java.io.IOException;
 import java.net.URISyntaxException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.InvalidConfigurationException;
 import org.eclipse.jgit.api.errors.InvalidRemoteException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.errors.NoRemoteRepositoryException;
 import org.eclipse.jgit.errors.NotSupportedException;
 import org.eclipse.jgit.errors.TransportException;
@@ -57,9 +60,13 @@
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.submodule.SubmoduleWalk;
 import org.eclipse.jgit.transport.FetchResult;
 import org.eclipse.jgit.transport.RefSpec;
 import org.eclipse.jgit.transport.TagOpt;
@@ -74,7 +81,6 @@
  *      >Git documentation about Fetch</a>
  */
 public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
-
 	private String remote = Constants.DEFAULT_REMOTE_NAME;
 
 	private List<RefSpec> refSpecs;
@@ -91,12 +97,90 @@
 
 	private TagOpt tagOption;
 
+	private FetchRecurseSubmodulesMode submoduleRecurseMode = null;
+
 	/**
 	 * @param repo
 	 */
 	protected FetchCommand(Repository repo) {
 		super(repo);
-		refSpecs = new ArrayList<RefSpec>(3);
+		refSpecs = new ArrayList<>(3);
+	}
+
+	private FetchRecurseSubmodulesMode getRecurseMode(String path) {
+		// Use the caller-specified mode, if set
+		if (submoduleRecurseMode != null) {
+			return submoduleRecurseMode;
+		}
+
+		// Fall back to submodule.name.fetchRecurseSubmodules, if set
+		FetchRecurseSubmodulesMode mode = repo.getConfig().getEnum(
+				FetchRecurseSubmodulesMode.values(),
+				ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+				ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES, null);
+		if (mode != null) {
+			return mode;
+		}
+
+		// Fall back to fetch.recurseSubmodules, if set
+		mode = repo.getConfig().getEnum(FetchRecurseSubmodulesMode.values(),
+				ConfigConstants.CONFIG_FETCH_SECTION, null,
+				ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES, null);
+		if (mode != null) {
+			return mode;
+		}
+
+		// Default to on-demand mode
+		return FetchRecurseSubmodulesMode.ON_DEMAND;
+	}
+
+	private void fetchSubmodules(FetchResult results)
+			throws org.eclipse.jgit.api.errors.TransportException,
+			GitAPIException, InvalidConfigurationException {
+		try (SubmoduleWalk walk = new SubmoduleWalk(repo);
+				RevWalk revWalk = new RevWalk(repo)) {
+			// Walk over submodules in the parent repository's FETCH_HEAD.
+			ObjectId fetchHead = repo.resolve(Constants.FETCH_HEAD);
+			if (fetchHead == null) {
+				return;
+			}
+			walk.setTree(revWalk.parseTree(fetchHead));
+			while (walk.next()) {
+				Repository submoduleRepo = walk.getRepository();
+
+				// Skip submodules that don't exist locally (have not been
+				// cloned), are not registered in the .gitmodules file, or
+				// not registered in the parent repository's config.
+				if (submoduleRepo == null || walk.getModulesPath() == null
+						|| walk.getConfigUrl() == null) {
+					continue;
+				}
+
+				FetchRecurseSubmodulesMode recurseMode = getRecurseMode(
+						walk.getPath());
+
+				// When the fetch mode is "yes" we always fetch. When the mode
+				// is "on demand", we only fetch if the submodule's revision was
+				// updated to an object that is not currently present in the
+				// submodule.
+				if ((recurseMode == FetchRecurseSubmodulesMode.ON_DEMAND
+						&& !submoduleRepo.hasObject(walk.getObjectId()))
+						|| recurseMode == FetchRecurseSubmodulesMode.YES) {
+					FetchCommand f = new FetchCommand(submoduleRepo)
+							.setProgressMonitor(monitor).setTagOpt(tagOption)
+							.setCheckFetchedObjects(checkFetchedObjects)
+							.setRemoveDeletedRefs(isRemoveDeletedRefs())
+							.setThin(thin).setRefSpecs(refSpecs)
+							.setDryRun(dryRun)
+							.setRecurseSubmodules(recurseMode);
+					results.addSubmodule(walk.getPath(), f.call());
+				}
+			}
+		} catch (IOException e) {
+			throw new JGitInternalException(e.getMessage(), e);
+		} catch (ConfigInvalidException e) {
+			throw new InvalidConfigurationException(e.getMessage(), e);
+		}
 	}
 
 	/**
@@ -112,6 +196,7 @@
 	 * @throws org.eclipse.jgit.api.errors.TransportException
 	 *             when an error occurs during transport
 	 */
+	@Override
 	public FetchResult call() throws GitAPIException, InvalidRemoteException,
 			org.eclipse.jgit.api.errors.TransportException {
 		checkCallable();
@@ -126,6 +211,10 @@
 			configure(transport);
 
 			FetchResult result = transport.fetch(monitor, refSpecs);
+			if (!repo.isBare()) {
+				fetchSubmodules(result);
+			}
+
 			return result;
 		} catch (NoRemoteRepositoryException e) {
 			throw new InvalidRemoteException(MessageFormat.format(
@@ -145,6 +234,20 @@
 	}
 
 	/**
+	 * Set the mode to be used for recursing into submodules.
+	 *
+	 * @param recurse
+	 * @return {@code this}
+	 * @since 4.7
+	 */
+	public FetchCommand setRecurseSubmodules(
+			FetchRecurseSubmodulesMode recurse) {
+		checkCallable();
+		submoduleRecurseMode = recurse;
+		return this;
+	}
+
+	/**
 	 * The remote (uri or name) used for the fetch operation. If no remote is
 	 * set, the default value of <code>Constants.DEFAULT_REMOTE_NAME</code> will
 	 * be used.
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 @@
 		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/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
index 2cd5f59..9699569 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -147,6 +147,7 @@
 	 *
 	 * @since 3.2
 	 */
+	@Override
 	public void close() {
 		if (closeRepo)
 			repo.close();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/GitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/GitCommand.java
index e9751f9..2a23408 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/GitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/GitCommand.java
@@ -126,5 +126,6 @@
 	 * @throws GitAPIException
 	 *             or subclass thereof when an error occurs
 	 */
+	@Override
 	public abstract T call() throws GitAPIException;
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
index 37a788e..649484c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/InitCommand.java
@@ -73,6 +73,7 @@
 	 *
 	 * @return the newly created {@code Git} object with associated repository
 	 */
+	@Override
 	public Git call() throws GitAPIException {
 		try {
 			RepositoryBuilder builder = new RepositoryBuilder();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java
index efcfd16..961eeaa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java
@@ -101,11 +101,12 @@
 		super(repo);
 	}
 
+	@Override
 	public List<Ref> call() throws GitAPIException {
 		checkCallable();
 		List<Ref> resultRefs;
 		try {
-			Collection<Ref> refs = new ArrayList<Ref>();
+			Collection<Ref> refs = new ArrayList<>();
 
 			// Also return HEAD if it's detached
 			Ref head = repo.exactRef(Constants.HEAD);
@@ -120,12 +121,13 @@
 				refs.addAll(getRefs(Constants.R_HEADS));
 				refs.addAll(getRefs(Constants.R_REMOTES));
 			}
-			resultRefs = new ArrayList<Ref>(filterRefs(refs));
+			resultRefs = new ArrayList<>(filterRefs(refs));
 		} catch (IOException e) {
 			throw new JGitInternalException(e.getMessage(), e);
 		}
 
 		Collections.sort(resultRefs, new Comparator<Ref>() {
+			@Override
 			public int compare(Ref o1, Ref o2) {
 				return o1.getName().compareTo(o2.getName());
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListNotesCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListNotesCommand.java
index ff963ed..476c10b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListNotesCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListNotesCommand.java
@@ -77,9 +77,10 @@
 	/**
 	 * @return the requested notes
 	 */
+	@Override
 	public List<Note> call() throws GitAPIException {
 		checkCallable();
-		List<Note> notes = new ArrayList<Note>();
+		List<Note> notes = new ArrayList<>();
 		NoteMap map = NoteMap.newEmptyMap();
 		try (RevWalk walk = new RevWalk(repo)) {
 			Ref ref = repo.findRef(notesRef);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
index a3b701b..d649a53 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
@@ -74,10 +74,11 @@
 	/**
 	 * @return the tags available
 	 */
+	@Override
 	public List<Ref> call() throws GitAPIException {
 		checkCallable();
 		Map<String, Ref> refList;
-		List<Ref> tags = new ArrayList<Ref>();
+		List<Ref> tags = new ArrayList<>();
 		try (RevWalk revWalk = new RevWalk(repo)) {
 			refList = repo.getRefDatabase().getRefs(Constants.R_TAGS);
 			for (Ref ref : refList.values()) {
@@ -87,6 +88,7 @@
 			throw new JGitInternalException(e.getMessage(), e);
 		}
 		Collections.sort(tags, new Comparator<Ref>() {
+			@Override
 			public int compare(Ref o1, Ref o2) {
 				return o1.getName().compareTo(o2.getName());
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
index bb1a738..f8fe95a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
@@ -107,7 +107,7 @@
 
 	private RevFilter revFilter;
 
-	private final List<PathFilter> pathFilters = new ArrayList<PathFilter>();
+	private final List<PathFilter> pathFilters = new ArrayList<>();
 
 	private int maxCount = -1;
 
@@ -132,6 +132,7 @@
 	 * @throws NoHeadException
 	 *             of the references ref cannot be resolved
 	 */
+	@Override
 	public Iterable<RevCommit> call() throws GitAPIException, NoHeadException {
 		checkCallable();
 		if (pathFilters.size() > 0)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java
index f3527fd..5157a41 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java
@@ -153,6 +153,7 @@
 	 * @throws org.eclipse.jgit.api.errors.TransportException
 	 *             for errors that occurs during transport
 	 */
+	@Override
 	public Collection<Ref> call() throws GitAPIException,
 			InvalidRemoteException,
 			org.eclipse.jgit.api.errors.TransportException {
@@ -187,14 +188,14 @@
 				: Transport.open(new URIish(remote))) {
 			transport.setOptionUploadPack(uploadPack);
 			configure(transport);
-			Collection<RefSpec> refSpecs = new ArrayList<RefSpec>(1);
+			Collection<RefSpec> refSpecs = new ArrayList<>(1);
 			if (tags)
 				refSpecs.add(new RefSpec(
 						"refs/tags/*:refs/remotes/origin/tags/*")); //$NON-NLS-1$
 			if (heads)
 				refSpecs.add(new RefSpec("refs/heads/*:refs/remotes/origin/*")); //$NON-NLS-1$
 			Collection<Ref> refs;
-			Map<String, Ref> refmap = new HashMap<String, Ref>();
+			Map<String, Ref> refmap = new HashMap<>();
 			try (FetchConnection fc = transport.openFetch()) {
 				refs = fc.getRefs();
 				if (refSpecs.isEmpty())
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
index ced1863..b5d9e8a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
@@ -50,6 +50,7 @@
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 import org.eclipse.jgit.api.MergeResult.MergeStatus;
@@ -101,7 +102,7 @@
 
 	private MergeStrategy mergeStrategy = MergeStrategy.RECURSIVE;
 
-	private List<Ref> commits = new LinkedList<Ref>();
+	private List<Ref> commits = new LinkedList<>();
 
 	private Boolean squash;
 
@@ -133,10 +134,12 @@
 		 */
 		FF_ONLY;
 
+		@Override
 		public String toConfigValue() {
-			return "--" + name().toLowerCase().replace('_', '-'); //$NON-NLS-1$
+			return "--" + name().toLowerCase(Locale.ROOT).replace('_', '-'); //$NON-NLS-1$
 		}
 
+		@Override
 		public boolean matchConfigValue(String in) {
 			if (StringUtils.isEmptyOrNull(in))
 				return false;
@@ -220,6 +223,7 @@
 	 *
 	 * @return the result of the merge
 	 */
+	@Override
 	@SuppressWarnings("boxing")
 	public MergeResult call() throws GitAPIException, NoHeadException,
 			ConcurrentRefUpdateException, CheckoutConflictException,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
index 6141e0c..c487ef6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
@@ -185,6 +185,7 @@
 		 * @since 3.0
 		 **/
 		MERGED_NOT_COMMITTED {
+			@Override
 			public String toString() {
 				return "Merged-not-committed"; //$NON-NLS-1$
 			}
@@ -212,6 +213,7 @@
 		 * files (i.e. local modifications prevent checkout of files).
 		 */
 		CHECKOUT_CONFLICT {
+			@Override
 			public String toString() {
 				return "Checkout Conflict"; //$NON-NLS-1$
 			}
@@ -414,7 +416,7 @@
 	 */
 	public void addConflict(String path, int[][] conflictingRanges) {
 		if (conflicts == null)
-			conflicts = new HashMap<String, int[][]>();
+			conflicts = new HashMap<>();
 		conflicts.put(path, conflictingRanges);
 	}
 
@@ -426,7 +428,7 @@
 		if (!lowLevelResult.containsConflicts())
 			return;
 		if (conflicts == null)
-			conflicts = new HashMap<String, int[][]>();
+			conflicts = new HashMap<>();
 		int nrOfConflicts = 0;
 		// just counting
 		for (MergeChunk mergeChunk : lowLevelResult) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/NameRevCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/NameRevCommand.java
index fd28d0e..2a86fab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/NameRevCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/NameRevCommand.java
@@ -124,8 +124,8 @@
 	protected NameRevCommand(Repository repo) {
 		super(repo);
 		mergeCost = MERGE_COST;
-		prefixes = new ArrayList<String>(2);
-		revs = new ArrayList<ObjectId>(2);
+		prefixes = new ArrayList<>(2);
+		revs = new ArrayList<>(2);
 		walk = new RevWalk(repo) {
 			@Override
 			public NameRevCommit createCommit(AnyObjectId id) {
@@ -137,7 +137,7 @@
 	@Override
 	public Map<ObjectId, String> call() throws GitAPIException {
 		try {
-			Map<ObjectId, String> nonCommits = new HashMap<ObjectId, String>();
+			Map<ObjectId, String> nonCommits = new HashMap<>();
 			FIFORevQueue pending = new FIFORevQueue();
 			if (refs != null) {
 				for (Ref ref : refs)
@@ -170,7 +170,7 @@
 			}
 
 			Map<ObjectId, String> result =
-				new LinkedHashMap<ObjectId, String>(revs.size());
+				new LinkedHashMap<>(revs.size());
 			for (ObjectId id : revs) {
 				RevObject o = walk.parseAny(id);
 				if (o instanceof NameRevCommit) {
@@ -275,7 +275,7 @@
 	public NameRevCommand addAnnotatedTags() {
 		checkCallable();
 		if (refs == null)
-			refs = new ArrayList<Ref>();
+			refs = new ArrayList<>();
 		try {
 			for (Ref ref : repo.getRefDatabase().getRefs(Constants.R_TAGS).values()) {
 				ObjectId id = ref.getObjectId();
@@ -302,7 +302,7 @@
 	public NameRevCommand addRef(Ref ref) {
 		checkCallable();
 		if (refs == null)
-			refs = new ArrayList<Ref>();
+			refs = new ArrayList<>();
 		refs.add(ref);
 		return this;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
index a4d9ec1..ae822da 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -69,8 +69,10 @@
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryState;
+import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode;
 import org.eclipse.jgit.merge.MergeStrategy;
 import org.eclipse.jgit.transport.FetchResult;
+import org.eclipse.jgit.transport.TagOpt;
 
 /**
  * The Pull command
@@ -92,6 +94,10 @@
 
 	private MergeStrategy strategy = MergeStrategy.RECURSIVE;
 
+	private TagOpt tagOption;
+
+	private FetchRecurseSubmodulesMode submoduleRecurseMode = null;
+
 	/**
 	 * @param repo
 	 */
@@ -194,6 +200,7 @@
 	 * @throws org.eclipse.jgit.api.errors.TransportException
 	 * @throws GitAPIException
 	 */
+	@Override
 	public PullResult call() throws GitAPIException,
 			WrongRepositoryStateException, InvalidConfigurationException,
 			DetachedHeadException, InvalidRemoteException, CanceledException,
@@ -272,9 +279,9 @@
 						JGitText.get().operationCanceled,
 						JGitText.get().pullTaskName));
 
-			FetchCommand fetch = new FetchCommand(repo);
-			fetch.setRemote(remote);
-			fetch.setProgressMonitor(monitor);
+			FetchCommand fetch = new FetchCommand(repo).setRemote(remote)
+					.setProgressMonitor(monitor).setTagOpt(tagOption)
+					.setRecurseSubmodules(submoduleRecurseMode);
 			configure(fetch);
 
 			fetchRes = fetch.call();
@@ -412,6 +419,32 @@
 	}
 
 	/**
+	 * Sets the specification of annotated tag behavior during fetch
+	 *
+	 * @param tagOpt
+	 * @return {@code this}
+	 * @since 4.7
+	 */
+	public PullCommand setTagOpt(TagOpt tagOpt) {
+		checkCallable();
+		this.tagOption = tagOpt;
+		return this;
+	}
+
+	/**
+	 * Set the mode to be used for recursing into submodules.
+	 *
+	 * @param recurse
+	 * @return {@code this}
+	 * @since 4.7
+	 */
+	public PullCommand setRecurseSubmodules(
+			FetchRecurseSubmodulesMode recurse) {
+		this.submoduleRecurseMode = recurse;
+		return this;
+	}
+
+	/**
 	 * Reads the rebase mode to use for a pull command from the repository
 	 * configuration. This is the value defined for the configurations
 	 * {@code branch.[branchName].rebase}, or,if not set, {@code pull.rebase}.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
index bd4521b..bf88842 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
@@ -47,9 +47,12 @@
 import java.net.URISyntaxException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.InvalidRemoteException;
@@ -65,6 +68,7 @@
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.transport.PushResult;
+import org.eclipse.jgit.transport.RefLeaseSpec;
 import org.eclipse.jgit.transport.RefSpec;
 import org.eclipse.jgit.transport.RemoteConfig;
 import org.eclipse.jgit.transport.RemoteRefUpdate;
@@ -85,6 +89,8 @@
 
 	private final List<RefSpec> refSpecs;
 
+	private final Map<String, RefLeaseSpec> refLeaseSpecs;
+
 	private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
 
 	private String receivePack = RemoteConfig.DEFAULT_RECEIVE_PACK;
@@ -103,7 +109,8 @@
 	 */
 	protected PushCommand(Repository repo) {
 		super(repo);
-		refSpecs = new ArrayList<RefSpec>(3);
+		refSpecs = new ArrayList<>(3);
+		refLeaseSpecs = new HashMap<>();
 	}
 
 	/**
@@ -119,12 +126,13 @@
 	 *             when an error occurs with the transport
 	 * @throws GitAPIException
 	 */
+	@Override
 	public Iterable<PushResult> call() throws GitAPIException,
 			InvalidRemoteException,
 			org.eclipse.jgit.api.errors.TransportException {
 		checkCallable();
 
-		ArrayList<PushResult> pushResults = new ArrayList<PushResult>(3);
+		ArrayList<PushResult> pushResults = new ArrayList<>(3);
 
 		try {
 			if (refSpecs.isEmpty()) {
@@ -155,7 +163,7 @@
 				configure(transport);
 
 				final Collection<RemoteRefUpdate> toPush = transport
-						.findRemoteRefUpdatesFor(refSpecs);
+						.findRemoteRefUpdatesFor(refSpecs, refLeaseSpecs);
 
 				try {
 					PushResult result = transport.push(monitor, toPush, out);
@@ -271,6 +279,43 @@
 	}
 
 	/**
+	 * @return the ref lease specs
+	 * @since 4.7
+	 */
+	public List<RefLeaseSpec> getRefLeaseSpecs() {
+		return new ArrayList<>(refLeaseSpecs.values());
+	}
+
+	/**
+	 * The ref lease specs to be used in the push operation,
+	 * for a force-with-lease push operation.
+	 *
+	 * @param specs
+	 * @return {@code this}
+	 * @since 4.7
+	 */
+	public PushCommand setRefLeaseSpecs(RefLeaseSpec... specs) {
+		return setRefLeaseSpecs(Arrays.asList(specs));
+	}
+
+	/**
+	 * The ref lease specs to be used in the push operation,
+	 * for a force-with-lease push operation.
+	 *
+	 * @param specs
+	 * @return {@code this}
+	 * @since 4.7
+	 */
+	public PushCommand setRefLeaseSpecs(List<RefLeaseSpec> specs) {
+		checkCallable();
+		this.refLeaseSpecs.clear();
+		for (RefLeaseSpec spec : specs) {
+			refLeaseSpecs.put(spec.getRef(), spec);
+		}
+		return this;
+	}
+
+	/**
 	 * @return the ref specs
 	 */
 	public List<RefSpec> getRefSpecs() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index d10cc3d..850ff49 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -258,6 +258,7 @@
 	 * @throws NoHeadException
 	 * @throws RefNotFoundException
 	 */
+	@Override
 	public RebaseResult call() throws GitAPIException, NoHeadException,
 			RefNotFoundException, WrongRepositoryStateException {
 		newHead = null;
@@ -298,7 +299,7 @@
 					org.eclipse.jgit.api.Status status = Git.wrap(repo)
 							.status().setIgnoreSubmodules(IgnoreSubmoduleMode.ALL).call();
 					if (status.hasUncommittedChanges()) {
-						List<String> list = new ArrayList<String>();
+						List<String> list = new ArrayList<>();
 						list.addAll(status.getUncommittedChanges());
 						return RebaseResult.uncommittedChanges(list);
 					}
@@ -649,7 +650,7 @@
 	// Get the rewritten equivalents for the parents of the given commit
 	private List<RevCommit> getNewParents(RevCommit commitToPick)
 			throws IOException {
-		List<RevCommit> newParents = new ArrayList<RevCommit>();
+		List<RevCommit> newParents = new ArrayList<>();
 		for (int p = 0; p < commitToPick.getParentCount(); p++) {
 			String parentHash = commitToPick.getParent(p).getName();
 			if (!new File(rebaseState.getRewrittenDir(), parentHash).exists())
@@ -805,8 +806,12 @@
 			if (!line.trim().startsWith("#")) //$NON-NLS-1$
 				result.append(line).append("\n"); //$NON-NLS-1$
 		}
-		if (!commitMessage.endsWith("\n")) //$NON-NLS-1$
-			result.deleteCharAt(result.length() - 1);
+		if (!commitMessage.endsWith("\n")) { //$NON-NLS-1$
+			int bufferSize = result.length();
+			if (bufferSize > 0 && result.charAt(bufferSize - 1) == '\n') {
+				result.deleteCharAt(bufferSize - 1);
+			}
+		}
 		return result.toString();
 	}
 
@@ -1055,8 +1060,8 @@
 	private void popSteps(int numSteps) throws IOException {
 		if (numSteps == 0)
 			return;
-		List<RebaseTodoLine> todoLines = new LinkedList<RebaseTodoLine>();
-		List<RebaseTodoLine> poppedLines = new LinkedList<RebaseTodoLine>();
+		List<RebaseTodoLine> todoLines = new LinkedList<>();
+		List<RebaseTodoLine> poppedLines = new LinkedList<>();
 
 		for (RebaseTodoLine line : repo.readRebaseTodo(
 				rebaseState.getPath(GIT_REBASE_TODO), true)) {
@@ -1121,7 +1126,7 @@
 		}
 		rebaseState.createFile(QUIET, ""); //$NON-NLS-1$
 
-		ArrayList<RebaseTodoLine> toDoSteps = new ArrayList<RebaseTodoLine>();
+		ArrayList<RebaseTodoLine> toDoSteps = new ArrayList<>();
 		toDoSteps.add(new RebaseTodoLine("# Created by EGit: rebasing " + headId.name() //$NON-NLS-1$
 						+ " onto " + upstreamCommit.name())); //$NON-NLS-1$
 		// determine the commits to be applied
@@ -1157,7 +1162,7 @@
 			LogCommand cmd = git.log().addRange(upstreamCommit, headCommit);
 			commitsToUse = cmd.call();
 		}
-		List<RevCommit> cherryPickList = new ArrayList<RevCommit>();
+		List<RevCommit> cherryPickList = new ArrayList<>();
 		for (RevCommit commit : commitsToUse) {
 			if (preserveMerges || commit.getParentCount() == 1)
 				cherryPickList.add(commit);
@@ -1604,7 +1609,7 @@
 		if (raw.length == 0)
 			return null;
 
-		Map<String, String> keyValueMap = new HashMap<String, String>();
+		Map<String, String> keyValueMap = new HashMap<>();
 		for (int p = 0; p < raw.length;) {
 			int end = RawParseUtils.nextLF(raw, p);
 			if (end == p)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
index 4536af1..04caa0f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
@@ -92,6 +92,7 @@
 	 * @throws GitAPIException
 	 * @throws InvalidRefNameException
 	 */
+	@Override
 	public Collection<ReflogEntry> call() throws GitAPIException,
 			InvalidRefNameException {
 		checkCallable();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java
index 1c4880c..fd8aac7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java
@@ -79,6 +79,7 @@
 		super(repo);
 	}
 
+	@Override
 	public Note call() throws GitAPIException {
 		checkCallable();
 		try (RevWalk walk = new RevWalk(repo);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
index 044a486..ce3a29f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
@@ -96,6 +96,7 @@
 	 *             if rename is tried without specifying the old name and HEAD
 	 *             is detached
 	 */
+	@Override
 	public Ref call() throws GitAPIException, RefNotFoundException, InvalidRefNameException,
 			RefAlreadyExistsException, DetachedHeadException {
 		checkCallable();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
index 106988d..c5222c2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
@@ -121,7 +121,7 @@
 
 	private ResetType mode;
 
-	private Collection<String> filepaths = new LinkedList<String>();
+	private Collection<String> filepaths = new LinkedList<>();
 
 	private boolean isReflogDisabled;
 
@@ -141,6 +141,7 @@
 	 * @return the Ref after reset
 	 * @throws GitAPIException
 	 */
+	@Override
 	public Ref call() throws GitAPIException, CheckoutConflictException {
 		checkCallable();
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
index 9d79ed0..c3152a9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
@@ -85,11 +85,11 @@
  *      >Git documentation about revert</a>
  */
 public class RevertCommand extends GitCommand<RevCommit> {
-	private List<Ref> commits = new LinkedList<Ref>();
+	private List<Ref> commits = new LinkedList<>();
 
 	private String ourCommitName = null;
 
-	private List<Ref> revertedRefs = new LinkedList<Ref>();
+	private List<Ref> revertedRefs = new LinkedList<>();
 
 	private MergeResult failingResult;
 
@@ -120,6 +120,7 @@
 	 * @throws UnmergedPathsException
 	 * @throws NoMessageException
 	 */
+	@Override
 	public RevCommit call() throws NoMessageException, UnmergedPathsException,
 			ConcurrentRefUpdateException, WrongRepositoryStateException,
 			GitAPIException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java
index fd2cbe0..9e2cf31 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java
@@ -99,7 +99,7 @@
 	 */
 	public RmCommand(Repository repo) {
 		super(repo);
-		filepatterns = new LinkedList<String>();
+		filepatterns = new LinkedList<>();
 	}
 
 	/**
@@ -136,6 +136,7 @@
 	 *
 	 * @return the DirCache after Rm
 	 */
+	@Override
 	public DirCache call() throws GitAPIException,
 			NoFilepatternException {
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ShowNoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ShowNoteCommand.java
index 168ea51..dbff463 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ShowNoteCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ShowNoteCommand.java
@@ -74,6 +74,7 @@
 		super(repo);
 	}
 
+	@Override
 	public Note call() throws GitAPIException {
 		checkCallable();
 		NoteMap map = NoteMap.newEmptyMap();
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..10ec2a6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
@@ -157,6 +157,7 @@
 	 * @throws NoHeadException
 	 * @throws StashApplyFailureException
 	 */
+	@Override
 	public ObjectId call() throws GitAPIException,
 			WrongRepositoryStateException, NoHeadException,
 			StashApplyFailureException {
@@ -232,19 +233,19 @@
 					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/api/StashCreateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
index ee9b7fc..681f8e6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
@@ -236,6 +236,7 @@
 	 * @return stashed commit or null if no changes to stash
 	 * @throws GitAPIException
 	 */
+	@Override
 	public RevCommit call() throws GitAPIException {
 		checkCallable();
 
@@ -261,9 +262,9 @@
 					return null;
 
 				MutableObjectId id = new MutableObjectId();
-				List<PathEdit> wtEdits = new ArrayList<PathEdit>();
-				List<String> wtDeletes = new ArrayList<String>();
-				List<DirCacheEntry> untracked = new ArrayList<DirCacheEntry>();
+				List<PathEdit> wtEdits = new ArrayList<>();
+				List<String> wtDeletes = new ArrayList<>();
+				List<DirCacheEntry> untracked = new ArrayList<>();
 				boolean hasChanges = false;
 				do {
 					AbstractTreeIterator headIter = treeWalk.getTree(0,
@@ -305,6 +306,7 @@
 							untracked.add(entry);
 						else
 							wtEdits.add(new PathEdit(entry) {
+								@Override
 								public void apply(DirCacheEntry ent) {
 									ent.copyMetaData(entry);
 								}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
index 6e1d0f2..e215bdf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
@@ -171,6 +171,7 @@
 	 * @return commit id of stash reference or null if no more stashed changes
 	 * @throws GitAPIException
 	 */
+	@Override
 	public ObjectId call() throws GitAPIException {
 		checkCallable();
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashListCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashListCommand.java
index aedc9a6..8420dd2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashListCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashListCommand.java
@@ -76,6 +76,7 @@
 		super(repo);
 	}
 
+	@Override
 	public Collection<RevCommit> call() throws GitAPIException,
 			InvalidRefNameException {
 		checkCallable();
@@ -94,7 +95,7 @@
 		if (stashEntries.isEmpty())
 			return Collections.emptyList();
 
-		final List<RevCommit> stashCommits = new ArrayList<RevCommit>(
+		final List<RevCommit> stashCommits = new ArrayList<>(
 				stashEntries.size());
 		try (RevWalk walk = new RevWalk(repo)) {
 			for (ReflogEntry entry : stashEntries) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Status.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Status.java
index 48759c2..5b7c73b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Status.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Status.java
@@ -189,7 +189,7 @@
 	 * @since 3.2
 	 */
 	public Set<String> getUncommittedChanges() {
-		Set<String> uncommittedChanges = new HashSet<String>();
+		Set<String> uncommittedChanges = new HashSet<>();
 		uncommittedChanges.addAll(diff.getAdded());
 		uncommittedChanges.addAll(diff.getChanged());
 		uncommittedChanges.addAll(diff.getRemoved());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StatusCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StatusCommand.java
index 9752195..8f7804a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StatusCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StatusCommand.java
@@ -109,7 +109,7 @@
 	 */
 	public StatusCommand addPath(String path) {
 		if (paths == null)
-			paths = new LinkedList<String>();
+			paths = new LinkedList<>();
 		paths.add(path);
 		return this;
 	}
@@ -134,6 +134,7 @@
 	 * @return a {@link Status} object telling about each path where working
 	 *         tree, index or HEAD differ from each other.
 	 */
+	@Override
 	public Status call() throws GitAPIException, NoWorkTreeException {
 		if (workingTreeIt == null)
 			workingTreeIt = new FileTreeIterator(repo);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
index b0f772e..0519d45 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
@@ -149,6 +149,7 @@
 	 * @return the newly created {@link Repository}
 	 * @throws GitAPIException
 	 */
+	@Override
 	public Repository call() throws GitAPIException {
 		checkCallable();
 		if (path == null || path.length() == 0)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java
index 1dbe368..4c5e317 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java
@@ -76,7 +76,7 @@
 	 */
 	public SubmoduleInitCommand(final Repository repo) {
 		super(repo);
-		paths = new ArrayList<String>();
+		paths = new ArrayList<>();
 	}
 
 	/**
@@ -91,6 +91,7 @@
 		return this;
 	}
 
+	@Override
 	public Collection<String> call() throws GitAPIException {
 		checkCallable();
 
@@ -98,7 +99,7 @@
 			if (!paths.isEmpty())
 				generator.setFilter(PathFilterGroup.createFromStrings(paths));
 			StoredConfig config = repo.getConfig();
-			List<String> initialized = new ArrayList<String>();
+			List<String> initialized = new ArrayList<>();
 			while (generator.next()) {
 				// Ignore entry if URL is already present in config file
 				if (generator.getConfigUrl() != null)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java
index a1ea790..8b27e4c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java
@@ -76,7 +76,7 @@
 	 */
 	public SubmoduleStatusCommand(final Repository repo) {
 		super(repo);
-		paths = new ArrayList<String>();
+		paths = new ArrayList<>();
 	}
 
 	/**
@@ -91,13 +91,14 @@
 		return this;
 	}
 
+	@Override
 	public Map<String, SubmoduleStatus> call() throws GitAPIException {
 		checkCallable();
 
 		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(repo)) {
 			if (!paths.isEmpty())
 				generator.setFilter(PathFilterGroup.createFromStrings(paths));
-			Map<String, SubmoduleStatus> statuses = new HashMap<String, SubmoduleStatus>();
+			Map<String, SubmoduleStatus> statuses = new HashMap<>();
 			while (generator.next()) {
 				SubmoduleStatus status = getStatus(generator);
 				statuses.put(status.getPath(), status);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java
index 088eedc..f97dce9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java
@@ -78,7 +78,7 @@
 	 */
 	public SubmoduleSyncCommand(final Repository repo) {
 		super(repo);
-		paths = new ArrayList<String>();
+		paths = new ArrayList<>();
 	}
 
 	/**
@@ -108,13 +108,14 @@
 			return null;
 	}
 
+	@Override
 	public Map<String, String> call() throws GitAPIException {
 		checkCallable();
 
 		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(repo)) {
 			if (!paths.isEmpty())
 				generator.setFilter(PathFilterGroup.createFromStrings(paths));
-			Map<String, String> synced = new HashMap<String, String>();
+			Map<String, String> synced = new HashMap<>();
 			StoredConfig config = repo.getConfig();
 			while (generator.next()) {
 				String remoteUrl = generator.getRemoteUrl();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
index 342d7f4..29d5d49 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
@@ -94,7 +94,7 @@
 	 */
 	public SubmoduleUpdateCommand(final Repository repo) {
 		super(repo);
-		paths = new ArrayList<String>();
+		paths = new ArrayList<>();
 	}
 
 	/**
@@ -137,6 +137,7 @@
 	 * @throws WrongRepositoryStateException
 	 * @throws GitAPIException
 	 */
+	@Override
 	public Collection<String> call() throws InvalidConfigurationException,
 			NoHeadException, ConcurrentRefUpdateException,
 			CheckoutConflictException, InvalidMergeHeadsException,
@@ -147,7 +148,7 @@
 		try (SubmoduleWalk generator = SubmoduleWalk.forIndex(repo)) {
 			if (!paths.isEmpty())
 				generator.setFilter(PathFilterGroup.createFromStrings(paths));
-			List<String> updated = new ArrayList<String>();
+			List<String> updated = new ArrayList<>();
 			while (generator.next()) {
 				// Skip submodules not registered in .gitmodules file
 				if (generator.getModulesPath() == null)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
index 39dd42c..bdbb862 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
@@ -121,6 +121,7 @@
 	 *             when called on a git repo without a HEAD reference
 	 * @since 2.0
 	 */
+	@Override
 	public Ref call() throws GitAPIException, ConcurrentRefUpdateException,
 			InvalidTagNameException, NoHeadException {
 		checkCallable();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/CheckoutConflictException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/CheckoutConflictException.java
index e317507..7df35c9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/CheckoutConflictException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/CheckoutConflictException.java
@@ -95,7 +95,7 @@
 	 */
 	CheckoutConflictException addConflictingPath(String conflictingPath) {
 		if (conflictingPaths == null)
-			conflictingPaths = new LinkedList<String>();
+			conflictingPaths = new LinkedList<>();
 		conflictingPaths.add(conflictingPath);
 		return this;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
index 7196502..13aeaee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
@@ -65,7 +65,7 @@
 
 	/** Create an empty ignore node with no rules. */
 	public AttributesNode() {
-		rules = new ArrayList<AttributesRule>();
+		rules = new ArrayList<>();
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
index 0532250..c9c69db 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
@@ -71,7 +71,7 @@
 
 	private static List<Attribute> parseAttributes(String attributesLine) {
 		// the C implementation oddly enough allows \r between attributes too.
-		ArrayList<Attribute> result = new ArrayList<Attribute>();
+		ArrayList<Attribute> result = new ArrayList<>();
 		for (String attribute : attributesLine.split(ATTRIBUTES_SPLIT_REGEX)) {
 			attribute = attribute.trim();
 			if (attribute.length() == 0)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
index fa6fe75..4ad58c3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
@@ -51,6 +51,7 @@
 import java.util.Collection;
 import java.util.Collections;
 
+import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.blame.Candidate.BlobCandidate;
 import org.eclipse.jgit.blame.Candidate.ReverseCandidate;
 import org.eclipse.jgit.blame.ReverseWalk.ReverseCommit;
@@ -238,11 +239,13 @@
 	}
 
 	/**
-	 * Obtain the RenameDetector if {@code setFollowFileRenames(true)}.
+	 * Obtain the RenameDetector, allowing the application to configure its
+	 * settings for rename score and breaking behavior.
 	 *
-	 * @return the rename detector, allowing the application to configure its
-	 *         settings for rename score and breaking behavior.
+	 * @return the rename detector, or {@code null} if
+	 *         {@code setFollowFileRenames(false)}.
 	 */
+	@Nullable
 	public RenameDetector getRenameDetector() {
 		return renameDetector;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java
index 39421c6..bd6e5c8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java
@@ -117,7 +117,7 @@
 			if (region.getLengthA() == 1 && region.getLengthB() == 1)
 				return EditList.singleton(region);
 
-			SubsequenceComparator<S> cs = new SubsequenceComparator<S>(cmp);
+			SubsequenceComparator<S> cs = new SubsequenceComparator<>(cmp);
 			Subsequence<S> as = Subsequence.a(a, region);
 			Subsequence<S> bs = Subsequence.b(b, region);
 			EditList e = Subsequence.toBase(diffNonCommon(cs, as, bs), as, bs);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java
index b1cbb91..324b99e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffConfig.java
@@ -55,6 +55,7 @@
 public class DiffConfig {
 	/** Key for {@link Config#get(SectionParser)}. */
 	public static final Config.SectionParser<DiffConfig> KEY = new SectionParser<DiffConfig>() {
+		@Override
 		public DiffConfig parse(final Config cfg) {
 			return new DiffConfig(cfg);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
index 24409ee..e1dfcff 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java
@@ -179,7 +179,7 @@
 		else
 			treeFilterMarker = null;
 
-		List<DiffEntry> r = new ArrayList<DiffEntry>();
+		List<DiffEntry> r = new ArrayList<>();
 		MutableObjectId idBuf = new MutableObjectId();
 		while (walk.next()) {
 			DiffEntry entry = new DiffEntry();
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 @@
 	}
 
 	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/diff/HashedSequencePair.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/HashedSequencePair.java
index 74bbca1..bf6d967 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/HashedSequencePair.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/HashedSequencePair.java
@@ -81,7 +81,7 @@
 
 	/** @return obtain a comparator that uses the cached hash codes. */
 	public HashedSequenceComparator<S> getComparator() {
-		return new HashedSequenceComparator<S>(cmp);
+		return new HashedSequenceComparator<>(cmp);
 	}
 
 	/** @return wrapper around A that includes cached hash codes. */
@@ -103,6 +103,6 @@
 		final int[] hashes = new int[end];
 		for (int ptr = 0; ptr < end; ptr++)
 			hashes[ptr] = cmp.hash(base, ptr);
-		return new HashedSequence<S>(base, hashes);
+		return new HashedSequence<>(base, hashes);
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/HistogramDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/HistogramDiff.java
index 2f5c9ea..4ef5845 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/HistogramDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/HistogramDiff.java
@@ -130,17 +130,18 @@
 		maxChainLength = maxLen;
 	}
 
+	@Override
 	public <S extends Sequence> void diffNonCommon(EditList edits,
 			HashedSequenceComparator<S> cmp, HashedSequence<S> a,
 			HashedSequence<S> b, Edit region) {
-		new State<S>(edits, cmp, a, b).diffRegion(region);
+		new State<>(edits, cmp, a, b).diffRegion(region);
 	}
 
 	private class State<S extends Sequence> {
 		private final HashedSequenceComparator<S> cmp;
 		private final HashedSequence<S> a;
 		private final HashedSequence<S> b;
-		private final List<Edit> queue = new ArrayList<Edit>();
+		private final List<Edit> queue = new ArrayList<>();
 
 		/** Result edits we have determined that must be made to convert a to b. */
 		final EditList edits;
@@ -160,7 +161,7 @@
 		}
 
 		private void diffReplace(Edit r) {
-			Edit lcs = new HistogramDiffIndex<S>(maxChainLength, cmp, a, b, r)
+			Edit lcs = new HistogramDiffIndex<>(maxChainLength, cmp, a, b, r)
 					.findLongestCommonSequence();
 			if (lcs != null) {
 				// If we were given an edit, we can prove a result here.
@@ -213,7 +214,7 @@
 		}
 
 		private SubsequenceComparator<HashedSequence<S>> subcmp() {
-			return new SubsequenceComparator<HashedSequence<S>>(cmp);
+			return new SubsequenceComparator<>(cmp);
 		}
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/LowLevelDiffAlgorithm.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/LowLevelDiffAlgorithm.java
index e3861cd..55ceec8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/LowLevelDiffAlgorithm.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/LowLevelDiffAlgorithm.java
@@ -48,7 +48,7 @@
 	@Override
 	public <S extends Sequence> EditList diffNonCommon(
 			SequenceComparator<? super S> cmp, S a, S b) {
-		HashedSequencePair<S> p = new HashedSequencePair<S>(cmp, a, b);
+		HashedSequencePair<S> p = new HashedSequencePair<>(cmp, a, b);
 		HashedSequenceComparator<S> hc = p.getComparator();
 		HashedSequence<S> ha = p.getA();
 		HashedSequence<S> hb = p.getB();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java
index 9810a6a..e1bda11 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java
@@ -460,6 +460,7 @@
 		}
 
 		class ForwardEditPaths extends EditPaths {
+			@Override
 			final int snake(int k, int x) {
 				for (; x < endA && k + x < endB; x++)
 					if (!cmp.equals(a, x, b, k + x))
@@ -467,18 +468,22 @@
 				return x;
 			}
 
+			@Override
 			final int getLeft(final int x) {
 				return x;
 			}
 
+			@Override
 			final int getRight(final int x) {
 				return x + 1;
 			}
 
+			@Override
 			final boolean isBetter(final int left, final int right) {
 				return left > right;
 			}
 
+			@Override
 			final void adjustMinMaxK(final int k, final int x) {
 				if (x >= endA || k + x >= endB) {
 					if (k > backward.middleK)
@@ -488,6 +493,7 @@
 				}
 			}
 
+			@Override
 			final boolean meets(int d, int k, int x, long snake) {
 				if (k < backward.beginK || k > backward.endK)
 					return false;
@@ -502,6 +508,7 @@
 		}
 
 		class BackwardEditPaths extends EditPaths {
+			@Override
 			final int snake(int k, int x) {
 				for (; x > beginA && k + x > beginB; x--)
 					if (!cmp.equals(a, x - 1, b, k + x - 1))
@@ -509,18 +516,22 @@
 				return x;
 			}
 
+			@Override
 			final int getLeft(final int x) {
 				return x - 1;
 			}
 
+			@Override
 			final int getRight(final int x) {
 				return x;
 			}
 
+			@Override
 			final boolean isBetter(final int left, final int right) {
 				return left < right;
 			}
 
+			@Override
 			final void adjustMinMaxK(final int k, final int x) {
 				if (x <= beginA || k + x <= beginB) {
 					if (k > forward.middleK)
@@ -530,6 +541,7 @@
 				}
 			}
 
+			@Override
 			final boolean meets(int d, int k, int x, long snake) {
 				if (k < forward.beginK || k > forward.endK)
 					return false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
index b26e1bc..5bfee75 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RawText.java
@@ -107,6 +107,7 @@
 	}
 
 	/** @return total number of items in the sequence. */
+	@Override
 	public int size() {
 		// The line map is always 2 entries larger than the number of lines in
 		// the file. Index 0 is padded out/unused. The last index is the total
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
index 8865b62..bc52473 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
@@ -70,6 +70,7 @@
 	private static final int EXACT_RENAME_SCORE = 100;
 
 	private static final Comparator<DiffEntry> DIFF_COMPARATOR = new Comparator<DiffEntry>() {
+		@Override
 		public int compare(DiffEntry a, DiffEntry b) {
 			int cmp = nameOf(a).compareTo(nameOf(b));
 			if (cmp == 0)
@@ -392,15 +393,15 @@
 
 	/** Reset this rename detector for another rename detection pass. */
 	public void reset() {
-		entries = new ArrayList<DiffEntry>();
-		deleted = new ArrayList<DiffEntry>();
-		added = new ArrayList<DiffEntry>();
+		entries = new ArrayList<>();
+		deleted = new ArrayList<>();
+		added = new ArrayList<>();
 		done = false;
 	}
 
 	private void breakModifies(ContentSource.Pair reader, ProgressMonitor pm)
 			throws IOException {
-		ArrayList<DiffEntry> newEntries = new ArrayList<DiffEntry>(entries.size());
+		ArrayList<DiffEntry> newEntries = new ArrayList<>(entries.size());
 
 		pm.beginTask(JGitText.get().renamesBreakingModifies, entries.size());
 
@@ -427,8 +428,8 @@
 	}
 
 	private void rejoinModifies(ProgressMonitor pm) {
-		HashMap<String, DiffEntry> nameMap = new HashMap<String, DiffEntry>();
-		ArrayList<DiffEntry> newAdded = new ArrayList<DiffEntry>(added.size());
+		HashMap<String, DiffEntry> nameMap = new HashMap<>();
+		ArrayList<DiffEntry> newAdded = new ArrayList<>(added.size());
 
 		pm.beginTask(JGitText.get().renamesRejoiningModifies, added.size()
 				+ deleted.size());
@@ -455,7 +456,7 @@
 		}
 
 		added = newAdded;
-		deleted = new ArrayList<DiffEntry>(nameMap.values());
+		deleted = new ArrayList<>(nameMap.values());
 	}
 
 	private int calculateModifyScore(ContentSource.Pair reader, DiffEntry d)
@@ -507,8 +508,8 @@
 		HashMap<AbbreviatedObjectId, Object> deletedMap = populateMap(deleted, pm);
 		HashMap<AbbreviatedObjectId, Object> addedMap = populateMap(added, pm);
 
-		ArrayList<DiffEntry> uniqueAdds = new ArrayList<DiffEntry>(added.size());
-		ArrayList<List<DiffEntry>> nonUniqueAdds = new ArrayList<List<DiffEntry>>();
+		ArrayList<DiffEntry> uniqueAdds = new ArrayList<>(added.size());
+		ArrayList<List<DiffEntry>> nonUniqueAdds = new ArrayList<>();
 
 		for (Object o : addedMap.values()) {
 			if (o instanceof DiffEntry)
@@ -517,7 +518,7 @@
 				nonUniqueAdds.add((List<DiffEntry>) o);
 		}
 
-		ArrayList<DiffEntry> left = new ArrayList<DiffEntry>(added.size());
+		ArrayList<DiffEntry> left = new ArrayList<>(added.size());
 
 		for (DiffEntry a : uniqueAdds) {
 			Object del = deletedMap.get(a.newId);
@@ -626,7 +627,7 @@
 		}
 		added = left;
 
-		deleted = new ArrayList<DiffEntry>(deletedMap.size());
+		deleted = new ArrayList<>(deletedMap.size());
 		for (Object o : deletedMap.values()) {
 			if (o instanceof DiffEntry) {
 				DiffEntry e = (DiffEntry) o;
@@ -676,11 +677,11 @@
 	@SuppressWarnings("unchecked")
 	private HashMap<AbbreviatedObjectId, Object> populateMap(
 			List<DiffEntry> diffEntries, ProgressMonitor pm) {
-		HashMap<AbbreviatedObjectId, Object> map = new HashMap<AbbreviatedObjectId, Object>();
+		HashMap<AbbreviatedObjectId, Object> map = new HashMap<>();
 		for (DiffEntry de : diffEntries) {
 			Object old = map.put(id(de), de);
 			if (old instanceof DiffEntry) {
-				ArrayList<DiffEntry> list = new ArrayList<DiffEntry>(2);
+				ArrayList<DiffEntry> list = new ArrayList<>(2);
 				list.add((DiffEntry) old);
 				list.add(de);
 				map.put(id(de), list);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
index 6088d72..653658b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityRenameDetector.java
@@ -136,7 +136,7 @@
 				2 * srcs.size() * dsts.size());
 
 		int mNext = buildMatrix(pm);
-		out = new ArrayList<DiffEntry>(Math.min(mNext, dsts.size()));
+		out = new ArrayList<>(Math.min(mNext, dsts.size()));
 
 		// Match rename pairs on a first come, first serve basis until
 		// we have looked at everything that is above our minimum score.
@@ -192,7 +192,7 @@
 	}
 
 	private static List<DiffEntry> compactSrcList(List<DiffEntry> in) {
-		ArrayList<DiffEntry> r = new ArrayList<DiffEntry>(in.size());
+		ArrayList<DiffEntry> r = new ArrayList<>(in.size());
 		for (DiffEntry e : in) {
 			if (e.changeType == ChangeType.DELETE)
 				r.add(e);
@@ -201,7 +201,7 @@
 	}
 
 	private static List<DiffEntry> compactDstList(List<DiffEntry> in) {
-		ArrayList<DiffEntry> r = new ArrayList<DiffEntry>(in.size());
+		ArrayList<DiffEntry> r = new ArrayList<>(in.size());
 		for (DiffEntry e : in) {
 			if (e != null)
 				r.add(e);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/Subsequence.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/Subsequence.java
index 017fe69..50ca613 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/Subsequence.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/Subsequence.java
@@ -66,7 +66,7 @@
 	 * @return subsequence of {@code base} as described by A in {@code region}.
 	 */
 	public static <S extends Sequence> Subsequence<S> a(S a, Edit region) {
-		return new Subsequence<S>(a, region.beginA, region.endA);
+		return new Subsequence<>(a, region.beginA, region.endA);
 	}
 
 	/**
@@ -81,7 +81,7 @@
 	 * @return subsequence of {@code base} as described by B in {@code region}.
 	 */
 	public static <S extends Sequence> Subsequence<S> b(S b, Edit region) {
-		return new Subsequence<S>(b, region.beginB, region.endB);
+		return new Subsequence<>(b, region.beginB, region.endB);
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
index b0cf8be..ce52fed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
@@ -111,6 +111,7 @@
 	private static final byte[] NO_CHECKSUM = {};
 
 	static final Comparator<DirCacheEntry> ENT_CMP = new Comparator<DirCacheEntry>() {
+		@Override
 		public int compare(final DirCacheEntry o1, final DirCacheEntry o2) {
 			final int cr = cmp(o1, o2);
 			if (cr != 0)
@@ -992,7 +993,7 @@
 	 * @throws IOException
 	 */
 	private void updateSmudgedEntries() throws IOException {
-		List<String> paths = new ArrayList<String>(128);
+		List<String> paths = new ArrayList<>(128);
 		try (TreeWalk walk = new TreeWalk(repository)) {
 			walk.setOperationType(OperationType.CHECKIN_OP);
 			for (int i = 0; i < entryCnt; i++)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
index cfebe2d..676a6ab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
@@ -218,6 +218,7 @@
 		return e;
 	}
 
+	@Override
 	public void finish() {
 		if (!sorted)
 			resort();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index c318443..84f0da9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -127,11 +127,11 @@
 
 	private Repository repo;
 
-	private HashMap<String, CheckoutMetadata> updated = new HashMap<String, CheckoutMetadata>();
+	private HashMap<String, CheckoutMetadata> updated = new HashMap<>();
 
-	private ArrayList<String> conflicts = new ArrayList<String>();
+	private ArrayList<String> conflicts = new ArrayList<>();
 
-	private ArrayList<String> removed = new ArrayList<String>();
+	private ArrayList<String> removed = new ArrayList<>();
 
 	private ObjectId mergeCommitTree;
 
@@ -147,7 +147,7 @@
 
 	private boolean failOnConflict = true;
 
-	private ArrayList<String> toBeDeleted = new ArrayList<String>();
+	private ArrayList<String> toBeDeleted = new ArrayList<>();
 
 	private boolean emptyDirCache;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
index c987c96..22bedcf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
@@ -74,6 +74,7 @@
  */
 public class DirCacheEditor extends BaseDirCacheEditor {
 	private static final Comparator<PathEdit> EDIT_CMP = new Comparator<PathEdit>() {
+		@Override
 		public int compare(final PathEdit o1, final PathEdit o2) {
 			final byte[] a = o1.path;
 			final byte[] b = o2.path;
@@ -95,7 +96,7 @@
 	 */
 	protected DirCacheEditor(final DirCache dc, final int ecnt) {
 		super(dc, ecnt);
-		edits = new ArrayList<PathEdit>();
+		edits = new ArrayList<>();
 	}
 
 	/**
@@ -123,6 +124,7 @@
 		return super.commit();
 	}
 
+	@Override
 	public void finish() {
 		if (!edits.isEmpty()) {
 			applyEdits();
@@ -383,6 +385,7 @@
 			super(ent);
 		}
 
+		@Override
 		public void apply(final DirCacheEntry ent) {
 			throw new UnsupportedOperationException(JGitText.get().noApplyInDelete);
 		}
@@ -432,6 +435,7 @@
 			return path;
 		}
 
+		@Override
 		public void apply(final DirCacheEntry ent) {
 			throw new UnsupportedOperationException(JGitText.get().noApplyInDelete);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
index 8bcf4bf..a06f9d3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
@@ -80,6 +80,7 @@
 	private static final DirCacheTree[] NO_CHILDREN = {};
 
 	private static final Comparator<DirCacheTree> TREE_CMP = new Comparator<DirCacheTree>() {
+		@Override
 		public int compare(final DirCacheTree o1, final DirCacheTree o2) {
 			final byte[] a = o1.encodedName;
 			final byte[] b = o2.encodedName;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/CancelledException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/CancelledException.java
new file mode 100644
index 0000000..c2833a1
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/CancelledException.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 Ericsson
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.errors;
+
+import java.io.IOException;
+
+/**
+ * Thrown when an operation was canceled
+ *
+ * @since 4.7
+ */
+public class CancelledException extends IOException {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * @param message
+	 */
+	public CancelledException(String message) {
+		super(message);
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/CompoundException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/CompoundException.java
index 55b64ee..3a7b2c6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/CompoundException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/CompoundException.java
@@ -75,7 +75,7 @@
 	 */
 	public CompoundException(final Collection<Throwable> why) {
 		super(format(why));
-		causeList = Collections.unmodifiableList(new ArrayList<Throwable>(why));
+		causeList = Collections.unmodifiableList(new ArrayList<>(why));
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java
index 6ac4b0f..12ef533 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/events/ListenerList.java
@@ -50,7 +50,7 @@
 
 /** Manages a thread-safe list of {@link RepositoryListener}s. */
 public class ListenerList {
-	private final ConcurrentMap<Class<? extends RepositoryListener>, CopyOnWriteArrayList<ListenerHandle>> lists = new ConcurrentHashMap<Class<? extends RepositoryListener>, CopyOnWriteArrayList<ListenerHandle>>();
+	private final ConcurrentMap<Class<? extends RepositoryListener>, CopyOnWriteArrayList<ListenerHandle>> lists = new ConcurrentHashMap<>();
 
 	/**
 	 * Register an IndexChangedListener.
@@ -126,7 +126,7 @@
 		if (list == null) {
 			CopyOnWriteArrayList<ListenerHandle> newList;
 
-			newList = new CopyOnWriteArrayList<ListenerHandle>();
+			newList = new CopyOnWriteArrayList<>();
 			list = lists.putIfAbsent(handle.type, newList);
 			if (list == null)
 				list = newList;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/AbstractHead.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/AbstractHead.java
index 0aa0075..10c84c4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/AbstractHead.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/AbstractHead.java
@@ -70,6 +70,7 @@
 		this.newHeads = newHeads;
 	}
 
+	@Override
 	public List<Head> getNextHeads(char c) {
 		if (matches(c))
 			return newHeads;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java
index f9c2394..856d74e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/FileNameMatcher.java
@@ -120,9 +120,9 @@
 	private FileNameMatcher(final List<Head> headsStartValue,
 			final List<Head> heads) {
 		this.headsStartValue = headsStartValue;
-		this.heads = new ArrayList<Head>(heads.size());
+		this.heads = new ArrayList<>(heads.size());
 		this.heads.addAll(heads);
-		this.listForLocalUseage = new ArrayList<Head>(heads.size());
+		this.listForLocalUseage = new ArrayList<>(heads.size());
 	}
 
 	/**
@@ -158,7 +158,7 @@
 		final List<AbstractHead> allHeads = parseHeads(patternString,
 				invalidWildgetCharacter);
 
-		List<Head> nextHeadsSuggestion = new ArrayList<Head>(2);
+		List<Head> nextHeadsSuggestion = new ArrayList<>(2);
 		nextHeadsSuggestion.add(LastHead.INSTANCE);
 		for (int i = allHeads.size() - 1; i >= 0; i--) {
 			final AbstractHead head = allHeads.get(i);
@@ -172,7 +172,7 @@
 				head.setNewHeads(nextHeadsSuggestion);
 			} else {
 				head.setNewHeads(nextHeadsSuggestion);
-				nextHeadsSuggestion = new ArrayList<Head>(2);
+				nextHeadsSuggestion = new ArrayList<>(2);
 				nextHeadsSuggestion.add(head);
 			}
 		}
@@ -236,7 +236,7 @@
 			throws InvalidPatternException {
 
 		int currentIndex = 0;
-		List<AbstractHead> heads = new ArrayList<AbstractHead>();
+		List<AbstractHead> heads = new ArrayList<>();
 		while (currentIndex < pattern.length()) {
 			final int groupStart = indexOfUnescaped(pattern, '[', currentIndex);
 			if (groupStart == -1) {
@@ -262,7 +262,7 @@
 
 	private static List<AbstractHead> createSimpleHeads(
 			final String patternPart, final Character invalidWildgetCharacter) {
-		final List<AbstractHead> heads = new ArrayList<AbstractHead>(
+		final List<AbstractHead> heads = new ArrayList<>(
 				patternPart.length());
 
 		boolean escaped = false;
@@ -375,7 +375,7 @@
 	 *         reset and start point.
 	 */
 	public FileNameMatcher createMatcherForSuffix() {
-		final List<Head> copyOfHeads = new ArrayList<Head>(heads.size());
+		final List<Head> copyOfHeads = new ArrayList<>(heads.size());
 		copyOfHeads.addAll(heads);
 		return new FileNameMatcher(copyOfHeads);
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/GroupHead.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/GroupHead.java
index 8af5228..5c18756 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/GroupHead.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/GroupHead.java
@@ -64,7 +64,7 @@
 	GroupHead(String pattern, final String wholePattern)
 			throws InvalidPatternException {
 		super(false);
-		this.characterClasses = new ArrayList<CharacterPattern>();
+		this.characterClasses = new ArrayList<>();
 		this.inverse = pattern.startsWith("!"); //$NON-NLS-1$
 		if (inverse) {
 			pattern = pattern.substring(1);
@@ -159,6 +159,7 @@
 			this.end = end;
 		}
 
+		@Override
 		public final boolean matches(char c) {
 			return start <= c && c <= end;
 		}
@@ -167,6 +168,7 @@
 	private static final class DigitPattern implements CharacterPattern {
 		static final GroupHead.DigitPattern INSTANCE = new DigitPattern();
 
+		@Override
 		public final boolean matches(char c) {
 			return Character.isDigit(c);
 		}
@@ -175,6 +177,7 @@
 	private static final class LetterPattern implements CharacterPattern {
 		static final GroupHead.LetterPattern INSTANCE = new LetterPattern();
 
+		@Override
 		public final boolean matches(char c) {
 			return Character.isLetter(c);
 		}
@@ -183,6 +186,7 @@
 	private static final class LowerPattern implements CharacterPattern {
 		static final GroupHead.LowerPattern INSTANCE = new LowerPattern();
 
+		@Override
 		public final boolean matches(char c) {
 			return Character.isLowerCase(c);
 		}
@@ -191,6 +195,7 @@
 	private static final class UpperPattern implements CharacterPattern {
 		static final GroupHead.UpperPattern INSTANCE = new UpperPattern();
 
+		@Override
 		public final boolean matches(char c) {
 			return Character.isUpperCase(c);
 		}
@@ -199,6 +204,7 @@
 	private static final class WhitespacePattern implements CharacterPattern {
 		static final GroupHead.WhitespacePattern INSTANCE = new WhitespacePattern();
 
+		@Override
 		public final boolean matches(char c) {
 			return Character.isWhitespace(c);
 		}
@@ -211,6 +217,7 @@
 			this.expectedCharacter = c;
 		}
 
+		@Override
 		public final boolean matches(char c) {
 			return this.expectedCharacter == c;
 		}
@@ -221,6 +228,7 @@
 
 		private static String punctCharacters = "-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"; //$NON-NLS-1$
 
+		@Override
 		public boolean matches(char c) {
 			return punctCharacters.indexOf(c) != -1;
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/LastHead.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/LastHead.java
index 78a61b9..f9ddd9e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/LastHead.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/LastHead.java
@@ -56,6 +56,7 @@
 		// defined because of javadoc and visibility modifier.
 	}
 
+	@Override
 	public List<Head> getNextHeads(char c) {
 		return FileNameMatcher.EMPTY_HEAD_LIST;
 	}
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..94c8e43 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;
@@ -77,7 +78,7 @@
  */
 public class ManifestParser extends DefaultHandler {
 	private final String filename;
-	private final String baseUrl;
+	private final URI baseUrl;
 	private final String defaultBranch;
 	private final Repository rootRepo;
 	private final Map<String, Remote> remotes;
@@ -124,15 +125,10 @@
 		this.filename = filename;
 		this.defaultBranch = defaultBranch;
 		this.rootRepo = rootRepo;
+		this.baseUrl = normalizeEmptyPath(URI.create(baseUrl));
 
-		// Strip trailing /s to match repo behavior.
-		int lastIndex = baseUrl.length() - 1;
-		while (lastIndex >= 0 && baseUrl.charAt(lastIndex) == '/')
-			lastIndex--;
-		this.baseUrl = baseUrl.substring(0, lastIndex + 1);
-
-		plusGroups = new HashSet<String>();
-		minusGroups = new HashSet<String>();
+		plusGroups = new HashSet<>();
+		minusGroups = new HashSet<>();
 		if (groups == null || groups.length() == 0
 				|| groups.equals("default")) { //$NON-NLS-1$
 			// default means "all,-notdefault"
@@ -146,9 +142,9 @@
 			}
 		}
 
-		remotes = new HashMap<String, Remote>();
-		projects = new ArrayList<RepoProject>();
-		filteredProjects = new ArrayList<RepoProject>();
+		remotes = new HashMap<>();
+		projects = new ArrayList<>();
+		filteredProjects = new ArrayList<>();
 	}
 
 	/**
@@ -257,13 +253,7 @@
 			return;
 
 		// Only do the following after we finished reading everything.
-		Map<String, String> remoteUrls = new HashMap<String, String>();
-		URI baseUri;
-		try {
-			baseUri = new URI(baseUrl);
-		} catch (URISyntaxException e) {
-			throw new SAXException(e);
-		}
+		Map<String, URI> remoteUrls = new HashMap<>();
 		if (defaultRevision == null && defaultRemote != null) {
 			Remote remote = remotes.get(defaultRemote);
 			if (remote != null) {
@@ -293,16 +283,18 @@
 					revision = r.revision;
 				}
 			}
-			String remoteUrl = remoteUrls.get(remote);
+			URI remoteUrl = remoteUrls.get(remote);
 			if (remoteUrl == null) {
-				remoteUrl =
-						baseUri.resolve(remotes.get(remote).fetch).toString();
-				if (!remoteUrl.endsWith("/")) //$NON-NLS-1$
-					remoteUrl = remoteUrl + "/"; //$NON-NLS-1$
+				String fetch = remotes.get(remote).fetch;
+				if (fetch == null) {
+					throw new SAXException(MessageFormat
+							.format(RepoText.get().errorNoFetch, remote));
+				}
+				remoteUrl = normalizeEmptyPath(baseUrl.resolve(fetch));
 				remoteUrls.put(remote, remoteUrl);
 			}
-			proj.setUrl(remoteUrl + proj.getName())
-					.setDefaultRevision(revision);
+			proj.setUrl(remoteUrl.resolve(proj.getName()).toString())
+				.setDefaultRevision(revision);
 		}
 
 		filteredProjects.addAll(projects);
@@ -310,6 +302,23 @@
 		removeOverlaps();
 	}
 
+	static URI normalizeEmptyPath(URI u) {
+		// URI.create("scheme://host").resolve("a/b") => "scheme://hosta/b"
+		// That seems like bug https://bugs.openjdk.java.net/browse/JDK-4666701.
+		// We workaround this by special casing the empty path case.
+		if (u.getHost() != null && !u.getHost().isEmpty() &&
+			(u.getPath() == null || u.getPath().isEmpty())) {
+			try {
+				return new URI(u.getScheme(),
+					u.getUserInfo(), u.getHost(), u.getPort(),
+						"/", u.getQuery(), u.getFragment()); //$NON-NLS-1$
+			} catch (URISyntaxException x) {
+				throw new IllegalArgumentException(x.getMessage(), x);
+			}
+		}
+		return u;
+	}
+
 	/**
 	 * Getter for projects.
 	 *
@@ -324,7 +333,7 @@
 	 *
 	 * @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/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index 86dbabc..e105dc0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -96,8 +96,8 @@
  * If called against a bare repository, it will replace all the existing content
  * of the repository with the contents populated from the manifest.
  *
- * repo manifest allows projects overlapping, e.g. one project's path is
- * &quot;foo&quot; and another project's path is &quot;foo/bar&quot;. This won't
+ * repo manifest allows projects overlapping, e.g. one project's manifestPath is
+ * &quot;foo&quot; and another project's manifestPath is &quot;foo/bar&quot;. This won't
  * work in git submodule, so we'll skip all the sub projects
  * (&quot;foo/bar&quot; in the example) while converting.
  *
@@ -105,7 +105,7 @@
  * @since 3.4
  */
 public class RepoCommand extends GitCommand<RevCommit> {
-	private String path;
+	private String manifestPath;
 	private String uri;
 	private String groupsParam;
 	private String branch;
@@ -168,6 +168,7 @@
 
 	/** A default implementation of {@link RemoteReader} callback. */
 	public static class DefaultRemoteReader implements RemoteReader {
+		@Override
 		public ObjectId sha1(String uri, String ref) throws GitAPIException {
 			Map<String, Ref> map = Git
 					.lsRemoteRepository()
@@ -177,13 +178,13 @@
 			return r != null ? r.getObjectId() : null;
 		}
 
+		@Override
 		public byte[] readFile(String uri, String ref, String path)
 				throws GitAPIException, IOException {
 			File dir = FileUtils.createTempDir("jgit_", ".git", null); //$NON-NLS-1$ //$NON-NLS-2$
 			try (Git git = Git.cloneRepository().setBare(true).setDirectory(dir)
-					.setURI(uri).call();
-					Repository repo = git.getRepository()) {
-				return readFileFromRepo(repo, ref, path);
+					.setURI(uri).call()) {
+				return readFileFromRepo(git.getRepository(), ref, path);
 			} finally {
 				FileUtils.delete(dir, FileUtils.RECURSIVE);
 			}
@@ -243,7 +244,7 @@
 	 * @return this command
 	 */
 	public RepoCommand setPath(String path) {
-		this.path = path;
+		this.manifestPath = path;
 		return this;
 	}
 
@@ -263,7 +264,11 @@
 	}
 
 	/**
-	 * Set base URI of the pathes inside the XML
+	 * Set base URI of the paths inside the XML. This is typically the name of
+	 * the directory holding the manifest repository, eg. for
+	 * https://android.googlesource.com/platform/manifest, this should be
+	 * /platform (if you would run this on android.googlesource.com)
+	 * or https://android.googlesource.com/platform elsewhere.
 	 *
 	 * @param uri
 	 * @return this command
@@ -447,15 +452,16 @@
 	public RevCommit call() throws GitAPIException {
 		try {
 			checkCallable();
-			if (uri == null || uri.length() == 0)
+			if (uri == null || uri.length() == 0) {
 				throw new IllegalArgumentException(
-						JGitText.get().uriNotConfigured);
+					JGitText.get().uriNotConfigured);
+			}
 			if (inputStream == null) {
-				if (path == null || path.length() == 0)
+				if (manifestPath == null || manifestPath.length() == 0)
 					throw new IllegalArgumentException(
 							JGitText.get().pathNotConfigured);
 				try {
-					inputStream = new FileInputStream(path);
+					inputStream = new FileInputStream(manifestPath);
 				} catch (IOException e) {
 					throw new IllegalArgumentException(
 							JGitText.get().pathNotConfigured);
@@ -463,7 +469,7 @@
 			}
 
 			if (repo.isBare()) {
-				bareProjects = new ArrayList<RepoProject>();
+				bareProjects = new ArrayList<>();
 				if (author == null)
 					author = new PersonIdent(repo);
 				if (callback == null)
@@ -472,7 +478,7 @@
 				git = new Git(repo);
 
 			ManifestParser parser = new ManifestParser(
-					includedReader, path, branch, uri, groupsParam, repo);
+					includedReader, manifestPath, branch, uri, groupsParam, repo);
 			try {
 				parser.read(inputStream);
 				for (RepoProject proj : parser.getFilteredProjects()) {
@@ -503,7 +509,7 @@
 				Config cfg = new Config();
 				StringBuilder attributes = new StringBuilder();
 				for (RepoProject proj : bareProjects) {
-					String name = proj.getPath();
+					String path = proj.getPath();
 					String nameUri = proj.getName();
 					ObjectId objectId;
 					if (ObjectId.isId(proj.getRevision())
@@ -519,7 +525,7 @@
 						}
 						if (recordRemoteBranch) {
 							// can be branch or tag
-							cfg.setString("submodule", name, "branch", //$NON-NLS-1$ //$NON-NLS-2$
+							cfg.setString("submodule", path, "branch", //$NON-NLS-1$ //$NON-NLS-2$
 									proj.getRevision());
 						}
 
@@ -529,14 +535,14 @@
 							// depth in the 'clone-depth' field, while
 							// git core only uses a binary 'shallow = true/false'
 							// hint, we'll map any depth to 'shallow = true'
-							cfg.setBoolean("submodule", name, "shallow", //$NON-NLS-1$ //$NON-NLS-2$
+							cfg.setBoolean("submodule", path, "shallow", //$NON-NLS-1$ //$NON-NLS-2$
 									true);
 						}
 					}
 					if (recordSubmoduleLabels) {
 						StringBuilder rec = new StringBuilder();
 						rec.append("/"); //$NON-NLS-1$
-						rec.append(name);
+						rec.append(path);
 						for (String group : proj.getGroups()) {
 							rec.append(" "); //$NON-NLS-1$
 							rec.append(group);
@@ -544,11 +550,11 @@
 						rec.append("\n"); //$NON-NLS-1$
 						attributes.append(rec.toString());
 					}
-					cfg.setString("submodule", name, "path", name); //$NON-NLS-1$ //$NON-NLS-2$
-					cfg.setString("submodule", name, "url", nameUri); //$NON-NLS-1$ //$NON-NLS-2$
+					cfg.setString("submodule", path, "path", path); //$NON-NLS-1$ //$NON-NLS-2$
+					cfg.setString("submodule", path, "url", nameUri); //$NON-NLS-1$ //$NON-NLS-2$
 
 					// create gitlink
-					DirCacheEntry dcEntry = new DirCacheEntry(name);
+					DirCacheEntry dcEntry = new DirCacheEntry(path);
 					dcEntry.setObjectId(objectId);
 					dcEntry.setFileMode(FileMode.GITLINK);
 					builder.add(dcEntry);
@@ -635,17 +641,17 @@
 		}
 	}
 
-	private void addSubmodule(String url, String name, String revision,
+	private void addSubmodule(String url, String path, String revision,
 			List<CopyFile> copyfiles, Set<String> groups, String recommendShallow)
 			throws GitAPIException, IOException {
 		if (repo.isBare()) {
-			RepoProject proj = new RepoProject(url, name, revision, null, groups, recommendShallow);
+			RepoProject proj = new RepoProject(url, path, revision, null, groups, recommendShallow);
 			proj.addCopyFiles(copyfiles);
 			bareProjects.add(proj);
 		} else {
 			SubmoduleAddCommand add = git
 				.submoduleAdd()
-				.setPath(name)
+				.setPath(path)
 				.setURI(url);
 			if (monitor != null)
 				add.setProgressMonitor(monitor);
@@ -657,7 +663,7 @@
 							.call();
 				}
 				subRepo.close();
-				git.add().addFilepattern(name).call();
+				git.add().addFilepattern(path).call();
 			}
 			for (CopyFile copyfile : copyfiles) {
 				copyfile.copy();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
index ff4a3ed..700cf11 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
@@ -155,7 +155,7 @@
 		this.remote = remote;
 		this.groups = groups;
 		this.recommendShallow = recommendShallow;
-		copyfiles = new ArrayList<CopyFile>();
+		copyfiles = new ArrayList<>();
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/internal/RepoText.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/internal/RepoText.java
index 36b6e3a..02a2565 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/internal/RepoText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/internal/RepoText.java
@@ -64,6 +64,7 @@
 	/***/ public String errorIncludeNotImplemented;
 	/***/ public String errorNoDefault;
 	/***/ public String errorNoDefaultFilename;
+	/***/ public String errorNoFetch;
 	/***/ public String errorParsingManifestFile;
 	/***/ public String errorRemoteUnavailable;
 	/***/ public String invalidManifest;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
index ad2eeb0..c1aca6a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
@@ -95,6 +95,7 @@
 	 *             If the hook has been run and a returned an exit code
 	 *             different from zero.
 	 */
+	@Override
 	public abstract T call() throws IOException, AbortedByHookException;
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
index 8b1244e..6314c63 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
@@ -83,7 +83,7 @@
 
 	/** Create an empty ignore node with no rules. */
 	public IgnoreNode() {
-		rules = new ArrayList<FastIgnoreRule>();
+		rules = new ArrayList<>();
 	}
 
 	/**
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..61f7b83 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 {
 
@@ -53,10 +51,12 @@
 	 * Matcher that does not match any pattern.
 	 */
 	public static final IMatcher NO_MATCH = new IMatcher() {
+		@Override
 		public boolean matches(String path, boolean assumeDirectory) {
 			return false;
 		}
 
+		@Override
 		public boolean matches(String segment, int startIncl, int endExcl,
 				boolean assumeDirectory) {
 			return false;
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..cc0fe93 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 {
 
@@ -57,6 +55,7 @@
 					"Pattern must have leading asterisk: " + pattern); //$NON-NLS-1$
 	}
 
+	@Override
 	public boolean matches(String segment, int startIncl, int endExcl,
 			boolean assumeDirectory) {
 		// faster local access, same as in string.indexOf()
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..0065123 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 {
 
@@ -72,6 +70,7 @@
 			this.subPattern = pattern.substring(1);
 	}
 
+	@Override
 	public boolean matches(String path, boolean assumeDirectory) {
 		int end = 0;
 		int firstChar = 0;
@@ -88,6 +87,7 @@
 		return false;
 	}
 
+	@Override
 	public boolean matches(String segment, int startIncl, int endExcl,
 			boolean assumeDirectory) {
 		// faster local access, same as in string.indexOf()
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..65224ea 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 {
 
@@ -92,7 +90,7 @@
 	static private List<IMatcher> createMatchers(List<String> segments,
 			Character pathSeparator, boolean dirOnly)
 			throws InvalidPatternException {
-		List<IMatcher> matchers = new ArrayList<IMatcher>(segments.size());
+		List<IMatcher> matchers = new ArrayList<>(segments.size());
 		for (int i = 0; i < segments.size(); i++) {
 			String segment = segments.get(i);
 			IMatcher matcher = createNameMatcher0(segment, pathSeparator,
@@ -172,6 +170,7 @@
 		}
 	}
 
+	@Override
 	public boolean matches(String path, boolean assumeDirectory) {
 		if (matchers == null)
 			return simpleMatch(path, assumeDirectory);
@@ -211,6 +210,7 @@
 		return false;
 	}
 
+	@Override
 	public boolean matches(String segment, int startIncl, int endExcl,
 			boolean assumeDirectory) {
 		throw new UnsupportedOperationException(
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..da482fa 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 {
 
@@ -150,7 +148,7 @@
 		if (count < 1)
 			throw new IllegalStateException(
 					"Pattern must have at least two segments: " + pattern); //$NON-NLS-1$
-		List<String> segments = new ArrayList<String>(count);
+		List<String> segments = new ArrayList<>(count);
 		int right = 0;
 		while (true) {
 			int left = right;
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..2e148f4 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 {
 
@@ -57,6 +55,7 @@
 					"Pattern must have trailing asterisk: " + pattern); //$NON-NLS-1$
 	}
 
+	@Override
 	public boolean matches(String segment, int startIncl, int endExcl,
 			boolean assumeDirectory) {
 		// faster local access, same as in string.indexOf()
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..93ea13c 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 {
 
@@ -63,10 +61,12 @@
 		super(WILDMATCH, false);
 	}
 
+	@Override
 	public final boolean matches(String path, boolean assumeDirectory) {
 		return true;
 	}
 
+	@Override
 	public final boolean matches(String segment, int startIncl, int endExcl,
 			boolean assumeDirectory) {
 		return true;
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..32bfb7e 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 String badObjectType;
 	/***/ public String badRef;
 	/***/ public String badSectionEntry;
+	/***/ public String badShallowLine;
 	/***/ public String bareRepositoryNoWorkdirAndIndex;
 	/***/ public String base64InputNotProperlyPadded;
 	/***/ public String baseLengthIncorrect;
@@ -357,6 +358,8 @@
 	/***/ public String flagsAlreadyCreated;
 	/***/ public String funnyRefname;
 	/***/ public String gcFailed;
+	/***/ public String gcLogExists;
+	/***/ public String gcTooManyUnpruned;
 	/***/ public String gitmodulesNotFound;
 	/***/ public String headRequiredToStash;
 	/***/ public String hoursAgo;
@@ -622,6 +625,7 @@
 	/***/ public String sequenceTooLargeForDiffAlgorithm;
 	/***/ public String serviceNotEnabledNoName;
 	/***/ public String serviceNotPermitted;
+	/***/ public String sha1CollisionDetected1;
 	/***/ public String shallowCommitsAlreadyInitialized;
 	/***/ public String shallowPacksRequireDepthWalk;
 	/***/ public String shortCompressedStreamAt;
@@ -668,6 +672,7 @@
 	/***/ public String theFactoryMustNotBeNull;
 	/***/ public String timeIsUncertain;
 	/***/ public String timerAlreadyTerminated;
+	/***/ public String tooManyCommands;
 	/***/ public String tooManyIncludeRecursions;
 	/***/ public String topologicalSortRequired;
 	/***/ public String transportExceptionBadRef;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java
index 35327ea..757c19a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/LagCheck.java
@@ -86,6 +86,7 @@
 		rw.setRetainBody(false);
 	}
 
+	@Override
 	public void close() {
 		if (rw != null) {
 			rw.close();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ProposalRound.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ProposalRound.java
index ddd7059..1eaa33d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ProposalRound.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/ProposalRound.java
@@ -122,6 +122,7 @@
 		return a == null && b == null;
 	}
 
+	@Override
 	void start() throws IOException {
 		for (Proposal p : todo) {
 			p.notifyState(RUNNING);
@@ -268,6 +269,7 @@
 		}
 	}
 
+	@Override
 	void success() {
 		for (Proposal p : todo) {
 			p.success();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java
index 396fbdd..a0ac608 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/ketch/RemoteGitReplica.java
@@ -240,6 +240,7 @@
 		ReceiveCommand.abort(tmp);
 	}
 
+	@Override
 	protected void blockingFetch(Repository repo, ReplicaFetchRequest req)
 			throws NotSupportedException, TransportException {
 		try (Transport transport = Transport.open(repo, uri)) {
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/DfsBlockCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
index f7decf1..ef0b80c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
@@ -183,7 +183,7 @@
 		if (tableSize < 1)
 			throw new IllegalArgumentException(JGitText.get().tSizeMustBeGreaterOrEqual1);
 
-		table = new AtomicReferenceArray<HashEntry>(tableSize);
+		table = new AtomicReferenceArray<>(tableSize);
 		loadLocks = new ReentrantLock[cfg.getConcurrencyLevel()];
 		for (int i = 0; i < loadLocks.length; i++)
 			loadLocks[i] = new ReentrantLock(true /* fair */);
@@ -194,10 +194,10 @@
 		blockSizeShift = Integer.numberOfTrailingZeros(blockSize);
 
 		clockLock = new ReentrantLock(true /* fair */);
-		clockHand = new Ref<Object>(new DfsPackKey(), -1, 0, null);
+		clockHand = new Ref<>(new DfsPackKey(), -1, 0, null);
 		clockHand.next = clockHand;
 
-		packCache = new ConcurrentHashMap<DfsPackDescription, DfsPackFile>(
+		packCache = new ConcurrentHashMap<>(
 				16, 0.75f, 1);
 		packFiles = Collections.unmodifiableCollection(packCache.values());
 
@@ -357,7 +357,7 @@
 			}
 
 			key.cachedSize.addAndGet(v.size());
-			Ref<DfsBlock> ref = new Ref<DfsBlock>(key, position, v.size(), v);
+			Ref<DfsBlock> ref = new Ref<>(key, position, v.size(), v);
 			ref.hot = true;
 			for (;;) {
 				HashEntry n = new HashEntry(clean(e2), ref);
@@ -461,7 +461,7 @@
 			}
 
 			key.cachedSize.addAndGet(size);
-			ref = new Ref<T>(key, pos, size, v);
+			ref = new Ref<>(key, pos, size, v);
 			ref.hot = true;
 			for (;;) {
 				HashEntry n = new HashEntry(clean(e2), ref);
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 @@
 	/**
 	 * @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 @@
 	 *            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 @@
 	 * @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 @@
 	 * @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..d380302 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
@@ -43,9 +43,12 @@
 
 package org.eclipse.jgit.internal.storage.dfs;
 
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.COMPACT;
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC_REST;
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC_TXN;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.INSERT;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.RECEIVE;
 import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
@@ -53,8 +56,11 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.EnumSet;
+import java.util.GregorianCalendar;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -63,6 +69,7 @@
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
 import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.file.PackReverseIndex;
 import org.eclipse.jgit.internal.storage.pack.PackExt;
 import org.eclipse.jgit.internal.storage.pack.PackWriter;
 import org.eclipse.jgit.internal.storage.reftree.RefTreeNames;
@@ -77,6 +84,7 @@
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.storage.pack.PackConfig;
 import org.eclipse.jgit.storage.pack.PackStatistics;
+import org.eclipse.jgit.util.SystemReader;
 import org.eclipse.jgit.util.io.CountingOutputStream;
 
 /** Repack and garbage collect a repository. */
@@ -95,7 +103,8 @@
 
 	private PackConfig packConfig;
 
-	// See pack(), below, for how these two variables interact.
+	// See packIsCoalesceableGarbage(), below, for how these two variables
+	// interact.
 	private long coalesceGarbageLimit = 50 << 20;
 	private long garbageTtlMillis = TimeUnit.DAYS.toMillis(1);
 
@@ -118,9 +127,9 @@
 		repo = repository;
 		refdb = repo.getRefDatabase();
 		objdb = repo.getObjectDatabase();
-		newPackDesc = new ArrayList<DfsPackDescription>(4);
-		newPackStats = new ArrayList<PackStatistics>(4);
-		newPackObj = new ArrayList<ObjectIdSet>(4);
+		newPackDesc = new ArrayList<>(4);
+		newPackStats = new ArrayList<>(4);
+		newPackObj = new ArrayList<>(4);
 
 		packConfig = new PackConfig(repo);
 		packConfig.setIndexVersion(2);
@@ -223,14 +232,8 @@
 		if (packConfig.getIndexVersion() != 2)
 			throw new IllegalStateException(
 					JGitText.get().supportOnlyPackIndexVersion2);
-		if (garbageTtlMillis > 0) {
-			// We disable coalescing because the coalescing step will keep
-			// refreshing the UNREACHABLE_GARBAGE pack and we wouldn't
-			// actually prune anything.
-			coalesceGarbageLimit = 0;
-		}
 
-		startTimeMillis = System.currentTimeMillis();
+		startTimeMillis = SystemReader.getInstance().getCurrentTime();
 		ctx = (DfsReader) objdb.newReader();
 		try {
 			refdb.refresh();
@@ -246,14 +249,14 @@
 				return true;
 			}
 
-			allHeads = new HashSet<ObjectId>();
-			nonHeads = new HashSet<ObjectId>();
-			txnHeads = new HashSet<ObjectId>();
-			tagTargets = new HashSet<ObjectId>();
+			allHeads = new HashSet<>();
+			nonHeads = new HashSet<>();
+			txnHeads = new HashSet<>();
+			tagTargets = new HashSet<>();
 			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());
@@ -301,18 +304,18 @@
 
 	private void readPacksBefore() throws IOException {
 		DfsPackFile[] packs = objdb.getPacks();
-		packsBefore = new ArrayList<DfsPackFile>(packs.length);
-		expiredGarbagePacks = new ArrayList<DfsPackFile>(packs.length);
+		packsBefore = new ArrayList<>(packs.length);
+		expiredGarbagePacks = new ArrayList<>(packs.length);
 
 		long mostRecentGC = mostRecentGC(packs);
-		long now = System.currentTimeMillis();
+		long now = SystemReader.getInstance().getCurrentTime();
 		for (DfsPackFile p : packs) {
 			DfsPackDescription d = p.getPackDescription();
 			if (d.getPackSource() != UNREACHABLE_GARBAGE) {
 				packsBefore.add(p);
 			} else if (packIsExpiredGarbage(d, mostRecentGC, now)) {
 				expiredGarbagePacks.add(p);
-			} else if (d.getFileSize(PackExt.PACK) < coalesceGarbageLimit) {
+			} else if (packIsCoalesceableGarbage(d, now)) {
 				packsBefore.add(p);
 			}
 		}
@@ -355,6 +358,68 @@
 				&& now - d.getLastModified() >= garbageTtlMillis;
 	}
 
+	private boolean packIsCoalesceableGarbage(DfsPackDescription d, long now) {
+		// An UNREACHABLE_GARBAGE pack can be coalesced if its size is less than
+		// the coalesceGarbageLimit and either garbageTtl is zero or if the pack
+		// is created in a close time interval (on a single calendar day when
+		// the garbageTtl is more than one day or one third of the garbageTtl).
+		//
+		// When the garbageTtl is more than 24 hours, garbage packs that are
+		// created within a single calendar day are coalesced together. This
+		// would make the effective ttl of the garbage pack as garbageTtl+23:59
+		// and limit the number of garbage to a maximum number of
+		// garbageTtl_in_days + 1 (assuming all of them are less than the size
+		// of coalesceGarbageLimit).
+		//
+		// When the garbageTtl is less than or equal to 24 hours, garbage packs
+		// that are created within a one third of garbageTtl are coalesced
+		// together. This would make the effective ttl of the garbage packs as
+		// garbageTtl + (garbageTtl / 3) and would limit the number of garbage
+		// packs to a maximum number of 4 (assuming all of them are less than
+		// the size of coalesceGarbageLimit).
+
+		if (d.getPackSource() != UNREACHABLE_GARBAGE
+				|| d.getFileSize(PackExt.PACK) >= coalesceGarbageLimit) {
+			return false;
+		}
+
+		if (garbageTtlMillis == 0) {
+			return true;
+		}
+
+		long lastModified = d.getLastModified();
+		long dayStartLastModified = dayStartInMillis(lastModified);
+		long dayStartToday = dayStartInMillis(now);
+
+		if (dayStartLastModified != dayStartToday) {
+			return false; // this pack is not created today.
+		}
+
+		if (garbageTtlMillis > TimeUnit.DAYS.toMillis(1)) {
+			return true; // ttl is more than one day and pack is created today.
+		}
+
+		long timeInterval = garbageTtlMillis / 3;
+		if (timeInterval == 0) {
+			return false; // ttl is too small, don't try to coalesce.
+		}
+
+		long modifiedTimeSlot = (lastModified - dayStartLastModified) / timeInterval;
+		long presentTimeSlot = (now - dayStartToday) / timeInterval;
+		return modifiedTimeSlot == presentTimeSlot;
+	}
+
+	private static long dayStartInMillis(long timeInMillis) {
+		Calendar cal = new GregorianCalendar(
+				SystemReader.getInstance().getTimeZone());
+		cal.setTimeInMillis(timeInMillis);
+		cal.set(Calendar.HOUR_OF_DAY, 0);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		return cal.getTimeInMillis();
+	}
+
 	/** @return all of the source packs that fed into this compaction. */
 	public List<DfsPackDescription> getSourcePacks() {
 		return toPrune();
@@ -372,7 +437,7 @@
 
 	private List<DfsPackDescription> toPrune() {
 		int cnt = packsBefore.size();
-		List<DfsPackDescription> all = new ArrayList<DfsPackDescription>(cnt);
+		List<DfsPackDescription> all = new ArrayList<>(cnt);
 		for (DfsPackFile pack : packsBefore) {
 			all.add(pack.getPackDescription());
 		}
@@ -390,7 +455,8 @@
 			pw.setTagTargets(tagTargets);
 			pw.preparePack(pm, allHeads, PackWriter.NONE);
 			if (0 < pw.getObjectCount())
-				writePack(GC, pw, pm);
+				writePack(GC, pw, pm,
+						estimateGcPackSize(INSERT, RECEIVE, COMPACT, GC));
 		}
 	}
 
@@ -403,7 +469,8 @@
 				pw.excludeObjects(packedObjs);
 			pw.preparePack(pm, nonHeads, allHeads);
 			if (0 < pw.getObjectCount())
-				writePack(GC_REST, pw, pm);
+				writePack(GC_REST, pw, pm,
+						estimateGcPackSize(INSERT, RECEIVE, COMPACT, GC_REST));
 		}
 	}
 
@@ -416,7 +483,7 @@
 				pw.excludeObjects(packedObjs);
 			pw.preparePack(pm, txnHeads, PackWriter.NONE);
 			if (0 < pw.getObjectCount())
-				writePack(GC_TXN, pw, pm);
+				writePack(GC_TXN, pw, pm, 0 /* unknown pack size */);
 		}
 	}
 
@@ -432,21 +499,29 @@
 			pw.setDeltaBaseAsOffset(true);
 			pw.setReuseDeltaCommits(true);
 			pm.beginTask(JGitText.get().findingGarbage, objectsBefore());
+			long estimatedPackSize = 12 + 20; // header and trailer sizes.
 			for (DfsPackFile oldPack : packsBefore) {
 				PackIndex oldIdx = oldPack.getPackIndex(ctx);
+				PackReverseIndex oldRevIdx = oldPack.getReverseIdx(ctx);
+				long maxOffset = oldPack.getPackDescription().getFileSize(PACK)
+						- 20; // pack size - trailer size.
 				for (PackIndex.MutableEntry ent : oldIdx) {
 					pm.update(1);
 					ObjectId id = ent.toObjectId();
 					if (pool.lookupOrNull(id) != null || anyPackHas(id))
 						continue;
 
-					int type = oldPack.getObjectType(ctx, ent.getOffset());
+					long offset = ent.getOffset();
+					int type = oldPack.getObjectType(ctx, offset);
 					pw.addObject(pool.lookupAny(id, type));
+					long objSize = oldRevIdx.findNextOffset(offset, maxOffset)
+							- offset;
+					estimatedPackSize += objSize;
 				}
 			}
 			pm.endTask();
 			if (0 < pw.getObjectCount())
-				writePack(UNREACHABLE_GARBAGE, pw, pm);
+				writePack(UNREACHABLE_GARBAGE, pw, pm, estimatedPackSize);
 		}
 	}
 
@@ -461,6 +536,10 @@
 		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)
@@ -475,9 +554,24 @@
 		return pw;
 	}
 
+	private long estimateGcPackSize(PackSource first, PackSource... rest) {
+		EnumSet<PackSource> sourceSet = EnumSet.of(first, rest);
+		// Every pack file contains 12 bytes of header and 20 bytes of trailer.
+		// Include the final pack file header and trailer size here and ignore
+		// the same from individual pack files.
+		long size = 32;
+		for (DfsPackDescription pack : getSourcePacks()) {
+			if (sourceSet.contains(pack.getPackSource())) {
+				size += pack.getFileSize(PACK) - 32;
+			}
+		}
+		return size;
+	}
+
 	private DfsPackDescription writePack(PackSource source, PackWriter pw,
-			ProgressMonitor pm) throws IOException {
-		DfsPackDescription pack = repo.getObjectDatabase().newPack(source);
+			ProgressMonitor pm, long estimatedPackSize) throws IOException {
+		DfsPackDescription pack = repo.getObjectDatabase().newPack(source,
+				estimatedPackSize);
 		newPackDesc.add(pack);
 
 		try (DfsOutputStream out = objdb.writeFile(pack, PACK)) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
index c179e77..fd72756 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
@@ -90,6 +90,7 @@
 import org.eclipse.jgit.util.NB;
 import org.eclipse.jgit.util.TemporaryBuffer;
 import org.eclipse.jgit.util.io.CountingOutputStream;
+import org.eclipse.jgit.util.sha1.SHA1;
 
 /** Inserts objects into the DFS. */
 public class DfsInserter extends ObjectInserter {
@@ -168,7 +169,7 @@
 		}
 
 		long offset = beginObject(type, len);
-		MessageDigest md = digest();
+		SHA1 md = digest();
 		md.update(Constants.encodedTypeString(type));
 		md.update((byte) ' ');
 		md.update(Constants.encodeASCII(len));
@@ -183,7 +184,7 @@
 			len -= n;
 		}
 		packOut.compress.finish();
-		return endObject(ObjectId.fromRaw(md.digest()), offset);
+		return endObject(md.toObjectId(), offset);
 	}
 
 	private byte[] insertBuffer(long len) {
@@ -274,8 +275,8 @@
 	}
 
 	private void beginPack() throws IOException {
-		objectList = new BlockList<PackedObjectInfo>();
-		objectMap = new ObjectIdOwnerMap<PackedObjectInfo>();
+		objectList = new BlockList<>();
+		objectMap = new ObjectIdOwnerMap<>();
 		cache = DfsBlockCache.getInstance();
 
 		rollback = true;
@@ -543,7 +544,7 @@
 			if (objectList == null)
 				return stored;
 
-			Set<ObjectId> r = new HashSet<ObjectId>(stored.size() + 2);
+			Set<ObjectId> r = new HashSet<>(stored.size() + 2);
 			r.addAll(stored);
 			for (PackedObjectInfo obj : objectList) {
 				if (id.prefixCompare(obj) == 0)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
index b1d6c0d..b1cb72d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
@@ -160,7 +160,7 @@
 	protected DfsObjDatabase(DfsRepository repository,
 			DfsReaderOptions options) {
 		this.repository = repository;
-		this.packList = new AtomicReference<PackList>(NO_PACKS);
+		this.packList = new AtomicReference<>(NO_PACKS);
 		this.readerOptions = options;
 	}
 
@@ -266,6 +266,32 @@
 			throws IOException;
 
 	/**
+	 * Generate a new unique name for a pack file.
+	 *
+	 * <p>
+	 * Default implementation of this method would be equivalent to
+	 * {@code newPack(source).setEstimatedPackSize(estimatedPackSize)}. But the
+	 * clients can override this method to use the given
+	 * {@code estomatedPackSize} value more efficiently in the process of
+	 * creating a new {@link DfsPackDescription} object.
+	 *
+	 * @param source
+	 *            where the pack stream is created.
+	 * @param estimatedPackSize
+	 *            the estimated size of the pack.
+	 * @return a unique name for the pack file. Must not collide with any other
+	 *         pack file name in the same DFS.
+	 * @throws IOException
+	 *             a new unique pack description cannot be generated.
+	 */
+	protected DfsPackDescription newPack(PackSource source,
+			long estimatedPackSize) throws IOException {
+		DfsPackDescription pack = newPack(source);
+		pack.setEstimatedPackSize(estimatedPackSize);
+		return pack;
+	}
+
+	/**
 	 * Commit a pack and index pair that was written to the DFS.
 	 * <p>
 	 * Committing the pack/index pair makes them visible to readers. The JGit
@@ -432,7 +458,7 @@
 		List<DfsPackDescription> scanned = listPacks();
 		Collections.sort(scanned);
 
-		List<DfsPackFile> list = new ArrayList<DfsPackFile>(scanned.size());
+		List<DfsPackFile> list = new ArrayList<>(scanned.size());
 		boolean foundNew = false;
 		for (DfsPackDescription dsc : scanned) {
 			DfsPackFile oldPack = forReuse.remove(dsc);
@@ -457,7 +483,7 @@
 
 	private static Map<DfsPackDescription, DfsPackFile> reuseMap(PackList old) {
 		Map<DfsPackDescription, DfsPackFile> forReuse
-			= new HashMap<DfsPackDescription, DfsPackFile>();
+			= new HashMap<>();
 		for (DfsPackFile p : old.packs) {
 			if (p.invalid()) {
 				// The pack instance is corrupted, and cannot be safely used
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectRepresentation.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectRepresentation.java
index 7edae88..ddcfff6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectRepresentation.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjectRepresentation.java
@@ -43,8 +43,6 @@
 
 package org.eclipse.jgit.internal.storage.dfs;
 
-import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
-
 import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation;
 import org.eclipse.jgit.lib.ObjectId;
 
@@ -76,6 +74,13 @@
 
 	@Override
 	public boolean wasDeltaAttempted() {
-		return pack.getPackDescription().getPackSource() == GC;
+		switch (pack.getPackDescription().getPackSource()) {
+		case GC:
+		case GC_REST:
+		case GC_TXN:
+			return true;
+		default:
+			return false;
+		}
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
index 11aef7f..0a29ac5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
@@ -113,10 +113,10 @@
 	public DfsPackCompactor(DfsRepository repository) {
 		repo = repository;
 		autoAddSize = 5 * 1024 * 1024; // 5 MiB
-		srcPacks = new ArrayList<DfsPackFile>();
-		exclude = new ArrayList<ObjectIdSet>(4);
-		newPacks = new ArrayList<DfsPackDescription>(1);
-		newStats = new ArrayList<PackStatistics>(1);
+		srcPacks = new ArrayList<>();
+		exclude = new ArrayList<>(4);
+		newPacks = new ArrayList<>(1);
+		newStats = new ArrayList<>(1);
 	}
 
 	/**
@@ -224,7 +224,8 @@
 				}
 
 				boolean rollback = true;
-				DfsPackDescription pack = objdb.newPack(COMPACT);
+				DfsPackDescription pack = objdb.newPack(COMPACT,
+						estimatePackSize());
 				try {
 					writePack(objdb, pack, pw, pm);
 					writeIndex(objdb, pack, pw);
@@ -251,6 +252,17 @@
 		}
 	}
 
+	private long estimatePackSize() {
+		// Every pack file contains 12 bytes of header and 20 bytes of trailer.
+		// Include the final pack file header and trailer size here and ignore
+		// the same from individual pack files.
+		long size = 32;
+		for (DfsPackFile pack : srcPacks) {
+			size += pack.getPackDescription().getFileSize(PACK) - 32;
+		}
+		return size;
+	}
+
 	/** @return all of the source packs that fed into this compaction. */
 	public List<DfsPackDescription> getSourcePacks() {
 		return toPrune();
@@ -268,7 +280,7 @@
 
 	private List<DfsPackDescription> toPrune() {
 		int cnt = srcPacks.size();
-		List<DfsPackDescription> all = new ArrayList<DfsPackDescription>(cnt);
+		List<DfsPackDescription> all = new ArrayList<>(cnt);
 		for (DfsPackFile pack : srcPacks)
 			all.add(pack.getPackDescription());
 		return all;
@@ -281,6 +293,7 @@
 		// older packs, allowing the PackWriter to be handed newer objects
 		// first and older objects last.
 		Collections.sort(srcPacks, new Comparator<DfsPackFile>() {
+			@Override
 			public int compare(DfsPackFile a, DfsPackFile b) {
 				return a.getPackDescription().compareTo(b.getPackDescription());
 			}
@@ -289,7 +302,7 @@
 		rw = new RevWalk(ctx);
 		added = rw.newFlag("ADDED"); //$NON-NLS-1$
 		isBase = rw.newFlag("IS_BASE"); //$NON-NLS-1$
-		List<RevObject> baseObjects = new BlockList<RevObject>();
+		List<RevObject> baseObjects = new BlockList<>();
 
 		pm.beginTask(JGitText.get().countingObjects, ProgressMonitor.UNKNOWN);
 		for (DfsPackFile src : srcPacks) {
@@ -333,7 +346,7 @@
 	private List<ObjectIdWithOffset> toInclude(DfsPackFile src, DfsReader ctx)
 			throws IOException {
 		PackIndex srcIdx = src.getPackIndex(ctx);
-		List<ObjectIdWithOffset> want = new BlockList<ObjectIdWithOffset>(
+		List<ObjectIdWithOffset> want = new BlockList<>(
 				(int) srcIdx.getObjectCount());
 		SCAN: for (PackIndex.MutableEntry ent : srcIdx) {
 			ObjectId id = ent.toObjectId();
@@ -346,6 +359,7 @@
 			want.add(new ObjectIdWithOffset(id, ent.getOffset()));
 		}
 		Collections.sort(want, new Comparator<ObjectIdWithOffset>() {
+			@Override
 			public int compare(ObjectIdWithOffset a, ObjectIdWithOffset b) {
 				return Long.signum(a.offset - b.offset);
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
index 2b9d0e5..e825f1a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
@@ -81,6 +81,8 @@
 
 	private int indexVersion;
 
+	private long estimatedPackSize;
+
 	/**
 	 * Initialize a description by pack name and repository.
 	 * <p>
@@ -100,7 +102,7 @@
 		this.repoDesc = repoDesc;
 		int dot = name.lastIndexOf('.');
 		this.packName = (dot < 0) ? name : name.substring(0, dot);
-		this.sizeMap = new HashMap<PackExt, Long>(PackExt.values().length * 2);
+		this.sizeMap = new HashMap<>(PackExt.values().length * 2);
 	}
 
 	/** @return description of the repository. */
@@ -189,6 +191,25 @@
 		return size == null ? 0 : size.longValue();
 	}
 
+	/**
+	 * @param estimatedPackSize
+	 *            estimated size of the .pack file in bytes. If 0 the pack file
+	 *            size is unknown.
+	 * @return {@code this}
+	 */
+	public DfsPackDescription setEstimatedPackSize(long estimatedPackSize) {
+		this.estimatedPackSize = Math.max(0, estimatedPackSize);
+		return this;
+	}
+
+	/**
+	 * @return estimated size of the .pack file in bytes. If 0 the pack file
+	 *         size is unknown.
+	 */
+	public long getEstimatedPackSize() {
+		return estimatedPackSize;
+	}
+
 	/** @return number of objects in the pack. */
 	public long getObjectCount() {
 		return objectCount;
@@ -288,6 +309,7 @@
 	 * @param b
 	 *            the other pack.
 	 */
+	@Override
 	public int compareTo(DfsPackDescription b) {
 		// Cluster by PackSource, pushing UNREACHABLE_GARBAGE to the end.
 		PackSource as = getPackSource();
@@ -298,6 +320,17 @@
 				return cmp;
 		}
 
+		// Tie break GC type packs by smallest first. There should be at most
+		// one of each source, but when multiple exist concurrent GCs may have
+		// run. Preferring the smaller file selects higher quality delta
+		// compression, placing less demand on the DfsBlockCache.
+		if (as != null && as == bs && isGC(as)) {
+			int cmp = Long.signum(getFileSize(PACK) - b.getFileSize(PACK));
+			if (cmp != 0) {
+				return cmp;
+			}
+		}
+
 		// Newer packs should sort first.
 		int cmp = Long.signum(b.getLastModified() - getLastModified());
 		if (cmp != 0)
@@ -309,6 +342,17 @@
 		return Long.signum(getObjectCount() - b.getObjectCount());
 	}
 
+	static boolean isGC(PackSource s) {
+		switch (s) {
+		case GC:
+		case GC_REST:
+		case GC_TXN:
+			return true;
+		default:
+			return false;
+		}
+	}
+
 	@Override
 	public String toString() {
 		return getFileName(PackExt.PACK);
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 @@
 
 	/**
 	 * @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 @@
 				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 @@
 					position += n;
 					remaining -= n;
 					rc.position(position);
+					packHeadSkipped = true;
 					continue;
 				}
 
@@ -517,7 +518,14 @@
 					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/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
index 8c93295..755b163 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
@@ -44,10 +44,12 @@
 
 package org.eclipse.jgit.internal.storage.dfs;
 
+import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
@@ -64,6 +66,7 @@
 import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackList;
+import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
 import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl;
 import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
 import org.eclipse.jgit.internal.storage.file.PackIndex;
@@ -146,6 +149,7 @@
 		return null;
 	}
 
+	@Override
 	public Collection<CachedPack> getCachedPacksAndUpdate(
 		BitmapBuilder needBitmap) throws IOException {
 		for (DfsPackFile pack : db.getPacks()) {
@@ -162,20 +166,19 @@
 			throws IOException {
 		if (id.isComplete())
 			return Collections.singleton(id.toObjectId());
-		boolean noGarbage = avoidUnreachable;
-		HashSet<ObjectId> matches = new HashSet<ObjectId>(4);
+		HashSet<ObjectId> matches = new HashSet<>(4);
 		PackList packList = db.getPackList();
-		resolveImpl(packList, id, noGarbage, matches);
+		resolveImpl(packList, id, matches);
 		if (matches.size() < MAX_RESOLVE_MATCHES && packList.dirty()) {
-			resolveImpl(db.scanPacks(packList), id, noGarbage, matches);
+			resolveImpl(db.scanPacks(packList), id, matches);
 		}
 		return matches;
 	}
 
 	private void resolveImpl(PackList packList, AbbreviatedObjectId id,
-			boolean noGarbage, HashSet<ObjectId> matches) throws IOException {
+			HashSet<ObjectId> matches) throws IOException {
 		for (DfsPackFile pack : packList.packs) {
-			if (noGarbage && pack.isGarbage()) {
+			if (skipGarbagePack(pack)) {
 				continue;
 			}
 			pack.resolve(this, matches, id, MAX_RESOLVE_MATCHES);
@@ -187,22 +190,23 @@
 
 	@Override
 	public boolean has(AnyObjectId objectId) throws IOException {
-		if (last != null && last.hasObject(this, objectId))
+		if (last != null
+				&& !skipGarbagePack(last)
+				&& last.hasObject(this, objectId))
 			return true;
-		boolean noGarbage = avoidUnreachable;
 		PackList packList = db.getPackList();
-		if (hasImpl(packList, objectId, noGarbage)) {
+		if (hasImpl(packList, objectId)) {
 			return true;
 		} else if (packList.dirty()) {
-			return hasImpl(db.scanPacks(packList), objectId, noGarbage);
+			return hasImpl(db.scanPacks(packList), objectId);
 		}
 		return false;
 	}
 
-	private boolean hasImpl(PackList packList, AnyObjectId objectId,
-			boolean noGarbage) throws IOException {
+	private boolean hasImpl(PackList packList, AnyObjectId objectId)
+			throws IOException {
 		for (DfsPackFile pack : packList.packs) {
-			if (pack == last || (noGarbage && pack.isGarbage()))
+			if (pack == last || skipGarbagePack(pack))
 				continue;
 			if (pack.hasObject(this, objectId)) {
 				last = pack;
@@ -217,7 +221,7 @@
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException {
 		ObjectLoader ldr;
-		if (last != null) {
+		if (last != null && !skipGarbagePack(last)) {
 			ldr = last.get(this, objectId);
 			if (ldr != null) {
 				return checkType(ldr, objectId, typeHint);
@@ -225,13 +229,12 @@
 		}
 
 		PackList packList = db.getPackList();
-		boolean noGarbage = avoidUnreachable;
-		ldr = openImpl(packList, objectId, noGarbage);
+		ldr = openImpl(packList, objectId);
 		if (ldr != null) {
 			return checkType(ldr, objectId, typeHint);
 		}
 		if (packList.dirty()) {
-			ldr = openImpl(db.scanPacks(packList), objectId, noGarbage);
+			ldr = openImpl(db.scanPacks(packList), objectId);
 			if (ldr != null) {
 				return checkType(ldr, objectId, typeHint);
 			}
@@ -251,10 +254,10 @@
 		return ldr;
 	}
 
-	private ObjectLoader openImpl(PackList packList, AnyObjectId objectId,
-			boolean noGarbage) throws IOException {
+	private ObjectLoader openImpl(PackList packList, AnyObjectId objectId)
+			throws IOException {
 		for (DfsPackFile pack : packList.packs) {
-			if (pack == last || (noGarbage && pack.isGarbage())) {
+			if (pack == last || skipGarbagePack(pack)) {
 				continue;
 			}
 			ObjectLoader ldr = pack.get(this, objectId);
@@ -272,6 +275,7 @@
 	}
 
 	private static final Comparator<FoundObject<?>> FOUND_OBJECT_SORT = new Comparator<FoundObject<?>>() {
+		@Override
 		public int compare(FoundObject<?> a, FoundObject<?> b) {
 			int cmp = a.packIndex - b.packIndex;
 			if (cmp == 0)
@@ -315,7 +319,7 @@
 			findAllImpl(db.scanPacks(packList), pending, r);
 		}
 		for (T t : pending) {
-			r.add(new FoundObject<T>(t));
+			r.add(new FoundObject<>(t));
 		}
 		Collections.sort(r, FOUND_OBJECT_SORT);
 		return r;
@@ -329,31 +333,32 @@
 		}
 		int lastIdx = 0;
 		DfsPackFile lastPack = packs[lastIdx];
-		boolean noGarbage = avoidUnreachable;
 
 		OBJECT_SCAN: for (Iterator<T> it = pending.iterator(); it.hasNext();) {
 			T t = it.next();
-			try {
-				long p = lastPack.findOffset(this, t);
-				if (0 < p) {
-					r.add(new FoundObject<T>(t, lastIdx, lastPack, p));
-					it.remove();
-					continue;
+			if (!skipGarbagePack(lastPack)) {
+				try {
+					long p = lastPack.findOffset(this, t);
+					if (0 < p) {
+						r.add(new FoundObject<>(t, lastIdx, lastPack, p));
+						it.remove();
+						continue;
+					}
+				} catch (IOException e) {
+					// Fall though and try to examine other packs.
 				}
-			} catch (IOException e) {
-				// Fall though and try to examine other packs.
 			}
 
 			for (int i = 0; i < packs.length; i++) {
 				if (i == lastIdx)
 					continue;
 				DfsPackFile pack = packs[i];
-				if (noGarbage && pack.isGarbage())
+				if (skipGarbagePack(pack))
 					continue;
 				try {
 					long p = pack.findOffset(this, t);
 					if (0 < p) {
-						r.add(new FoundObject<T>(t, i, pack, p));
+						r.add(new FoundObject<>(t, i, pack, p));
 						it.remove();
 						lastIdx = i;
 						lastPack = pack;
@@ -368,6 +373,10 @@
 		last = lastPack;
 	}
 
+	private boolean skipGarbagePack(DfsPackFile pack) {
+		return avoidUnreachable && pack.isGarbage();
+	}
+
 	@Override
 	public <T extends ObjectId> AsyncObjectLoaderQueue<T> open(
 			Iterable<T> objectIds, final boolean reportMissing) {
@@ -385,6 +394,7 @@
 		return new AsyncObjectLoaderQueue<T>() {
 			private FoundObject<T> cur;
 
+			@Override
 			public boolean next() throws MissingObjectException, IOException {
 				if (idItr.hasNext()) {
 					cur = idItr.next();
@@ -396,14 +406,17 @@
 				}
 			}
 
+			@Override
 			public T getCurrent() {
 				return cur.id;
 			}
 
+			@Override
 			public ObjectId getObjectId() {
 				return cur.id;
 			}
 
+			@Override
 			public ObjectLoader open() throws IOException {
 				if (cur.pack == null)
 					throw new MissingObjectException(cur.id,
@@ -411,10 +424,12 @@
 				return cur.pack.load(DfsReader.this, cur.offset);
 			}
 
+			@Override
 			public boolean cancel(boolean mayInterruptIfRunning) {
 				return true;
 			}
 
+			@Override
 			public void release() {
 				// Nothing to clean up.
 			}
@@ -440,6 +455,7 @@
 
 			private long sz;
 
+			@Override
 			public boolean next() throws MissingObjectException, IOException {
 				if (idItr.hasNext()) {
 					cur = idItr.next();
@@ -455,22 +471,27 @@
 				}
 			}
 
+			@Override
 			public T getCurrent() {
 				return cur.id;
 			}
 
+			@Override
 			public ObjectId getObjectId() {
 				return cur.id;
 			}
 
+			@Override
 			public long getSize() {
 				return sz;
 			}
 
+			@Override
 			public boolean cancel(boolean mayInterruptIfRunning) {
 				return true;
 			}
 
+			@Override
 			public void release() {
 				// Nothing to clean up.
 			}
@@ -481,7 +502,7 @@
 	public long getObjectSize(AnyObjectId objectId, int typeHint)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException {
-		if (last != null) {
+		if (last != null && !skipGarbagePack(last)) {
 			long sz = last.getObjectSize(this, objectId);
 			if (0 <= sz) {
 				return sz;
@@ -510,7 +531,7 @@
 	private long getObjectSizeImpl(PackList packList, AnyObjectId objectId)
 			throws IOException {
 		for (DfsPackFile pack : packList.packs) {
-			if (pack == last) {
+			if (pack == last || skipGarbagePack(pack)) {
 				continue;
 			}
 			long sz = pack.getObjectSize(this, objectId);
@@ -522,22 +543,25 @@
 		return -1;
 	}
 
+	@Override
 	public DfsObjectToPack newObjectToPack(AnyObjectId objectId, int type) {
 		return new DfsObjectToPack(objectId, type);
 	}
 
 	private static final Comparator<DfsObjectToPack> OFFSET_SORT = new Comparator<DfsObjectToPack>() {
+		@Override
 		public int compare(DfsObjectToPack a, DfsObjectToPack b) {
 			return Long.signum(a.getOffset() - b.getOffset());
 		}
 	};
 
+	@Override
 	public void selectObjectRepresentation(PackWriter packer,
 			ProgressMonitor monitor, Iterable<ObjectToPack> objects)
 			throws IOException, MissingObjectException {
 		// Don't check dirty bit on PackList; assume ObjectToPacks all came from the
 		// current list.
-		for (DfsPackFile pack : db.getPacks()) {
+		for (DfsPackFile pack : sortPacksForSelectRepresentation()) {
 			List<DfsObjectToPack> tmp = findAllFromPack(pack, objects);
 			if (tmp.isEmpty())
 				continue;
@@ -556,9 +580,39 @@
 		}
 	}
 
+	private static final Comparator<DfsPackFile> PACK_SORT_FOR_REUSE = new Comparator<DfsPackFile>() {
+		@Override
+		public int compare(DfsPackFile af, DfsPackFile bf) {
+			DfsPackDescription ad = af.getPackDescription();
+			DfsPackDescription bd = bf.getPackDescription();
+			PackSource as = ad.getPackSource();
+			PackSource bs = bd.getPackSource();
+
+			if (as != null && as == bs && DfsPackDescription.isGC(as)) {
+				// Push smaller GC files last; these likely have higher quality
+				// delta compression and the contained representation should be
+				// favored over other files.
+				return Long.signum(bd.getFileSize(PACK) - ad.getFileSize(PACK));
+			}
+
+			// DfsPackDescription.compareTo already did a reasonable sort.
+			// Rely on Arrays.sort being stable, leaving equal elements.
+			return 0;
+		}
+	};
+
+	private DfsPackFile[] sortPacksForSelectRepresentation()
+			throws IOException {
+		DfsPackFile[] packs = db.getPacks();
+		DfsPackFile[] sorted = new DfsPackFile[packs.length];
+		System.arraycopy(packs, 0, sorted, 0, packs.length);
+		Arrays.sort(sorted, PACK_SORT_FOR_REUSE);
+		return sorted;
+	}
+
 	private List<DfsObjectToPack> findAllFromPack(DfsPackFile pack,
 			Iterable<ObjectToPack> objects) throws IOException {
-		List<DfsObjectToPack> tmp = new BlockList<DfsObjectToPack>();
+		List<DfsObjectToPack> tmp = new BlockList<>();
 		PackIndex idx = pack.getPackIndex(this);
 		for (ObjectToPack otp : objects) {
 			long p = idx.findOffset(otp);
@@ -570,6 +624,7 @@
 		return tmp;
 	}
 
+	@Override
 	public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp,
 			boolean validate) throws IOException,
 			StoredObjectRepresentationNotAvailableException {
@@ -577,12 +632,14 @@
 		src.pack.copyAsIs(out, src, validate, this);
 	}
 
+	@Override
 	public void writeObjects(PackOutputStream out, List<ObjectToPack> list)
 			throws IOException {
 		for (ObjectToPack otp : list)
 			out.writeObject(otp);
 	}
 
+	@Override
 	public void copyPackAsIs(PackOutputStream out, CachedPack pack)
 			throws IOException {
 		((DfsCachedPack) pack).copyAsIs(out, this);
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 @@
 	/**
 	 * @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 @@
 	 *            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/DfsRefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
index 4ddcec1..b41c18b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
@@ -78,7 +78,7 @@
 	 */
 	protected DfsRefDatabase(DfsRepository repository) {
 		this.repository = repository;
-		this.cache = new AtomicReference<RefCache>();
+		this.cache = new AtomicReference<>();
 	}
 
 	/** @return the repository the database holds the references of. */
@@ -120,7 +120,7 @@
 		RefCache curr = read();
 		RefList<Ref> packed = RefList.emptyList();
 		RefList<Ref> loose = curr.ids;
-		RefList.Builder<Ref> sym = new RefList.Builder<Ref>(curr.sym.size());
+		RefList.Builder<Ref> sym = new RefList.Builder<>(curr.sym.size());
 
 		for (int idx = 0; idx < curr.sym.size(); idx++) {
 			Ref ref = curr.sym.get(idx);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
index ef88450..a5dd514 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
@@ -143,10 +143,12 @@
 			AttributesNodeProvider {
 		private EmptyAttributesNode emptyAttributesNode = new EmptyAttributesNode();
 
+		@Override
 		public AttributesNode getInfoAttributesNode() throws IOException {
 			return emptyAttributesNode;
 		}
 
+		@Override
 		public AttributesNode getGlobalAttributesNode() throws IOException {
 			return emptyAttributesNode;
 		}
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..527e46b 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 @@
 	 *
 	 * @param repoDesc
 	 *            description of the repository.
-	 * @since 2.0
 	 */
 	public InMemoryRepository(DfsRepositoryDescription repoDesc) {
 		this(new Builder().setRepositoryDescription(repoDesc));
@@ -108,7 +107,7 @@
 	}
 
 	private class MemObjDatabase extends DfsObjDatabase {
-		private List<DfsPackDescription> packs = new ArrayList<DfsPackDescription>();
+		private List<DfsPackDescription> packs = new ArrayList<>();
 
 		MemObjDatabase(DfsRepository repo) {
 			super(repo, new DfsReaderOptions());
@@ -133,7 +132,7 @@
 				Collection<DfsPackDescription> desc,
 				Collection<DfsPackDescription> replace) {
 			List<DfsPackDescription> n;
-			n = new ArrayList<DfsPackDescription>(desc.size() + packs.size());
+			n = new ArrayList<>(desc.size() + packs.size());
 			n.addAll(desc);
 			n.addAll(packs);
 			if (replace != null)
@@ -171,7 +170,7 @@
 
 	private static class MemPack extends DfsPackDescription {
 		final Map<PackExt, byte[]>
-				fileMap = new HashMap<PackExt, byte[]>();
+				fileMap = new HashMap<>();
 
 		MemPack(String name, DfsRepositoryDescription repoDesc) {
 			super(repoDesc, name);
@@ -226,6 +225,7 @@
 			data = buf;
 		}
 
+		@Override
 		public int read(ByteBuffer dst) {
 			int n = Math.min(dst.remaining(), data.length - position);
 			if (n == 0)
@@ -235,30 +235,37 @@
 			return n;
 		}
 
+		@Override
 		public void close() {
 			open = false;
 		}
 
+		@Override
 		public boolean isOpen() {
 			return open;
 		}
 
+		@Override
 		public long position() {
 			return position;
 		}
 
+		@Override
 		public void position(long newPosition) {
 			position = (int) newPosition;
 		}
 
+		@Override
 		public long size() {
 			return data.length;
 		}
 
+		@Override
 		public int blockSize() {
 			return 0;
 		}
 
+		@Override
 		public void setReadAheadBytes(int b) {
 			// Unnecessary on a byte array.
 		}
@@ -271,7 +278,7 @@
 	 * subclasses of InMemoryRepository.
 	 */
     protected class MemRefDatabase extends DfsRefDatabase {
-		private final ConcurrentMap<String, Ref> refs = new ConcurrentHashMap<String, Ref>();
+		private final ConcurrentMap<String, Ref> refs = new ConcurrentHashMap<>();
 		private final ReadWriteLock lock = new ReentrantReadWriteLock(true /* fair */);
 
 		/**
@@ -308,8 +315,8 @@
 
 		@Override
 		protected RefCache scanAllRefs() throws IOException {
-			RefList.Builder<Ref> ids = new RefList.Builder<Ref>();
-			RefList.Builder<Ref> sym = new RefList.Builder<Ref>();
+			RefList.Builder<Ref> ids = new RefList.Builder<>();
+			RefList.Builder<Ref> sym = new RefList.Builder<>();
 			try {
 				lock.readLock().lock();
 				for (Ref ref : refs.values()) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java
index 30e973e..b78ff2a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java
@@ -58,6 +58,7 @@
 		this.bitmaps = bitmaps;
 	}
 
+	@Override
 	public EWAHCompressedBitmap getBitmap(AnyObjectId objectId) {
 		StoredBitmap sb = bitmaps.get(objectId);
 		return sb != null ? sb.getBitmap() : null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java
index b18a06f..88eef4c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java
@@ -370,6 +370,7 @@
 				private int type;
 				private IntIterator cached = dynamic;
 
+				@Override
 				public boolean hasNext() {
 					if (!cached.hasNext()) {
 						if (commits.hasNext()) {
@@ -391,6 +392,7 @@
 					return true;
 				}
 
+				@Override
 				public BitmapObject next() {
 					if (!hasNext())
 						throw new NoSuchElementException();
@@ -408,6 +410,7 @@
 					return out;
 				}
 
+				@Override
 				public void remove() {
 					throw new UnsupportedOperationException();
 				}
@@ -439,10 +442,10 @@
 
 	private static final class MutableBitmapIndex {
 		private final ObjectIdOwnerMap<MutableEntry>
-				revMap = new ObjectIdOwnerMap<MutableEntry>();
+				revMap = new ObjectIdOwnerMap<>();
 
 		private final BlockList<MutableEntry>
-				revList = new BlockList<MutableEntry>();
+				revList = new BlockList<>();
 
 		int findPosition(AnyObjectId objectId) {
 			MutableEntry entry = revMap.get(objectId);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
index a81d8ec..d47b304 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
@@ -90,7 +90,7 @@
 	}
 
 	private ObjectIdOwnerMap<UnpackedObjectId> scanLoose() {
-		ObjectIdOwnerMap<UnpackedObjectId> m = new ObjectIdOwnerMap<UnpackedObjectId>();
+		ObjectIdOwnerMap<UnpackedObjectId> m = new ObjectIdOwnerMap<>();
 		File objects = wrapped.getDirectory();
 		String[] fanout = objects.list();
 		if (fanout == null)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CheckoutEntryImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CheckoutEntryImpl.java
index e968119..4b4337d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CheckoutEntryImpl.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CheckoutEntryImpl.java
@@ -65,10 +65,12 @@
 		to = comment.substring(p2 + " to ".length(), p3); //$NON-NLS-1$
 	}
 
+	@Override
 	public String getFromBranch() {
 		return from;
 	}
 
+	@Override
 	public String getToBranch() {
 		return to;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java
index a95dea7..b397989 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java
@@ -69,7 +69,7 @@
 	private int openByteCount;
 
 	static {
-		DEAD = new SoftReference<Entry>(null);
+		DEAD = new SoftReference<>(null);
 		reconfigure(new WindowCacheConfig());
 	}
 
@@ -115,7 +115,7 @@
 		e.provider = pack;
 		e.position = position;
 		e.sz = data.length;
-		e.data = new SoftReference<Entry>(new Entry(data, objectType));
+		e.data = new SoftReference<>(new Entry(data, objectType));
 		moveToHead(e);
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
index 0388acb..ef9aa37 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
@@ -55,6 +55,7 @@
 import java.text.MessageFormat;
 import java.text.ParseException;
 import java.util.HashSet;
+import java.util.Locale;
 import java.util.Objects;
 import java.util.Set;
 
@@ -342,7 +343,7 @@
 		if (symLinks != null)
 			cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
 					ConfigConstants.CONFIG_KEY_SYMLINKS, symLinks.name()
-							.toLowerCase());
+							.toLowerCase(Locale.ROOT));
 		cfg.setInt(ConfigConstants.CONFIG_CORE_SECTION, null,
 				ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 0);
 		cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
@@ -489,7 +490,7 @@
 	 */
 	@Override
 	public Set<ObjectId> getAdditionalHaves() {
-		HashSet<ObjectId> r = new HashSet<ObjectId>();
+		HashSet<ObjectId> r = new HashSet<>();
 		for (AlternateHandle d : objectDatabase.myAlternates()) {
 			if (d instanceof AlternateRepository) {
 				Repository repo;
@@ -618,12 +619,18 @@
 
 	}
 
+	private boolean shouldAutoDetach() {
+		return getConfig().getBoolean(ConfigConstants.CONFIG_GC_SECTION,
+				ConfigConstants.CONFIG_KEY_AUTODETACH, true);
+	}
+
 	@Override
 	public void autoGC(ProgressMonitor monitor) {
 		GC gc = new GC(this);
 		gc.setPackConfig(new PackConfig(this));
 		gc.setProgressMonitor(monitor);
 		gc.setAuto(true);
+		gc.setBackground(shouldAutoDetach());
 		try {
 			gc.gc();
 		} catch (ParseException | IOException e) {
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 b608416..e1b309d 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
@@ -50,6 +50,8 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.nio.channels.Channels;
 import java.nio.channels.FileChannel;
 import java.nio.file.DirectoryStream;
@@ -73,12 +75,19 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.dircache.DirCacheIterator;
+import org.eclipse.jgit.errors.CancelledException;
 import org.eclipse.jgit.errors.CorruptObjectException;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
@@ -93,6 +102,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;
@@ -140,6 +151,8 @@
 
 	private static final int DEFAULT_AUTOLIMIT = 6700;
 
+	private static ExecutorService executor = Executors.newFixedThreadPool(1);
+
 	private final FileRepository repo;
 
 	private ProgressMonitor pm;
@@ -175,6 +188,11 @@
 	private boolean automatic;
 
 	/**
+	 * Whether to run gc in a background thread
+	 */
+	private boolean background;
+
+	/**
 	 * Creates a new garbage collector with default values. An expirationTime of
 	 * two weeks and <code>null</code> as progress monitor will be used.
 	 *
@@ -199,13 +217,73 @@
 	 * first check whether any housekeeping is required; if not, it exits
 	 * without performing any work.
 	 *
+	 * If {@link #setBackground(boolean)} was set to {@code true}
+	 * {@code collectGarbage} will start the gc in the background, and then
+	 * return immediately. In this case, errors will not be reported except in
+	 * gc.log.
+	 *
 	 * @return the collection of {@link PackFile}'s which are newly created
 	 * @throws IOException
 	 * @throws ParseException
 	 *             If the configuration parameter "gc.pruneexpire" couldn't be
 	 *             parsed
 	 */
+	// TODO(ms): in 5.0 change signature and return Future<Collection<PackFile>>
 	public Collection<PackFile> gc() throws IOException, ParseException {
+		final GcLog gcLog = background ? new GcLog(repo) : null;
+		if (gcLog != null && !gcLog.lock(background)) {
+			// there is already a background gc running
+			return Collections.emptyList();
+		}
+
+		Callable<Collection<PackFile>> gcTask = () -> {
+			try {
+				Collection<PackFile> newPacks = doGc();
+				if (automatic && tooManyLooseObjects() && gcLog != null) {
+					String message = JGitText.get().gcTooManyUnpruned;
+					gcLog.write(message);
+					gcLog.commit();
+				}
+				return newPacks;
+			} catch (IOException | ParseException e) {
+				if (background) {
+					if (gcLog == null) {
+						// Lacking a log, there's no way to report this.
+						return Collections.emptyList();
+					}
+					try {
+						gcLog.write(e.getMessage());
+						StringWriter sw = new StringWriter();
+						e.printStackTrace(new PrintWriter(sw));
+						gcLog.write(sw.toString());
+						gcLog.commit();
+					} catch (IOException e2) {
+						e2.addSuppressed(e);
+						LOG.error(e2.getMessage(), e2);
+					}
+				} else {
+					throw new JGitInternalException(e.getMessage(), e);
+				}
+			} finally {
+				if (gcLog != null) {
+					gcLog.unlock();
+				}
+			}
+			return Collections.emptyList();
+		};
+		Future<Collection<PackFile>> result = executor.submit(gcTask);
+		if (background) {
+			// TODO(ms): in 5.0 change signature and return the Future
+			return Collections.emptyList();
+		}
+		try {
+			return result.get();
+		} catch (InterruptedException | ExecutionException e) {
+			throw new IOException(e);
+		}
+	}
+
+	private Collection<PackFile> doGc() throws IOException, ParseException {
 		if (automatic && !needGc()) {
 			return Collections.emptyList();
 		}
@@ -219,11 +297,41 @@
 	}
 
 	/**
+	 * 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
@@ -232,8 +340,22 @@
 	 */
 	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 = !"now".equals(getPruneExpireStr()) && //$NON-NLS-1$
+			getExpireDate() < Long.MAX_VALUE;
+
+		prunePreserved();
 		long packExpireDate = getPackExpireDate();
 		oldPackLoop: for (PackFile oldPack : oldPacks) {
+			checkCancelled();
 			String oldName = oldPack.getPackName();
 			// check whether an old pack file is also among the list of new
 			// pack files. Then we must not delete it.
@@ -245,15 +367,57 @@
 					&& 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
@@ -272,7 +436,7 @@
 			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
@@ -281,7 +445,7 @@
 			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) {
@@ -306,6 +470,7 @@
 			pm.beginTask(JGitText.get().pruneLoosePackedObjects, fanout.length);
 			try {
 				for (String d : fanout) {
+					checkCancelled();
 					pm.update(1);
 					if (d.length() != 2)
 						continue;
@@ -313,6 +478,7 @@
 					if (entries == null)
 						continue;
 					for (String e : entries) {
+						checkCancelled();
 						if (e.length() != Constants.OBJECT_ID_STRING_LENGTH - 2)
 							continue;
 						ObjectId id;
@@ -324,11 +490,13 @@
 							continue;
 						}
 						boolean found = false;
-						for (PackFile p : packs)
+						for (PackFile p : packs) {
+							checkCancelled();
 							if (p.hasObject(id)) {
 								found = true;
 								break;
 							}
+						}
 						if (found)
 							FileUtils.delete(objdb.fileFor(id), FileUtils.RETRY
 									| FileUtils.SKIP_MISSING
@@ -360,7 +528,7 @@
 
 		// Collect all loose objects which are old enough, not referenced from
 		// the index and not in objectsToKeep
-		Map<ObjectId, File> deletionCandidates = new HashMap<ObjectId, File>();
+		Map<ObjectId, File> deletionCandidates = new HashMap<>();
 		Set<ObjectId> indexObjects = null;
 		File objects = repo.getObjectsDirectory();
 		String[] fanout = objects.list();
@@ -371,6 +539,7 @@
 				fanout.length);
 		try {
 			for (String d : fanout) {
+				checkCancelled();
 				pm.update(1);
 				if (d.length() != 2)
 					continue;
@@ -378,6 +547,7 @@
 				if (entries == null)
 					continue;
 				for (File f : entries) {
+					checkCancelled();
 					String fName = f.getName();
 					if (fName.length() != Constants.OBJECT_ID_STRING_LENGTH - 2)
 						continue;
@@ -407,6 +577,8 @@
 			return;
 		}
 
+		checkCancelled();
+
 		// From the set of current refs remove all those which have been handled
 		// during last repack(). Only those refs will survive which have been
 		// added or modified since the last repack. Only these can save existing
@@ -436,11 +608,14 @@
 			// leave this method.
 			ObjectWalk w = new ObjectWalk(repo);
 			try {
-				for (Ref cr : newRefs)
+				for (Ref cr : newRefs) {
+					checkCancelled();
 					w.markStart(w.parseAny(cr.getObjectId()));
+				}
 				if (lastPackedRefs != null)
-					for (Ref lpr : lastPackedRefs)
+					for (Ref lpr : lastPackedRefs) {
 						w.markUninteresting(w.parseAny(lpr.getObjectId()));
+					}
 				removeReferenced(deletionCandidates, w);
 			} finally {
 				w.dispose();
@@ -458,11 +633,15 @@
 		ObjectWalk w = new ObjectWalk(repo);
 		try {
 			for (Ref ar : getAllRefs())
-				for (ObjectId id : listRefLogObjects(ar, lastRepackTime))
+				for (ObjectId id : listRefLogObjects(ar, lastRepackTime)) {
+					checkCancelled();
 					w.markStart(w.parseAny(id));
+				}
 			if (lastPackedRefs != null)
-				for (Ref lpr : lastPackedRefs)
+				for (Ref lpr : lastPackedRefs) {
+					checkCancelled();
 					w.markUninteresting(w.parseAny(lpr.getObjectId()));
+				}
 			removeReferenced(deletionCandidates, w);
 		} finally {
 			w.dispose();
@@ -471,6 +650,8 @@
 		if (deletionCandidates.isEmpty())
 			return;
 
+		checkCancelled();
+
 		// delete all candidates which have survived: these are unreferenced
 		// loose objects. Make a last check, though, to avoid deleting objects
 		// that could have been referenced while the candidates list was being
@@ -495,9 +676,7 @@
 		long expireDate = Long.MAX_VALUE;
 
 		if (expire == null && expireAgeMillis == -1) {
-			String pruneExpireStr = repo.getConfig().getString(
-					ConfigConstants.CONFIG_GC_SECTION, null,
-					ConfigConstants.CONFIG_KEY_PRUNEEXPIRE);
+			String pruneExpireStr = getPruneExpireStr();
 			if (pruneExpireStr == null)
 				pruneExpireStr = PRUNE_EXPIRE_DEFAULT;
 			expire = GitDateParser.parse(pruneExpireStr, null, SystemReader
@@ -511,6 +690,12 @@
 		return expireDate;
 	}
 
+	private String getPruneExpireStr() {
+		return repo.getConfig().getString(
+                        ConfigConstants.CONFIG_GC_SECTION, null,
+                        ConfigConstants.CONFIG_KEY_PRUNEEXPIRE);
+	}
+
 	private long getPackExpireDate() throws ParseException {
 		long packExpireDate = Long.MAX_VALUE;
 
@@ -546,6 +731,7 @@
 			IncorrectObjectTypeException, IOException {
 		RevObject ro = w.next();
 		while (ro != null) {
+			checkCancelled();
 			if (id2File.remove(ro.getId()) != null)
 				if (id2File.isEmpty())
 					return;
@@ -553,6 +739,7 @@
 		}
 		ro = w.nextObject();
 		while (ro != null) {
+			checkCancelled();
 			if (id2File.remove(ro.getId()) != null)
 				if (id2File.isEmpty())
 					return;
@@ -582,10 +769,11 @@
 	 */
 	public void packRefs() throws IOException {
 		Collection<Ref> refs = repo.getRefDatabase().getRefs(Constants.R_REFS).values();
-		List<String> refsToBePacked = new ArrayList<String>(refs.size());
+		List<String> refsToBePacked = new ArrayList<>(refs.size());
 		pm.beginTask(JGitText.get().packRefs, refs.size());
 		try {
 			for (Ref ref : refs) {
+				checkCancelled();
 				if (!ref.isSymbolic() && ref.getStorage().isLoose())
 					refsToBePacked.add(ref.getName());
 				pm.update(1);
@@ -616,18 +804,19 @@
 		long time = System.currentTimeMillis();
 		Collection<Ref> refsBefore = getAllRefs();
 
-		Set<ObjectId> allHeads = new HashSet<ObjectId>();
-		Set<ObjectId> nonHeads = new HashSet<ObjectId>();
-		Set<ObjectId> txnHeads = new HashSet<ObjectId>();
-		Set<ObjectId> tagTargets = new HashSet<ObjectId>();
+		Set<ObjectId> allHeads = new HashSet<>();
+		Set<ObjectId> nonHeads = new HashSet<>();
+		Set<ObjectId> txnHeads = new HashSet<>();
+		Set<ObjectId> tagTargets = new HashSet<>();
 		Set<ObjectId> indexObjects = listNonHEADIndexObjects();
 		RefDatabase refdb = repo.getRefDatabase();
 
 		for (Ref ref : refsBefore) {
+			checkCancelled();
 			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());
@@ -637,15 +826,17 @@
 				tagTargets.add(ref.getPeeledObjectId());
 		}
 
-		List<ObjectIdSet> excluded = new LinkedList<ObjectIdSet>();
-		for (final PackFile f : repo.getObjectDatabase().getPacks())
+		List<ObjectIdSet> excluded = new LinkedList<>();
+		for (final PackFile f : repo.getObjectDatabase().getPacks()) {
+			checkCancelled();
 			if (f.shouldBeKept())
 				excluded.add(f.getIndex());
+		}
 
 		tagTargets.addAll(allHeads);
 		nonHeads.addAll(indexObjects);
 
-		List<PackFile> ret = new ArrayList<PackFile>(2);
+		List<PackFile> ret = new ArrayList<>(2);
 		PackFile heads = null;
 		if (!allHeads.isEmpty()) {
 			heads = writePack(allHeads, Collections.<ObjectId> emptySet(),
@@ -681,6 +872,14 @@
 		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);
+	}
+
 	/**
 	 * Deletes orphans
 	 * <p>
@@ -739,7 +938,7 @@
 				.getReverseEntries();
 		if (rlEntries == null || rlEntries.isEmpty())
 			return Collections.<ObjectId> emptySet();
-		Set<ObjectId> ret = new HashSet<ObjectId>();
+		Set<ObjectId> ret = new HashSet<>();
 		for (ReflogEntry e : rlEntries) {
 			if (e.getWho().getWhen().getTime() < minTime)
 				break;
@@ -773,6 +972,7 @@
 			all.addAll(refs);
 			// add additional refs which start with refs/
 			for (Ref r : addl) {
+				checkCancelled();
 				if (r.getName().startsWith(Constants.R_REFS)) {
 					all.add(r);
 				}
@@ -807,9 +1007,10 @@
 
 			treeWalk.setFilter(TreeFilter.ANY_DIFF);
 			treeWalk.setRecursive(true);
-			Set<ObjectId> ret = new HashSet<ObjectId>();
+			Set<ObjectId> ret = new HashSet<>();
 
 			while (treeWalk.next()) {
+				checkCancelled();
 				ObjectId objectId = treeWalk.getObjectId(0);
 				switch (treeWalk.getRawMode(0) & FileMode.TYPE_MASK) {
 				case FileMode.TYPE_MISSING:
@@ -837,9 +1038,11 @@
 	private PackFile writePack(@NonNull Set<? extends ObjectId> want,
 			@NonNull Set<? extends ObjectId> have, Set<ObjectId> tagTargets,
 			List<ObjectIdSet> excludeObjects) throws IOException {
+		checkCancelled();
 		File tmpPack = null;
-		Map<PackExt, File> tmpExts = new TreeMap<PackExt, File>(
+		Map<PackExt, File> tmpExts = new TreeMap<>(
 				new Comparator<PackExt>() {
+					@Override
 					public int compare(PackExt o1, PackExt o2) {
 						// INDEX entries must be returned last, so the pack
 						// scanner does pick up the new pack until all the
@@ -868,6 +1071,7 @@
 			pw.preparePack(pm, want, have);
 			if (pw.getObjectCount() == 0)
 				return null;
+			checkCancelled();
 
 			// create temporary files
 			String id = pw.computeName().getName();
@@ -984,6 +1188,12 @@
 		return new File(packdir, "pack-" + name + ext); //$NON-NLS-1$
 	}
 
+	private void checkCancelled() throws CancelledException {
+		if (pm.isCancelled()) {
+			throw new CancelledException(JGitText.get().operationCanceled);
+		}
+	}
+
 	/**
 	 * A class holding statistical data for a FileRepository regarding how many
 	 * objects are stored as loose or packed objects
@@ -1031,6 +1241,7 @@
 		 */
 		public long numberOfBitmaps;
 
+		@Override
 		public String toString() {
 			final StringBuilder b = new StringBuilder();
 			b.append("numberOfPackedObjects=").append(numberOfPackedObjects); //$NON-NLS-1$
@@ -1135,7 +1346,6 @@
 	 * 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
 	 */
@@ -1208,12 +1418,19 @@
 	 *
 	 * @param auto
 	 *            defines whether gc should do automatic housekeeping
-	 * @since 4.5
 	 */
 	public void setAuto(boolean auto) {
 		this.automatic = auto;
 	}
 
+	/**
+	 * @param background
+	 *            whether to run the gc in a background thread.
+	 */
+	void setBackground(boolean background) {
+		this.background = background;
+	}
+
 	private boolean needGc() {
 		if (tooManyPacks()) {
 			addRepackAllOption();
@@ -1252,8 +1469,7 @@
 	 * @return {@code true} if number of loose objects > gc.auto (default 6700)
 	 */
 	boolean tooManyLooseObjects() {
-		int auto = repo.getConfig().getInt(ConfigConstants.CONFIG_GC_SECTION,
-				ConfigConstants.CONFIG_KEY_AUTO, DEFAULT_AUTOLIMIT);
+		int auto = getLooseObjectLimit();
 		if (auto <= 0) {
 			return false;
 		}
@@ -1266,10 +1482,12 @@
 		try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir,
 				new DirectoryStream.Filter<Path>() {
 
+					@Override
 					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();
@@ -1283,4 +1501,9 @@
 		}
 		return false;
 	}
+
+	private int getLooseObjectLimit() {
+		return repo.getConfig().getInt(ConfigConstants.CONFIG_GC_SECTION,
+				ConfigConstants.CONFIG_KEY_AUTO, DEFAULT_AUTOLIMIT);
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java
new file mode 100644
index 0000000..9ea77cc
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GcLog.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2017 Two Sigma Open Source
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.util.GitDateParser;
+import org.eclipse.jgit.util.SystemReader;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.attribute.FileTime;
+import java.text.MessageFormat;
+import java.text.ParseException;
+import java.time.Instant;
+
+/**
+ * This class manages the gc.log file for a {@link FileRepository}.
+ */
+class GcLog {
+	private final FileRepository repo;
+
+	private final File logFile;
+
+	private final LockFile lock;
+
+	private Instant gcLogExpire;
+
+	private static final String LOG_EXPIRY_DEFAULT = "1.day.ago"; //$NON-NLS-1$
+
+	private boolean nonEmpty = false;
+
+	/**
+	 * Construct a GcLog object for a {@link FileRepository}
+	 *
+	 * @param repo
+	 *            the repository
+	 */
+	GcLog(FileRepository repo) {
+		this.repo = repo;
+		logFile = new File(repo.getDirectory(), "gc.log"); //$NON-NLS-1$
+		lock = new LockFile(logFile);
+	}
+
+	private Instant getLogExpiry() throws ParseException {
+		if (gcLogExpire == null) {
+			String logExpiryStr = repo.getConfig().getString(
+					ConfigConstants.CONFIG_GC_SECTION, null,
+					ConfigConstants.CONFIG_KEY_LOGEXPIRY);
+			if (logExpiryStr == null) {
+				logExpiryStr = LOG_EXPIRY_DEFAULT;
+			}
+			gcLogExpire = GitDateParser.parse(logExpiryStr, null,
+					SystemReader.getInstance().getLocale()).toInstant();
+		}
+		return gcLogExpire;
+	}
+
+	private boolean autoGcBlockedByOldLockFile(boolean background) {
+		try {
+			FileTime lastModified = Files.getLastModifiedTime(logFile.toPath());
+			if (lastModified.toInstant().compareTo(getLogExpiry()) > 0) {
+				// There is an existing log file, which is too recent to ignore
+				if (!background) {
+					try (BufferedReader reader = Files
+							.newBufferedReader(logFile.toPath())) {
+						char[] buf = new char[1000];
+						int len = reader.read(buf, 0, 1000);
+						String oldError = new String(buf, 0, len);
+
+						throw new JGitInternalException(MessageFormat.format(
+								JGitText.get().gcLogExists, oldError, logFile));
+					}
+				}
+				return true;
+			}
+		} catch (NoSuchFileException e) {
+			// No existing log file, OK.
+		} catch (IOException | ParseException e) {
+			throw new JGitInternalException(e.getMessage(), e);
+		}
+		return false;
+	}
+
+	/**
+	 * Lock the GC log file for updates
+	 *
+	 * @param background
+	 *            If true, and if gc.log already exists, unlock and return false
+	 * @return {@code true} if we hold the lock
+	 */
+	boolean lock(boolean background) {
+		try {
+			if (!lock.lock()) {
+				return false;
+			}
+		} catch (IOException e) {
+			throw new JGitInternalException(e.getMessage(), e);
+		}
+		if (autoGcBlockedByOldLockFile(background)) {
+			lock.unlock();
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Unlock (roll back) the GC log lock
+	 */
+	void unlock() {
+		lock.unlock();
+	}
+
+	/**
+	 * Commit changes to the gc log, if there have been any writes. Otherwise,
+	 * just unlock and delete the existing file (if any)
+	 *
+	 * @return true if committing (or unlocking/deleting) succeeds.
+	 */
+	boolean commit() {
+		if (nonEmpty) {
+			return lock.commit();
+		} else {
+			logFile.delete();
+			lock.unlock();
+			return true;
+		}
+	}
+
+	/**
+	 * Write to the pending gc log. Content will be committed upon a call to
+	 * commit()
+	 *
+	 * @param content
+	 *            The content to write
+	 * @throws IOException
+	 */
+	void write(String content) throws IOException {
+		if (content.length() > 0) {
+			nonEmpty = true;
+		}
+		lock.write(content.getBytes(UTF_8));
+	}
+}
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..d2fcacf 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
@@ -110,6 +110,7 @@
 
 	/** Filter to skip over active lock files when listing a directory. */
 	static final FilenameFilter FILTER = new FilenameFilter() {
+		@Override
 		public boolean accept(File dir, String name) {
 			return !name.endsWith(SUFFIX);
 		}
@@ -150,7 +151,6 @@
 	 *
 	 * @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 6489415..00e3953 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 @@
 
 	private final File packDirectory;
 
+	private final File preservedDirectory;
+
 	private final File alternatesFile;
 
 	private final AtomicReference<PackList> packList;
@@ -165,13 +167,14 @@
 		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);
+		packList = new AtomicReference<>(NO_PACKS);
 		unpackedObjectCache = new UnpackedObjectCache();
 		this.fs = fs;
 		this.shallowFile = shallowFile;
 
-		alternates = new AtomicReference<AlternateHandle[]>();
+		alternates = new AtomicReference<>();
 		if (alternatePaths != null) {
 			AlternateHandle[] alt;
 
@@ -185,10 +188,18 @@
 	/**
 	 * @return the location of the <code>objects</code> directory.
 	 */
+	@Override
 	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);
@@ -250,6 +261,7 @@
 	 *             index file could not be opened, read, or is not recognized as
 	 *             a Git pack file index.
 	 */
+	@Override
 	public PackFile openPack(final File pack)
 			throws IOException {
 		final String p = pack.getName();
@@ -436,6 +448,7 @@
 		return null;
 	}
 
+	@Override
 	ObjectLoader openLooseObject(WindowCursor curs, AnyObjectId id)
 			throws IOException {
 		File path = fileFor(id);
@@ -451,6 +464,7 @@
 		}
 	}
 
+	@Override
 	long getObjectSize(WindowCursor curs, AnyObjectId id)
 			throws IOException {
 		if (unpackedObjectCache.isUnpacked(id)) {
@@ -574,7 +588,7 @@
 				warnTmpl = JGitText.get().packWasDeleted;
 				removePack(p);
 			}
-		} else if (FileUtils.isStaleFileHandle(e)) {
+		} else if (FileUtils.isStaleFileHandleInCausalChain(e)) {
 			warnTmpl = JGitText.get().packHandleIsStale;
 			removePack(p);
 		} else {
@@ -689,6 +703,7 @@
 				&& old != scanPacks(old);
 	}
 
+	@Override
 	Config getConfig() {
 		return config;
 	}
@@ -705,13 +720,19 @@
 
 		if (shallowFileSnapshot == null
 				|| shallowFileSnapshot.isModified(shallowFile)) {
-			shallowCommitsIds = new HashSet<ObjectId>();
+			shallowCommitsIds = new HashSet<>();
 
 			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();
 			}
@@ -796,7 +817,7 @@
 		final Map<String, PackFile> forReuse = reuseMap(old);
 		final FileSnapshot snapshot = FileSnapshot.save(packDirectory);
 		final Set<String> names = listPackDirectory();
-		final List<PackFile> list = new ArrayList<PackFile>(names.size() >> 2);
+		final List<PackFile> list = new ArrayList<>(names.size() >> 2);
 		boolean foundNew = false;
 		for (final String indexName : names) {
 			// Must match "pack-[0-9a-f]{40}.idx" to be an index.
@@ -854,7 +875,7 @@
 	}
 
 	private static Map<String, PackFile> reuseMap(final PackList old) {
-		final Map<String, PackFile> forReuse = new HashMap<String, PackFile>();
+		final Map<String, PackFile> forReuse = new HashMap<>();
 		for (final PackFile p : old.packs) {
 			if (p.invalid()) {
 				// The pack instance is corrupted, and cannot be safely used
@@ -883,7 +904,7 @@
 		final String[] nameList = packDirectory.list();
 		if (nameList == null)
 			return Collections.emptySet();
-		final Set<String> nameSet = new HashSet<String>(nameList.length << 1);
+		final Set<String> nameSet = new HashSet<>(nameList.length << 1);
 		for (final String name : nameList) {
 			if (name.startsWith("pack-")) //$NON-NLS-1$
 				nameSet.add(name);
@@ -910,7 +931,7 @@
 	}
 
 	private AlternateHandle[] loadAlternates() throws IOException {
-		final List<AlternateHandle> l = new ArrayList<AlternateHandle>(4);
+		final List<AlternateHandle> l = new ArrayList<>(4);
 		final BufferedReader br = open(alternatesFile);
 		try {
 			String line;
@@ -953,6 +974,7 @@
 	 *            identity of the loose object to map to the directory.
 	 * @return location of the object, if it were to exist as a loose object.
 	 */
+	@Override
 	public File fileFor(AnyObjectId objectId) {
 		String n = objectId.name();
 		String d = n.substring(0, 2);
@@ -993,6 +1015,7 @@
 			repository = r;
 		}
 
+		@Override
 		void close() {
 			repository.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..aa435bf 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
@@ -49,12 +49,11 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.channels.Channels;
-import java.security.DigestOutputStream;
-import java.security.MessageDigest;
 import java.text.MessageFormat;
 import java.util.zip.Deflater;
 import java.util.zip.DeflaterOutputStream;
@@ -69,6 +68,7 @@
 import org.eclipse.jgit.transport.PackParser;
 import org.eclipse.jgit.util.FileUtils;
 import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.sha1.SHA1;
 
 /** Creates loose objects in a {@link ObjectDirectory}. */
 class ObjectDirectoryInserter extends ObjectInserter {
@@ -86,34 +86,71 @@
 	@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();
+			SHA1 md = digest();
 			File tmp = toTemp(md, type, len, is);
-			ObjectId id = ObjectId.fromRaw(md.digest());
-			return insertOneObject(tmp, id);
+			ObjectId id = md.toObjectId();
+			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:
@@ -156,7 +193,7 @@
 	}
 
 	@SuppressWarnings("resource" /* java 7 */)
-	private File toTemp(final MessageDigest md, final int type, long len,
+	private File toTemp(final SHA1 md, final int type, long len,
 			final InputStream is) throws IOException, FileNotFoundException,
 			Error {
 		boolean delete = true;
@@ -168,7 +205,7 @@
 				if (config.getFSyncObjectFiles())
 					out = Channels.newOutputStream(fOut.getChannel());
 				DeflaterOutputStream cOut = compress(out);
-				DigestOutputStream dOut = new DigestOutputStream(cOut, md);
+				SHA1OutputStream dOut = new SHA1OutputStream(cOut, md);
 				writeHeader(dOut, type, len);
 
 				final byte[] buf = buffer();
@@ -248,4 +285,25 @@
 		return new EOFException(MessageFormat.format(
 				JGitText.get().inputDidntMatchLength, Long.valueOf(missing)));
 	}
+
+	private static class SHA1OutputStream extends FilterOutputStream {
+		private final SHA1 md;
+
+		SHA1OutputStream(OutputStream out, SHA1 md) {
+			super(out);
+			this.md = md;
+		}
+
+		@Override
+		public void write(int b) throws IOException {
+			md.update((byte) b);
+			out.write(b);
+		}
+
+		@Override
+		public void write(byte[] in, int p, int n) throws IOException {
+			md.update(in, p, n);
+			out.write(in, p, n);
+		}
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java
index 956e8de..bfd60fc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java
@@ -75,9 +75,9 @@
 	private final EWAHCompressedBitmap tags;
 	private final BlockList<PositionEntry> byOffset;
 	final BlockList<StoredBitmap>
-			byAddOrder = new BlockList<StoredBitmap>();
+			byAddOrder = new BlockList<>();
 	final ObjectIdOwnerMap<PositionEntry>
-			positionEntries = new ObjectIdOwnerMap<PositionEntry>();
+			positionEntries = new ObjectIdOwnerMap<>();
 
 	/**
 	 * Creates a PackBitmapIndex used for building the contents of an index
@@ -133,6 +133,7 @@
 			positionEntries.add(new PositionEntry(entries.get(i), i));
 		}
 		Collections.sort(entries, new Comparator<ObjectToPack>() {
+			@Override
 			public int compare(ObjectToPack a, ObjectToPack b) {
 				return Long.signum(a.getOffset() - b.getOffset());
 			}
@@ -274,14 +275,17 @@
 		// Add order is from oldest to newest. The reverse add order is the
 		// output order.
 		return new Iterable<StoredEntry>() {
+			@Override
 			public Iterator<StoredEntry> iterator() {
 				return new Iterator<StoredEntry>() {
 					private int index = byAddOrder.size() - 1;
 
+					@Override
 					public boolean hasNext() {
 						return index >= 0;
 					}
 
+					@Override
 					public StoredEntry next() {
 						if (!hasNext())
 							throw new NoSuchElementException();
@@ -315,6 +319,7 @@
 								bestXorOffset, item.getFlags());
 					}
 
+					@Override
 					public void remove() {
 						throw new UnsupportedOperationException();
 					}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
index 2c462a7..9a8c275 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
@@ -107,7 +107,7 @@
 			BasePackBitmapIndex oldPackIndex, PackBitmapIndex newPackIndex) {
 		this.oldPackIndex = oldPackIndex;
 		this.newPackIndex = newPackIndex;
-		convertedBitmaps = new ObjectIdOwnerMap<StoredBitmap>();
+		convertedBitmaps = new ObjectIdOwnerMap<>();
 		inflated = new BitSet(newPackIndex.getObjectCount());
 
 		prevToNewMapping = new int[oldPackIndex.getObjectCount()];
@@ -137,6 +137,7 @@
 		return newPackIndex.ofObjectType(bitmap, type);
 	}
 
+	@Override
 	public Iterator<Entry> iterator() {
 		if (oldPackIndex == null)
 			return Collections.<Entry> emptyList().iterator();
@@ -145,6 +146,7 @@
 		return new Iterator<Entry>() {
 			private Entry entry;
 
+			@Override
 			public boolean hasNext() {
 				while (entry == null && it.hasNext()) {
 					StoredBitmap sb = it.next();
@@ -154,6 +156,7 @@
 				return entry != null;
 			}
 
+			@Override
 			public Entry next() {
 				if (!hasNext())
 					throw new NoSuchElementException();
@@ -163,6 +166,7 @@
 				return res;
 			}
 
+			@Override
 			public void remove() {
 				throw new UnsupportedOperationException();
 			}
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 b5889f2..0611d3e 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
@@ -47,6 +47,7 @@
 
 import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
 import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
+import static org.eclipse.jgit.internal.storage.pack.PackExt.KEEP;
 
 import java.io.EOFException;
 import java.io.File;
@@ -101,6 +102,7 @@
 public class PackFile implements Iterable<PackIndex.MutableEntry> {
 	/** Sorts PackFiles to be most recently created to least recently created. */
 	public static final Comparator<PackFile> SORT = new Comparator<PackFile>() {
+		@Override
 		public int compare(final PackFile a, final PackFile b) {
 			return b.packLastModified - a.packLastModified;
 		}
@@ -251,7 +253,7 @@
 	 */
 	public boolean shouldBeKept() {
 		if (keepFile == null)
-			keepFile = new File(packFile.getPath() + ".keep"); //$NON-NLS-1$
+			keepFile = extFile(KEEP);
 		return keepFile.exists();
 	}
 
@@ -301,6 +303,7 @@
 	 *
 	 * @see PackIndex#iterator()
 	 */
+	@Override
 	public Iterator<PackIndex.MutableEntry> iterator() {
 		try {
 			return idx().iterator();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
index 5d4a30f..2252778 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
@@ -183,6 +183,7 @@
 	 *
 	 * @return iterator over pack index entries
 	 */
+	@Override
 	public abstract Iterator<MutableEntry> iterator();
 
 	/**
@@ -365,6 +366,7 @@
 
 		protected abstract MutableEntry initEntry();
 
+		@Override
 		public boolean hasNext() {
 			return returnedNumber < getObjectCount();
 		}
@@ -373,8 +375,10 @@
 		 * Implementation must update {@link #returnedNumber} before returning
 		 * element.
 		 */
+		@Override
 		public abstract MutableEntry next();
 
+		@Override
 		public void remove() {
 			throw new UnsupportedOperationException();
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
index e5a729d..8a08456 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
@@ -240,6 +240,7 @@
 		@Override
 		protected MutableEntry initEntry() {
 			return new MutableEntry() {
+				@Override
 				protected void ensureId() {
 					idBuffer.fromRaw(idxdata[levelOne], levelTwo
 							- Constants.OBJECT_ID_LENGTH);
@@ -247,6 +248,7 @@
 			};
 		}
 
+		@Override
 		public MutableEntry next() {
 			for (; levelOne < idxdata.length; levelOne++) {
 				if (idxdata[levelOne] == null)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
index d87336f..5c2986a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
@@ -311,6 +311,7 @@
 		@Override
 		protected MutableEntry initEntry() {
 			return new MutableEntry() {
+				@Override
 				protected void ensureId() {
 					idBuffer.fromRaw(names[levelOne], levelTwo
 							- Constants.OBJECT_ID_LENGTH / 4);
@@ -318,6 +319,7 @@
 			};
 		}
 
+		@Override
 		public MutableEntry next() {
 			for (; levelOne < names.length; levelOne++) {
 				if (levelTwo < names[levelOne].length) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index e3d0d61..24d51a5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
@@ -153,10 +153,10 @@
 	 * converted into resolved references during a get operation, ensuring the
 	 * live value is always returned.
 	 */
-	private final AtomicReference<RefList<LooseRef>> looseRefs = new AtomicReference<RefList<LooseRef>>();
+	private final AtomicReference<RefList<LooseRef>> looseRefs = new AtomicReference<>();
 
 	/** Immutable sorted list of packed references. */
-	final AtomicReference<PackedRefList> packedRefs = new AtomicReference<PackedRefList>();
+	final AtomicReference<PackedRefList> packedRefs = new AtomicReference<>();
 
 	/**
 	 * Number of modifications made to this database.
@@ -194,6 +194,7 @@
 		return logWriter;
 	}
 
+	@Override
 	public void create() throws IOException {
 		FileUtils.mkdir(refsDir);
 		FileUtils.mkdir(new File(refsDir, R_HEADS.substring(R_REFS.length())));
@@ -351,7 +352,7 @@
 
 	@Override
 	public List<Ref> getAdditionalRefs() throws IOException {
-		List<Ref> ret = new LinkedList<Ref>();
+		List<Ref> ret = new LinkedList<>();
 		for (String name : additionalRefsNames) {
 			Ref r = getRef(name);
 			if (r != null)
@@ -370,7 +371,7 @@
 
 		private int curIdx;
 
-		final RefList.Builder<Ref> symbolic = new RefList.Builder<Ref>(4);
+		final RefList.Builder<Ref> symbolic = new RefList.Builder<>(4);
 
 		RefList.Builder<LooseRef> newLoose;
 
@@ -534,6 +535,7 @@
 		fireRefsChanged();
 	}
 
+	@Override
 	public RefDirectoryUpdate newUpdate(String name, boolean detach)
 			throws IOException {
 		boolean detachingSymbolicRef = false;
@@ -798,7 +800,8 @@
 				return new PackedRefList(parsePackedRefs(br), snapshot,
 						ObjectId.fromRaw(digest.digest()));
 			} catch (IOException e) {
-				if (FileUtils.isStaleFileHandle(e) && retries < maxStaleRetries) {
+				if (FileUtils.isStaleFileHandleInCausalChain(e)
+						&& retries < maxStaleRetries) {
 					if (LOG.isDebugEnabled()) {
 						LOG.debug(MessageFormat.format(
 								JGitText.get().packedRefsHandleIsStale,
@@ -816,7 +819,7 @@
 
 	private RefList<Ref> parsePackedRefs(final BufferedReader br)
 			throws IOException {
-		RefList.Builder<Ref> all = new RefList.Builder<Ref>();
+		RefList.Builder<Ref> all = new RefList.Builder<>();
 		Ref last = null;
 		boolean peeled = false;
 		boolean needSort = false;
@@ -1125,10 +1128,12 @@
 			this.snapShot = snapshot;
 		}
 
+		@Override
 		public FileSnapshot getSnapShot() {
 			return snapShot;
 		}
 
+		@Override
 		public LooseRef peel(ObjectIdRef newLeaf) {
 			return this;
 		}
@@ -1144,10 +1149,12 @@
 			this.snapShot = snapshot;
 		}
 
+		@Override
 		public FileSnapshot getSnapShot() {
 			return snapShot;
 		}
 
+		@Override
 		public LooseRef peel(ObjectIdRef newLeaf) {
 			return this;
 		}
@@ -1163,6 +1170,7 @@
 			this.snapShot = snapShot;
 		}
 
+		@Override
 		public FileSnapshot getSnapShot() {
 			return snapShot;
 		}
@@ -1175,6 +1183,7 @@
 			return id;
 		}
 
+		@Override
 		public LooseRef peel(ObjectIdRef newLeaf) {
 			ObjectId peeledObjectId = newLeaf.getPeeledObjectId();
 			ObjectId objectId = getObjectId();
@@ -1198,10 +1207,12 @@
 			this.snapShot = snapshot;
 		}
 
+		@Override
 		public FileSnapshot getSnapShot() {
 			return snapShot;
 		}
 
+		@Override
 		public LooseRef peel(ObjectIdRef newLeaf) {
 			// We should never try to peel the symbolic references.
 			throw new UnsupportedOperationException();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogEntryImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogEntryImpl.java
index 60f04b8..16b2a46 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogEntryImpl.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogEntryImpl.java
@@ -93,6 +93,7 @@
 	/* (non-Javadoc)
 	 * @see org.eclipse.jgit.internal.storage.file.ReflogEntry#getOldId()
 	 */
+	@Override
 	public ObjectId getOldId() {
 		return oldId;
 	}
@@ -100,6 +101,7 @@
 	/* (non-Javadoc)
 	 * @see org.eclipse.jgit.internal.storage.file.ReflogEntry#getNewId()
 	 */
+	@Override
 	public ObjectId getNewId() {
 		return newId;
 	}
@@ -107,6 +109,7 @@
 	/* (non-Javadoc)
 	 * @see org.eclipse.jgit.internal.storage.file.ReflogEntry#getWho()
 	 */
+	@Override
 	public PersonIdent getWho() {
 		return who;
 	}
@@ -114,6 +117,7 @@
 	/* (non-Javadoc)
 	 * @see org.eclipse.jgit.internal.storage.file.ReflogEntry#getComment()
 	 */
+	@Override
 	public String getComment() {
 		return comment;
 	}
@@ -128,6 +132,7 @@
 	/* (non-Javadoc)
 	 * @see org.eclipse.jgit.internal.storage.file.ReflogEntry#parseCheckout()
 	 */
+	@Override
 	public CheckoutEntry parseCheckout() {
 		if (getComment().startsWith(CheckoutEntryImpl.CHECKOUT_MOVING_FROM))
 			return new CheckoutEntryImpl(this);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
index 2f583b2..c3702fe 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
@@ -74,6 +74,7 @@
 	/* (non-Javadoc)
 	 * @see org.eclipse.jgit.internal.storage.file.ReflogReaader#getLastEntry()
 	 */
+	@Override
 	public ReflogEntry getLastEntry() throws IOException {
 		return getReverseEntry(0);
 	}
@@ -81,6 +82,7 @@
 	/* (non-Javadoc)
 	 * @see org.eclipse.jgit.internal.storage.file.ReflogReaader#getReverseEntries()
 	 */
+	@Override
 	public List<ReflogEntry> getReverseEntries() throws IOException {
 		return getReverseEntries(Integer.MAX_VALUE);
 	}
@@ -88,6 +90,7 @@
 	/* (non-Javadoc)
 	 * @see org.eclipse.jgit.internal.storage.file.ReflogReaader#getReverseEntry(int)
 	 */
+	@Override
 	public ReflogEntry getReverseEntry(int number) throws IOException {
 		if (number < 0)
 			throw new IllegalArgumentException();
@@ -116,6 +119,7 @@
 	/* (non-Javadoc)
 	 * @see org.eclipse.jgit.internal.storage.file.ReflogReaader#getReverseEntries(int)
 	 */
+	@Override
 	public List<ReflogEntry> getReverseEntries(int max) throws IOException {
 		final byte[] log;
 		try {
@@ -128,7 +132,7 @@
 		}
 
 		int rs = RawParseUtils.prevLF(log, log.length);
-		List<ReflogEntry> ret = new ArrayList<ReflogEntry>();
+		List<ReflogEntry> ret = new ArrayList<>();
 		while (rs >= 0 && max-- > 0) {
 			rs = RawParseUtils.prevLF(log, rs);
 			ReflogEntry entry = new ReflogEntryImpl(log, rs < 0 ? 0 : rs + 2);
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/file/SimpleDataInput.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SimpleDataInput.java
index a733068..452636d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SimpleDataInput.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SimpleDataInput.java
@@ -64,11 +64,13 @@
 		this.fd = fd;
 	}
 
+	@Override
 	public int readInt() throws IOException {
 		readFully(buf, 0, 4);
 		return NB.decodeInt32(buf, 0);
 	}
 
+	@Override
 	public long readLong() throws IOException {
 		readFully(buf, 0, 8);
 		return NB.decodeInt64(buf, 0);
@@ -79,54 +81,67 @@
 		return NB.decodeUInt32(buf, 0);
 	}
 
+	@Override
 	public void readFully(byte[] b) throws IOException {
 		readFully(b, 0, b.length);
 	}
 
+	@Override
 	public void readFully(byte[] b, int off, int len) throws IOException {
 		IO.readFully(fd, b, off, len);
 	}
 
+	@Override
 	public int skipBytes(int n) throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public boolean readBoolean() throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public byte readByte() throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public int readUnsignedByte() throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public short readShort() throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public int readUnsignedShort() throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public char readChar() throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public float readFloat() throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public double readDouble() throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public String readLine() throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public String readUTF() throws IOException {
 		throw new UnsupportedOperationException();
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SimpleDataOutput.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SimpleDataOutput.java
index d9c899a..373a494 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SimpleDataOutput.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/SimpleDataOutput.java
@@ -64,61 +64,75 @@
 		this.fd = fd;
 	}
 
+	@Override
 	public void writeShort(int v) throws IOException {
 		NB.encodeInt16(buf, 0, v);
 		fd.write(buf, 0, 2);
 	}
 
+	@Override
 	public void writeInt(int v) throws IOException {
 		NB.encodeInt32(buf, 0, v);
 		fd.write(buf, 0, 4);
 	}
 
+	@Override
 	public void writeLong(long v) throws IOException {
 		NB.encodeInt64(buf, 0, v);
 		fd.write(buf, 0, 8);
 	}
 
+	@Override
 	public void write(int b) throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public void write(byte[] b) throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public void write(byte[] b, int off, int len) throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public void writeBoolean(boolean v) throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public void writeByte(int v) throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public void writeChar(int v) throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public void writeFloat(float v) throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public void writeDouble(double v) throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public void writeBytes(String s) throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public void writeChars(String s) throws IOException {
 		throw new UnsupportedOperationException();
 	}
 
+	@Override
 	public void writeUTF(String s) throws IOException {
 		throw new UnsupportedOperationException();
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java
index ce67ae0..967754a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObjectCache.java
@@ -100,7 +100,7 @@
 		final int bits;
 
 		Table(int bits) {
-			this.ids = new AtomicReferenceArray<ObjectId>(1 << bits);
+			this.ids = new AtomicReferenceArray<>(1 << bits);
 			this.shift = 32 - bits;
 			this.bits = bits;
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
index e1b7606..a525c85 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
@@ -235,9 +235,9 @@
 		if (lockCount < 1)
 			throw new IllegalArgumentException(JGitText.get().lockCountMustBeGreaterOrEqual1);
 
-		queue = new ReferenceQueue<ByteWindow>();
+		queue = new ReferenceQueue<>();
 		clock = new AtomicLong(1);
-		table = new AtomicReferenceArray<Entry>(tableSize);
+		table = new AtomicReferenceArray<>(tableSize);
 		locks = new Lock[lockCount];
 		for (int i = 0; i < locks.length; i++)
 			locks[i] = new Lock();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
index a742d17..83b236e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
@@ -125,6 +125,7 @@
 		return null;
 	}
 
+	@Override
 	public Collection<CachedPack> getCachedPacksAndUpdate(
 			BitmapBuilder needBitmap) throws IOException {
 		for (PackFile pack : db.getPacks()) {
@@ -141,15 +142,17 @@
 			throws IOException {
 		if (id.isComplete())
 			return Collections.singleton(id.toObjectId());
-		HashSet<ObjectId> matches = new HashSet<ObjectId>(4);
+		HashSet<ObjectId> matches = new HashSet<>(4);
 		db.resolve(matches, id);
 		return matches;
 	}
 
+	@Override
 	public boolean has(AnyObjectId objectId) throws IOException {
 		return db.has(objectId);
 	}
 
+	@Override
 	public ObjectLoader open(AnyObjectId objectId, int typeHint)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException {
@@ -170,6 +173,7 @@
 		return db.getShallowCommits();
 	}
 
+	@Override
 	public long getObjectSize(AnyObjectId objectId, int typeHint)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException {
@@ -183,10 +187,12 @@
 		return sz;
 	}
 
+	@Override
 	public LocalObjectToPack newObjectToPack(AnyObjectId objectId, int type) {
 		return new LocalObjectToPack(objectId, type);
 	}
 
+	@Override
 	public void selectObjectRepresentation(PackWriter packer,
 			ProgressMonitor monitor, Iterable<ObjectToPack> objects)
 			throws IOException, MissingObjectException {
@@ -196,6 +202,7 @@
 		}
 	}
 
+	@Override
 	public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp,
 			boolean validate) throws IOException,
 			StoredObjectRepresentationNotAvailableException {
@@ -203,6 +210,7 @@
 		src.pack.copyAsIs(out, src, validate, this);
 	}
 
+	@Override
 	public void writeObjects(PackOutputStream out, List<ObjectToPack> list)
 			throws IOException {
 		for (ObjectToPack otp : list)
@@ -245,6 +253,7 @@
 		return cnt - need;
 	}
 
+	@Override
 	public void copyPackAsIs(PackOutputStream out, CachedPack pack)
 			throws IOException {
 		((LocalCachedPack) pack).copyAsIs(out, this);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WriteConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WriteConfig.java
index 4f79ea9..1e2b239 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WriteConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WriteConfig.java
@@ -50,6 +50,7 @@
 class WriteConfig {
 	/** Key for {@link Config#get(SectionParser)}. */
 	static final Config.SectionParser<WriteConfig> KEY = new SectionParser<WriteConfig>() {
+		@Override
 		public WriteConfig parse(final Config cfg) {
 			return new WriteConfig(cfg);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BaseSearch.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BaseSearch.java
index b6af0b0..d231ccb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BaseSearch.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BaseSearch.java
@@ -96,7 +96,7 @@
 		edgeObjects = edges;
 
 		alreadyProcessed = new IntSet();
-		treeCache = new ObjectIdOwnerMap<TreeWithData>();
+		treeCache = new ObjectIdOwnerMap<>();
 		parser = new CanonicalTreeParser();
 		idBuf = new MutableObjectId();
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaCache.java
index 91917b2..973dd1d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaCache.java
@@ -60,7 +60,7 @@
 	DeltaCache(PackConfig pc) {
 		size = pc.getDeltaCacheSize();
 		entryLimit = pc.getDeltaCacheLimit();
-		queue = new ReferenceQueue<byte[]>();
+		queue = new ReferenceQueue<>();
 	}
 
 	boolean canCache(int length, ObjectToPack src, ObjectToPack res) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaIndex.java
index 2a2a463..0f22de0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaIndex.java
@@ -421,6 +421,7 @@
 		return start - resPtr;
 	}
 
+	@Override
 	@SuppressWarnings("nls")
 	public String toString() {
 		String[] units = { "bytes", "KiB", "MiB", "GiB" };
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java
index 4292742..0c4e444 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java
@@ -78,7 +78,7 @@
 		Block(int threads, PackConfig config, ObjectReader reader,
 				DeltaCache dc, ThreadSafeProgressMonitor pm,
 				ObjectToPack[] list, int begin, int end) {
-			this.tasks = new ArrayList<DeltaTask>(threads);
+			this.tasks = new ArrayList<>(threads);
 			this.threads = threads;
 			this.config = config;
 			this.templateReader = reader;
@@ -176,7 +176,7 @@
 		}
 
 		private ArrayList<WeightedPath> computeTopPaths() {
-			ArrayList<WeightedPath> topPaths = new ArrayList<WeightedPath>(
+			ArrayList<WeightedPath> topPaths = new ArrayList<>(
 					threads);
 			int cp = beginIndex;
 			int ch = list[cp].getPathHash();
@@ -213,6 +213,7 @@
 
 			// Sort by starting index to identify gaps later.
 			Collections.sort(topPaths, new Comparator<WeightedPath>() {
+				@Override
 				public int compare(WeightedPath a, WeightedPath b) {
 					return a.slice.beginIndex - b.slice.beginIndex;
 				}
@@ -244,6 +245,7 @@
 			this.slice = s;
 		}
 
+		@Override
 		public int compareTo(WeightedPath o) {
 			int cmp = Long.signum(weight - o.weight);
 			if (cmp != 0) {
@@ -275,7 +277,7 @@
 
 	DeltaTask(Block b) {
 		this.block = b;
-		this.slices = new LinkedList<Slice>();
+		this.slices = new LinkedList<>();
 	}
 
 	void add(Slice s) {
@@ -290,6 +292,7 @@
 		slices.add(s);
 	}
 
+	@Override
 	public Object call() throws Exception {
 		or = block.templateReader.newReader();
 		try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java
index 19d06a2..73b285a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaWindow.java
@@ -160,6 +160,7 @@
 						clear(n);
 				}
 				res.set(next);
+				clearWindowOnTypeSwitch();
 
 				if (res.object.isEdge() || res.object.doNotAttemptDelta()) {
 					// We don't actually want to make a delta for
@@ -194,6 +195,15 @@
 		return DeltaIndex.estimateIndexSize(len) - len;
 	}
 
+	private void clearWindowOnTypeSwitch() {
+		DeltaWindowEntry p = res.prev;
+		if (!p.empty() && res.type() != p.type()) {
+			for (; p != res; p = p.prev) {
+				clear(p);
+			}
+		}
+	}
+
 	private void clear(DeltaWindowEntry ent) {
 		if (ent.index != null)
 			loaded -= ent.index.getIndexSize();
@@ -258,12 +268,6 @@
 
 	private boolean delta(final DeltaWindowEntry src)
 			throws IOException {
-		// Objects must use only the same type as their delta base.
-		if (src.type() != res.type()) {
-			keepInWindow();
-			return NEXT_RES;
-		}
-
 		// If the sizes are radically different, this is a bad pairing.
 		if (res.size() < src.size() >>> 4)
 			return NEXT_SRC;
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 @@
 	/** 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..93dbee3 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,18 +164,20 @@
 	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>();
+			new ConcurrentHashMap<>();
 
 	private static final Iterable<PackWriter> instancesIterable = new Iterable<PackWriter>() {
+		@Override
 		public Iterator<PackWriter> iterator() {
 			return new Iterator<PackWriter>() {
 				private final Iterator<WeakReference<PackWriter>> it =
 						instances.keySet().iterator();
 				private PackWriter next;
 
+				@Override
 				public boolean hasNext() {
 					if (next != null)
 						return true;
@@ -189,6 +191,7 @@
 					return false;
 				}
 
+				@Override
 				public PackWriter next() {
 					if (hasNext()) {
 						PackWriter result = next;
@@ -198,6 +201,7 @@
 					throw new NoSuchElementException();
 				}
 
+				@Override
 				public void remove() {
 					throw new UnsupportedOperationException();
 				}
@@ -213,21 +217,21 @@
 	@SuppressWarnings("unchecked")
 	BlockList<ObjectToPack> objectsLists[] = new BlockList[OBJ_TAG + 1];
 	{
-		objectsLists[OBJ_COMMIT] = new BlockList<ObjectToPack>();
-		objectsLists[OBJ_TREE] = new BlockList<ObjectToPack>();
-		objectsLists[OBJ_BLOB] = new BlockList<ObjectToPack>();
-		objectsLists[OBJ_TAG] = new BlockList<ObjectToPack>();
+		objectsLists[OBJ_COMMIT] = new BlockList<>();
+		objectsLists[OBJ_TREE] = new BlockList<>();
+		objectsLists[OBJ_BLOB] = new BlockList<>();
+		objectsLists[OBJ_TAG] = new BlockList<>();
 	}
 
-	private ObjectIdOwnerMap<ObjectToPack> objectsMap = new ObjectIdOwnerMap<ObjectToPack>();
+	private ObjectIdOwnerMap<ObjectToPack> objectsMap = new ObjectIdOwnerMap<>();
 
 	// edge objects for thin packs
-	private List<ObjectToPack> edgeObjects = new BlockList<ObjectToPack>();
+	private List<ObjectToPack> edgeObjects = new BlockList<>();
 
 	// Objects the client is known to have already.
 	private BitmapBuilder haveObjects;
 
-	private List<CachedPack> cachedPacks = new ArrayList<CachedPack>(2);
+	private List<CachedPack> cachedPacks = new ArrayList<>(2);
 
 	private Set<ObjectId> tagTargets = Collections.emptySet();
 
@@ -355,7 +359,7 @@
 		reuseValidate = true; // be paranoid by default
 		stats = new PackStatistics.Accumulator();
 		state = new MutableState();
-		selfRef = new WeakReference<PackWriter>(this);
+		selfRef = new WeakReference<>(this);
 		instances.put(selfRef, Boolean.TRUE);
 	}
 
@@ -369,7 +373,6 @@
 	 *            the callback to set
 	 *
 	 * @return this object for chaining.
-	 * @since 4.1
 	 */
 	public PackWriter setObjectCountCallback(ObjectCountCallback callback) {
 		this.callback = callback;
@@ -381,11 +384,10 @@
 	 *
 	 * @param clientShallowCommits
 	 *            the shallow commits in the client
-	 * @since 4.1
 	 */
 	public void setClientShallowCommits(Set<ObjectId> clientShallowCommits) {
 		stats.clientShallowCommits = Collections
-				.unmodifiableSet(new HashSet<ObjectId>(clientShallowCommits));
+				.unmodifiableSet(new HashSet<>(clientShallowCommits));
 	}
 
 	/**
@@ -742,21 +744,20 @@
 	 *            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,
 			@NonNull Set<? extends ObjectId> have,
 			@NonNull Set<? extends ObjectId> shallow) throws IOException {
-		ObjectWalk ow;
-		if (shallowPack) {
-			ow = new DepthWalk.ObjectWalk(reader, depth - 1);
-		} else {
-			ow = new ObjectWalk(reader);
+		try (ObjectWalk ow = getObjectWalk()) {
+			ow.assumeShallow(shallow);
+			preparePack(countingMonitor, ow, want, have);
 		}
-		ow.assumeShallow(shallow);
-		preparePack(countingMonitor, ow, want, have);
+	}
+
+	private ObjectWalk getObjectWalk() {
+		return shallowPack ? new DepthWalk.ObjectWalk(reader, depth - 1)
+				: new ObjectWalk(reader);
 	}
 
 	/**
@@ -917,7 +918,7 @@
 			cnt += objectsLists[OBJ_BLOB].size();
 			cnt += objectsLists[OBJ_TAG].size();
 
-			sortedByName = new BlockList<ObjectToPack>(cnt);
+			sortedByName = new BlockList<>(cnt);
 			sortedByName.addAll(objectsLists[OBJ_COMMIT]);
 			sortedByName.addAll(objectsLists[OBJ_TREE]);
 			sortedByName.addAll(objectsLists[OBJ_BLOB]);
@@ -1089,8 +1090,6 @@
 
 	/**
 	 * Release all resources used by this writer.
-	 *
-	 * @since 4.0
 	 */
 	@Override
 	public void close() {
@@ -1113,7 +1112,7 @@
 		beginPhase(PackingPhase.FINDING_SOURCES, monitor, cnt);
 		if (cnt <= 4096) {
 			// For small object counts, do everything as one list.
-			BlockList<ObjectToPack> tmp = new BlockList<ObjectToPack>((int) cnt);
+			BlockList<ObjectToPack> tmp = new BlockList<>((int) cnt);
 			tmp.addAll(objectsLists[OBJ_TAG]);
 			tmp.addAll(objectsLists[OBJ_COMMIT]);
 			tmp.addAll(objectsLists[OBJ_TREE]);
@@ -1259,6 +1258,7 @@
 		// bigger ones, because source files grow and hardly ever shrink.
 		//
 		Arrays.sort(list, 0, cnt, new Comparator<ObjectToPack>() {
+			@Override
 			public int compare(ObjectToPack a, ObjectToPack b) {
 				int cmp = (a.isDoNotDelta() ? 1 : 0)
 						- (b.isDoNotDelta() ? 1 : 0);
@@ -1404,6 +1404,7 @@
 			// can schedule these for us.
 			for (final DeltaTask task : taskBlock.tasks) {
 				executor.execute(new Runnable() {
+					@Override
 					public void run() {
 						try {
 							task.call();
@@ -1446,7 +1447,7 @@
 	private static void runTasks(ExecutorService pool,
 			ThreadSafeProgressMonitor pm,
 			DeltaTask.Block tb, List<Throwable> errors) throws IOException {
-		List<Future<?>> futures = new ArrayList<Future<?>>(tb.tasks.size());
+		List<Future<?>> futures = new ArrayList<>(tb.tasks.size());
 		for (DeltaTask task : tb.tasks)
 			futures.add(pool.submit(task));
 
@@ -1675,7 +1676,7 @@
 			}
 		}
 
-		List<ObjectId> all = new ArrayList<ObjectId>(want.size() + have.size());
+		List<ObjectId> all = new ArrayList<>(want.size() + have.size());
 		all.addAll(want);
 		all.addAll(have);
 
@@ -1693,9 +1694,9 @@
 				walker.sort(RevSort.BOUNDARY, true);
 		}
 
-		List<RevObject> wantObjs = new ArrayList<RevObject>(want.size());
-		List<RevObject> haveObjs = new ArrayList<RevObject>(haveEst);
-		List<RevTag> wantTags = new ArrayList<RevTag>(want.size());
+		List<RevObject> wantObjs = new ArrayList<>(want.size());
+		List<RevObject> haveObjs = new ArrayList<>(haveEst);
+		List<RevTag> wantTags = new ArrayList<>(want.size());
 
 		// Retrieve the RevWalk's versions of "want" and "have" objects to
 		// maintain any state previously set in the RevWalk.
@@ -1726,7 +1727,7 @@
 		}
 
 		if (!wantTags.isEmpty()) {
-			all = new ArrayList<ObjectId>(wantTags.size());
+			all = new ArrayList<>(wantTags.size());
 			for (RevTag tag : wantTags)
 				all.add(tag.getObject());
 			q = walker.parseAny(all, true);
@@ -1769,8 +1770,8 @@
 			walker.markUninteresting(obj);
 
 		final int maxBases = config.getDeltaSearchWindowSize();
-		Set<RevTree> baseTrees = new HashSet<RevTree>();
-		BlockList<RevCommit> commits = new BlockList<RevCommit>();
+		Set<RevTree> baseTrees = new HashSet<>();
+		BlockList<RevCommit> commits = new BlockList<>();
 		Set<ObjectId> roots = new HashSet<>();
 		RevCommit c;
 		while ((c = walker.next()) != null) {
@@ -2258,8 +2259,6 @@
 		 * @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/internal/storage/pack/PackWriterBitmapPreparer.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
index 77311ab..07a03b4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
@@ -91,6 +91,7 @@
 	private static final int DAY_IN_SECONDS = 24 * 60 * 60;
 
 	private static final Comparator<BitmapBuilderEntry> ORDER_BY_CARDINALITY = new Comparator<BitmapBuilderEntry>() {
+		@Override
 		public int compare(BitmapBuilderEntry a, BitmapBuilderEntry b) {
 			return Integer.signum(a.getBuilder().cardinality()
 					- b.getBuilder().cardinality());
@@ -167,7 +168,7 @@
 		pm.endTask();
 
 		int totCommits = selectionHelper.getCommitCount();
-		BlockList<BitmapCommit> selections = new BlockList<BitmapCommit>(
+		BlockList<BitmapCommit> selections = new BlockList<>(
 				totCommits / recentCommitSpan + 1);
 		for (BitmapCommit reuse : selectionHelper.reusedCommits) {
 			selections.add(reuse);
@@ -194,7 +195,7 @@
 			// better compression/on the run-length encoding of the XORs between
 			// them.
 			List<List<BitmapCommit>> chains =
-					new ArrayList<List<BitmapCommit>>();
+					new ArrayList<>();
 
 			// Mark the current branch as inactive if its tip commit isn't
 			// recent and there are an excessive number of branches, to
@@ -286,7 +287,7 @@
 				}
 
 				if (longestAncestorChain == null) {
-					longestAncestorChain = new ArrayList<BitmapCommit>();
+					longestAncestorChain = new ArrayList<>();
 					chains.add(longestAncestorChain);
 				}
 				longestAncestorChain.add(new BitmapCommit(
@@ -375,7 +376,7 @@
 			int expectedCommitCount) throws IncorrectObjectTypeException,
 					IOException, MissingObjectException {
 		BitmapBuilder reuse = commitBitmapIndex.newBitmapBuilder();
-		List<BitmapCommit> reuseCommits = new ArrayList<BitmapCommit>();
+		List<BitmapCommit> reuseCommits = new ArrayList<>();
 		for (PackBitmapIndexRemapper.Entry entry : bitmapRemapper) {
 			// More recent commits did not have the reuse flag set, so skip them
 			if ((entry.getFlags() & FLAG_REUSE) != FLAG_REUSE) {
@@ -397,9 +398,9 @@
 
 		// Add branch tips that are not represented in old bitmap indices. Set
 		// up the RevWalk to walk the new commits not in the old packs.
-		List<BitmapBuilderEntry> tipCommitBitmaps = new ArrayList<BitmapBuilderEntry>(
+		List<BitmapBuilderEntry> tipCommitBitmaps = new ArrayList<>(
 				want.size());
-		Set<RevCommit> peeledWant = new HashSet<RevCommit>(want.size());
+		Set<RevCommit> peeledWant = new HashSet<>(want.size());
 		for (AnyObjectId objectId : want) {
 			RevObject ro = rw.peel(rw.parseAny(objectId));
 			if (!(ro instanceof RevCommit) || reuse.contains(ro)) {
@@ -579,20 +580,24 @@
 			this.reusedCommits = reuse;
 		}
 
+		@Override
 		public Iterator<RevCommit> iterator() {
 			// Member variables referenced by this iterator will have synthetic
 			// accessors generated for them if they are made private.
 			return new Iterator<RevCommit>() {
 				int pos = commitStartPos;
 
+				@Override
 				public boolean hasNext() {
 					return pos < commitsByOldest.length;
 				}
 
+				@Override
 				public RevCommit next() {
 					return commitsByOldest[pos++];
 				}
 
+				@Override
 				public void remove() {
 					throw new UnsupportedOperationException();
 				}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
index 11081d5..f964bf2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
@@ -159,6 +159,7 @@
 	 * @return &lt; 0 if this id comes before other; 0 if this id is equal to
 	 *         other; &gt; 0 if this id comes after other.
 	 */
+	@Override
 	public final int compareTo(final AnyObjectId other) {
 		if (this == other)
 			return 0;
@@ -261,6 +262,7 @@
 		return abbr.prefixCompare(this) == 0;
 	}
 
+	@Override
 	public final int hashCode() {
 		return w2;
 	}
@@ -276,6 +278,7 @@
 		return other != null ? equals(this, other) : false;
 	}
 
+	@Override
 	public final boolean equals(final Object o) {
 		if (o instanceof AnyObjectId)
 			return equals((AnyObjectId) o);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
index 670f9a9..de1003b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
@@ -218,7 +218,7 @@
 	public B addAlternateObjectDirectory(File other) {
 		if (other != null) {
 			if (alternateObjectDirectories == null)
-				alternateObjectDirectories = new LinkedList<File>();
+				alternateObjectDirectories = new LinkedList<>();
 			alternateObjectDirectories.add(other);
 		}
 		return self();
@@ -429,7 +429,7 @@
 	public B addCeilingDirectory(File root) {
 		if (root != null) {
 			if (ceilingDirectories == null)
-				ceilingDirectories = new LinkedList<File>();
+				ceilingDirectories = new LinkedList<>();
 			ceilingDirectories.add(root);
 		}
 		return self();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
index 653c9f6..3f6995d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
@@ -120,7 +120,7 @@
 	 */
 	protected BatchRefUpdate(RefDatabase refdb) {
 		this.refdb = refdb;
-		this.commands = new ArrayList<ReceiveCommand>();
+		this.commands = new ArrayList<>();
 		this.atomic = refdb.performsAtomicTransactions();
 	}
 
@@ -400,7 +400,7 @@
 		}
 
 		monitor.beginTask(JGitText.get().updatingReferences, commands.size());
-		List<ReceiveCommand> commands2 = new ArrayList<ReceiveCommand>(
+		List<ReceiveCommand> commands2 = new ArrayList<>(
 				commands.size());
 		// First delete refs. This may free the name space for some of the
 		// updates.
@@ -431,7 +431,7 @@
 		}
 		if (!commands2.isEmpty()) {
 			// What part of the name space is already taken
-			Collection<String> takenNames = new HashSet<String>(refdb.getRefs(
+			Collection<String> takenNames = new HashSet<>(refdb.getRefs(
 					RefDatabase.ALL).keySet());
 			Collection<String> takenPrefixes = getTakenPrefixes(takenNames);
 
@@ -525,7 +525,7 @@
 
 	private static Collection<String> getTakenPrefixes(
 			final Collection<String> names) {
-		Collection<String> ref = new HashSet<String>();
+		Collection<String> ref = new HashSet<>();
 		for (String name : names)
 			ref.addAll(getPrefixes(name));
 		return ref;
@@ -539,7 +539,7 @@
 	}
 
 	static Collection<String> getPrefixes(String s) {
-		Collection<String> ret = new HashSet<String>();
+		Collection<String> ret = new HashSet<>();
 		int p1 = s.indexOf('/');
 		while (p1 > 0) {
 			ret.add(s.substring(0, p1));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
index a3859ab..54c8052 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
@@ -68,10 +68,12 @@
 		delayStartUnit = unit;
 	}
 
+	@Override
 	public void start(int totalTasks) {
 		// Ignore the number of tasks.
 	}
 
+	@Override
 	public void beginTask(String title, int work) {
 		endTask();
 		task = new Task(title, work);
@@ -79,11 +81,13 @@
 			task.delay(delayStartTime, delayStartUnit);
 	}
 
+	@Override
 	public void update(int completed) {
 		if (task != null)
 			task.update(this, completed);
 	}
 
+	@Override
 	public void endTask() {
 		if (task != null) {
 			task.end(this);
@@ -91,6 +95,7 @@
 		}
 	}
 
+	@Override
 	public boolean isCancelled() {
 		return false;
 	}
@@ -178,6 +183,7 @@
 			timerFuture = WorkQueue.getExecutor().schedule(this, time, unit);
 		}
 
+		@Override
 		public void run() {
 			display = true;
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
index 9ddff25..00f42a4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
@@ -108,6 +108,7 @@
 		 *
 		 * @return an Iterator.
 		 */
+		@Override
 		Iterator<BitmapObject> iterator();
 	}
 
@@ -166,6 +167,7 @@
 		 *            the other bitmap
 		 * @return the current builder.
 		 */
+		@Override
 		BitmapBuilder or(Bitmap other);
 
 		/**
@@ -176,6 +178,7 @@
 		 *            the other bitmap
 		 * @return the current builder.
 		 */
+		@Override
 		BitmapBuilder andNot(Bitmap other);
 
 		/**
@@ -185,6 +188,7 @@
 		 *            the other bitmap
 		 * @return the current builder.
 		 */
+		@Override
 		BitmapBuilder xor(Bitmap other);
 
 		/** @return the fully built immutable bitmap */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
index 8fe8a96..f45c71c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -58,6 +58,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -121,7 +122,7 @@
 	 */
 	public Config(Config defaultConfig) {
 		baseConfig = defaultConfig;
-		state = new AtomicReference<ConfigSnapshot>(newState());
+		state = new AtomicReference<>(newState());
 	}
 
 	/**
@@ -895,7 +896,7 @@
 		if (value instanceof ConfigEnum)
 			n = ((ConfigEnum) value).toConfigValue();
 		else
-			n = value.name().toLowerCase().replace('_', ' ');
+			n = value.name().toLowerCase(Locale.ROOT).replace('_', ' ');
 		setString(section, subsection, name, n);
 	}
 
@@ -959,7 +960,7 @@
 			final String section,
 			final String subsection) {
 		final int max = srcState.entryList.size();
-		final ArrayList<ConfigLine> r = new ArrayList<ConfigLine>(max);
+		final ArrayList<ConfigLine> r = new ArrayList<>(max);
 
 		boolean lastWasMatch = false;
 		for (ConfigLine e : srcState.entryList) {
@@ -1074,7 +1075,7 @@
 		// for a new section header. Assume that and allocate the space.
 		//
 		final int max = src.entryList.size() + values.size() + 1;
-		final ArrayList<ConfigLine> r = new ArrayList<ConfigLine>(max);
+		final ArrayList<ConfigLine> r = new ArrayList<>(max);
 		r.addAll(src.entryList);
 		return r;
 	}
@@ -1162,7 +1163,7 @@
 			throw new ConfigInvalidException(
 					JGitText.get().tooManyIncludeRecursions);
 		}
-		final List<ConfigLine> newEntries = new ArrayList<ConfigLine>();
+		final List<ConfigLine> newEntries = new ArrayList<>();
 		final StringReader in = new StringReader(text);
 		ConfigLine last = null;
 		ConfigLine e = new ConfigLine();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index cde41c2..189361b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -291,6 +291,20 @@
 	public static final String CONFIG_KEY_PRUNEPACKEXPIRE = "prunepackexpire";
 
 	/**
+	 * The "logexpiry" key
+	 *
+	 * @since 4.7
+	 */
+	public static final String CONFIG_KEY_LOGEXPIRY = "logExpiry";
+
+	/**
+	 * The "autodetach" key
+	 *
+	 * @since 4.7
+	 */
+	public static final String CONFIG_KEY_AUTODETACH = "autoDetach";
+
+	/**
 	 * The "aggressiveDepth" key
 	 * @since 3.6
 	 */
@@ -374,4 +388,16 @@
 	 * @since 4.6
 	 */
 	public static final String CONFIG_KEY_USEJGITBUILTIN = "useJGitBuiltin";
+
+	/**
+	 * The "fetchRecurseSubmodules" key
+	 * @since 4.7
+	 */
+	public static final String CONFIG_KEY_FETCH_RECURSE_SUBMODULES = "fetchRecurseSubmodules";
+
+	/**
+	 * The "recurseSubmodules" key
+	 * @since 4.7
+	 */
+	public static final String CONFIG_KEY_RECURSE_SUBMODULES = "recurseSubmodules";
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigSnapshot.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigSnapshot.java
index 5ed129e..f5ada13 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigSnapshot.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigSnapshot.java
@@ -78,7 +78,7 @@
 
 	ConfigSnapshot(List<ConfigLine> entries, ConfigSnapshot base) {
 		entryList = entries;
-		cache = new ConcurrentHashMap<Object, Object>(16, 0.75f, 1);
+		cache = new ConcurrentHashMap<>(16, 0.75f, 1);
 		baseState = base;
 	}
 
@@ -112,7 +112,7 @@
 		if (idx < 0)
 			idx = -(idx + 1);
 
-		Map<String, String> m = new LinkedHashMap<String, String>();
+		Map<String, String> m = new LinkedHashMap<>();
 		while (idx < s.size()) {
 			ConfigLine e = s.get(idx++);
 			if (!e.match(section, subsection))
@@ -187,7 +187,7 @@
 	}
 
 	private static List<ConfigLine> sort(List<ConfigLine> in) {
-		List<ConfigLine> sorted = new ArrayList<ConfigLine>(in.size());
+		List<ConfigLine> sorted = new ArrayList<>(in.size());
 		for (ConfigLine line : in) {
 			if (line.section != null && line.name != null)
 				sorted.add(line);
@@ -217,6 +217,7 @@
 	}
 
 	private static class LineComparator implements Comparator<ConfigLine> {
+		@Override
 		public int compare(ConfigLine a, ConfigLine b) {
 			return compare2(
 					a.section, a.subsection, a.name,
@@ -236,8 +237,8 @@
 		final Map<String, Set<String>> subsections;
 
 		SectionNames(ConfigSnapshot cfg) {
-			Map<String, String> sec = new LinkedHashMap<String, String>();
-			Map<String, Set<String>> sub = new HashMap<String, Set<String>>();
+			Map<String, String> sec = new LinkedHashMap<>();
+			Map<String, Set<String>> sub = new HashMap<>();
 			while (cfg != null) {
 				for (ConfigLine e : cfg.entryList) {
 					if (e.section == null)
@@ -252,7 +253,7 @@
 
 					Set<String> m = sub.get(l1);
 					if (m == null) {
-						m = new LinkedHashSet<String>();
+						m = new LinkedHashSet<>();
 						sub.put(l1, m);
 					}
 					m.add(e.subsection);
@@ -286,14 +287,17 @@
 		public Iterator<String> iterator() {
 			final Iterator<String> i = names.values().iterator();
 			return new Iterator<String>() {
+				@Override
 				public boolean hasNext() {
 					return i.hasNext();
 				}
 
+				@Override
 				public String next() {
 					return i.next();
 				}
 
+				@Override
 				public void remove() {
 					throw new UnsupportedOperationException();
 				}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
index 83efd43..40aba63 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
@@ -58,6 +58,7 @@
 public class CoreConfig {
 	/** Key for {@link Config#get(SectionParser)}. */
 	public static final Config.SectionParser<CoreConfig> KEY = new SectionParser<CoreConfig>() {
+		@Override
 		public CoreConfig parse(final Config cfg) {
 			return new CoreConfig(cfg);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/EmptyProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/EmptyProgressMonitor.java
index eabcbbf..c236c35 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/EmptyProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/EmptyProgressMonitor.java
@@ -51,22 +51,27 @@
  */
 public abstract class EmptyProgressMonitor implements ProgressMonitor {
 
+	@Override
 	public void start(int totalTasks) {
 		// empty
 	}
 
+	@Override
 	public void beginTask(String title, int totalWork) {
 		// empty
 	}
 
+	@Override
 	public void update(int completed) {
 		// empty
 	}
 
+	@Override
 	public void endTask() {
 		// empty
 	}
 
+	@Override
 	public boolean isCancelled() {
 		return false;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java
index f295f5b..a489461 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java
@@ -86,6 +86,7 @@
 	@SuppressWarnings("synthetic-access")
 	public static final FileMode TREE = new FileMode(TYPE_TREE,
 			Constants.OBJ_TREE) {
+		@Override
 		public boolean equals(final int modeBits) {
 			return (modeBits & TYPE_MASK) == TYPE_TREE;
 		}
@@ -95,6 +96,7 @@
 	@SuppressWarnings("synthetic-access")
 	public static final FileMode SYMLINK = new FileMode(TYPE_SYMLINK,
 			Constants.OBJ_BLOB) {
+		@Override
 		public boolean equals(final int modeBits) {
 			return (modeBits & TYPE_MASK) == TYPE_SYMLINK;
 		}
@@ -104,6 +106,7 @@
 	@SuppressWarnings("synthetic-access")
 	public static final FileMode REGULAR_FILE = new FileMode(0100644,
 			Constants.OBJ_BLOB) {
+		@Override
 		public boolean equals(final int modeBits) {
 			return (modeBits & TYPE_MASK) == TYPE_FILE && (modeBits & 0111) == 0;
 		}
@@ -113,6 +116,7 @@
 	@SuppressWarnings("synthetic-access")
 	public static final FileMode EXECUTABLE_FILE = new FileMode(0100755,
 			Constants.OBJ_BLOB) {
+		@Override
 		public boolean equals(final int modeBits) {
 			return (modeBits & TYPE_MASK) == TYPE_FILE && (modeBits & 0111) != 0;
 		}
@@ -122,6 +126,7 @@
 	@SuppressWarnings("synthetic-access")
 	public static final FileMode GITLINK = new FileMode(TYPE_GITLINK,
 			Constants.OBJ_COMMIT) {
+		@Override
 		public boolean equals(final int modeBits) {
 			return (modeBits & TYPE_MASK) == TYPE_GITLINK;
 		}
@@ -131,6 +136,7 @@
 	@SuppressWarnings("synthetic-access")
 	public static final FileMode MISSING = new FileMode(TYPE_MISSING,
 			Constants.OBJ_BAD) {
+		@Override
 		public boolean equals(final int modeBits) {
 			return modeBits == 0;
 		}
@@ -258,6 +264,7 @@
 	}
 
 	/** Format this mode as an octal string (for debugging only). */
+	@Override
 	public String toString() {
 		return Integer.toOctalString(modeBits);
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
index af6a4fb..e544b72 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
@@ -253,19 +253,19 @@
 
 	private final WorkingTreeIterator initialWorkingTreeIterator;
 
-	private Set<String> added = new HashSet<String>();
+	private Set<String> added = new HashSet<>();
 
-	private Set<String> changed = new HashSet<String>();
+	private Set<String> changed = new HashSet<>();
 
-	private Set<String> removed = new HashSet<String>();
+	private Set<String> removed = new HashSet<>();
 
-	private Set<String> missing = new HashSet<String>();
+	private Set<String> missing = new HashSet<>();
 
-	private Set<String> modified = new HashSet<String>();
+	private Set<String> modified = new HashSet<>();
 
-	private Set<String> untracked = new HashSet<String>();
+	private Set<String> untracked = new HashSet<>();
 
-	private Map<String, StageState> conflicts = new HashMap<String, StageState>();
+	private Map<String, StageState> conflicts = new HashMap<>();
 
 	private Set<String> ignored;
 
@@ -275,11 +275,11 @@
 
 	private IndexDiffFilter indexDiffFilter;
 
-	private Map<String, IndexDiff> submoduleIndexDiffs = new HashMap<String, IndexDiff>();
+	private Map<String, IndexDiff> submoduleIndexDiffs = new HashMap<>();
 
 	private IgnoreSubmoduleMode ignoreSubmoduleMode = null;
 
-	private Map<FileMode, Set<String>> fileModes = new HashMap<FileMode, Set<String>>();
+	private Map<FileMode, Set<String>> fileModes = new HashMap<>();
 
 	/**
 	 * Construct an IndexDiff
@@ -342,6 +342,7 @@
 	}
 
 	private WorkingTreeIteratorFactory wTreeIt = new WorkingTreeIteratorFactory() {
+		@Override
 		public WorkingTreeIterator getWorkingTreeIterator(Repository repo) {
 			return new FileTreeIterator(repo);
 		}
@@ -416,7 +417,7 @@
 			treeWalk.addTree(new DirCacheIterator(dirCache));
 			treeWalk.addTree(initialWorkingTreeIterator);
 			initialWorkingTreeIterator.setDirCacheIterator(treeWalk, 1);
-			Collection<TreeFilter> filters = new ArrayList<TreeFilter>(4);
+			Collection<TreeFilter> filters = new ArrayList<>(4);
 
 			if (monitor != null) {
 				// Get the maximum size of the work tree and index
@@ -517,7 +518,7 @@
 					String path = treeWalk.getPathString();
 					if (path != null) {
 						if (values == null)
-							values = new HashSet<String>();
+							values = new HashSet<>();
 						values.add(path);
 						fileModes.put(treeWalk.getFileMode(i), values);
 					}
@@ -686,7 +687,7 @@
 	 */
 	public Set<String> getAssumeUnchanged() {
 		if (assumeUnchanged == null) {
-			HashSet<String> unchanged = new HashSet<String>();
+			HashSet<String> unchanged = new HashSet<>();
 			for (int i = 0; i < dirCache.getEntryCount(); i++)
 				if (dirCache.getEntry(i).isAssumeValid())
 					unchanged.add(dirCache.getEntry(i).getPathString());
@@ -700,7 +701,7 @@
 	 */
 	public Set<String> getUntrackedFolders() {
 		return ((indexDiffFilter == null) ? Collections.<String> emptySet()
-				: new HashSet<String>(indexDiffFilter.getUntrackedFolders()));
+				: new HashSet<>(indexDiffFilter.getUntrackedFolders()));
 	}
 
 	/**
@@ -726,7 +727,7 @@
 	public Set<String> getPathsWithIndexMode(final FileMode mode) {
 		Set<String> paths = fileModes.get(mode);
 		if (paths == null)
-			paths = new HashSet<String>();
+			paths = new HashSet<>();
 		return paths;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/MutableObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/MutableObjectId.java
index 1a49ae9..4b14d12 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/MutableObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/MutableObjectId.java
@@ -209,6 +209,24 @@
 	}
 
 	/**
+	 * Convert an ObjectId from binary representation expressed in integers.
+	 *
+	 * @param a
+	 * @param b
+	 * @param c
+	 * @param d
+	 * @param e
+	 * @since 4.7
+	 */
+	public void set(int a, int b, int c, int d, int e) {
+		w1 = a;
+		w2 = b;
+		w3 = c;
+		w4 = d;
+		w5 = e;
+	}
+
+	/**
 	 * Convert an ObjectId from hex characters (US-ASCII).
 	 *
 	 * @param buf
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/NullProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/NullProgressMonitor.java
index d05c8c6..497beb0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/NullProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/NullProgressMonitor.java
@@ -56,22 +56,27 @@
 		// Do not let others instantiate
 	}
 
+	@Override
 	public void start(int totalTasks) {
 		// Do not report.
 	}
 
+	@Override
 	public void beginTask(String title, int totalWork) {
 		// Do not report.
 	}
 
+	@Override
 	public void update(int completed) {
 		// Do not report.
 	}
 
+	@Override
 	public boolean isCancelled() {
 		return false;
 	}
 
+	@Override
 	public void endTask() {
 		// Do not report.
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java
index feecbd8..9d3aee1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java
@@ -611,7 +611,7 @@
 		int ptr = 0;
 		int lastNameB = 0, lastNameE = 0, lastMode = 0;
 		Set<String> normalized = windows || macosx
-				? new HashSet<String>()
+				? new HashSet<>()
 				: null;
 
 		while (ptr < sz) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
index 2a2d67d..991f03f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
@@ -248,8 +248,17 @@
 		}
 	}
 
-	ObjectId(final int new_1, final int new_2, final int new_3,
-			final int new_4, final int new_5) {
+	/**
+	 * Construct an ObjectId from 160 bits provided in 5 words.
+	 *
+	 * @param new_1
+	 * @param new_2
+	 * @param new_3
+	 * @param new_4
+	 * @param new_5
+	 * @since 4.7
+	 */
+	public ObjectId(int new_1, int new_2, int new_3, int new_4, int new_5) {
 		w1 = new_1;
 		w2 = new_2;
 		w3 = new_3;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java
index c286f5e..636716b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java
@@ -67,11 +67,13 @@
 			super(st, name, id);
 		}
 
+		@Override
 		@Nullable
 		public ObjectId getPeeledObjectId() {
 			return null;
 		}
 
+		@Override
 		public boolean isPeeled() {
 			return false;
 		}
@@ -99,11 +101,13 @@
 			peeledObjectId = p;
 		}
 
+		@Override
 		@NonNull
 		public ObjectId getPeeledObjectId() {
 			return peeledObjectId;
 		}
 
+		@Override
 		public boolean isPeeled() {
 			return true;
 		}
@@ -127,11 +131,13 @@
 			super(st, name, id);
 		}
 
+		@Override
 		@Nullable
 		public ObjectId getPeeledObjectId() {
 			return null;
 		}
 
+		@Override
 		public boolean isPeeled() {
 			return true;
 		}
@@ -161,30 +167,36 @@
 		this.objectId = id;
 	}
 
+	@Override
 	@NonNull
 	public String getName() {
 		return name;
 	}
 
+	@Override
 	public boolean isSymbolic() {
 		return false;
 	}
 
+	@Override
 	@NonNull
 	public Ref getLeaf() {
 		return this;
 	}
 
+	@Override
 	@NonNull
 	public Ref getTarget() {
 		return this;
 	}
 
+	@Override
 	@Nullable
 	public ObjectId getObjectId() {
 		return objectId;
 	}
 
+	@Override
 	@NonNull
 	public Storage getStorage() {
 		return storage;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSubclassMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSubclassMap.java
index faed64b..43fc7bf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSubclassMap.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSubclassMap.java
@@ -111,6 +111,7 @@
 	 *            object to find.
 	 * @return true if the mapping exists for this object; false otherwise.
 	 */
+	@Override
 	public boolean contains(final AnyObjectId toFind) {
 		return get(toFind) != null;
 	}
@@ -187,16 +188,19 @@
 		return size == 0;
 	}
 
+	@Override
 	public Iterator<V> iterator() {
 		return new Iterator<V>() {
 			private int found;
 
 			private int i;
 
+			@Override
 			public boolean hasNext() {
 				return found < size;
 			}
 
+			@Override
 			public V next() {
 				while (i < table.length) {
 					final V v = table[i++];
@@ -208,6 +212,7 @@
 				throw new NoSuchElementException();
 			}
 
+			@Override
 			public void remove() {
 				throw new UnsupportedOperationException();
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java
index 4c51279..857ec9b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java
@@ -50,10 +50,10 @@
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.security.MessageDigest;
 
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.transport.PackParser;
+import org.eclipse.jgit.util.sha1.SHA1;
 
 /**
  * Inserts objects into an existing {@code ObjectDatabase}.
@@ -107,41 +107,50 @@
 			return delegate().buffer();
 		}
 
+		@Override
 		public ObjectId idFor(int type, byte[] data) {
 			return delegate().idFor(type, data);
 		}
 
+		@Override
 		public ObjectId idFor(int type, byte[] data, int off, int len) {
 			return delegate().idFor(type, data, off, len);
 		}
 
+		@Override
 		public ObjectId idFor(int objectType, long length, InputStream in)
 				throws IOException {
 			return delegate().idFor(objectType, length, in);
 		}
 
+		@Override
 		public ObjectId idFor(TreeFormatter formatter) {
 			return delegate().idFor(formatter);
 		}
 
+		@Override
 		public ObjectId insert(int type, byte[] data) throws IOException {
 			return delegate().insert(type, data);
 		}
 
+		@Override
 		public ObjectId insert(int type, byte[] data, int off, int len)
 				throws IOException {
 			return delegate().insert(type, data, off, len);
 		}
 
+		@Override
 		public ObjectId insert(int objectType, long length, InputStream in)
 				throws IOException {
 			return delegate().insert(objectType, length, in);
 		}
 
+		@Override
 		public PackParser newPackParser(InputStream in) throws IOException {
 			return delegate().newPackParser(in);
 		}
 
+		@Override
 		public ObjectReader newReader() {
 			final ObjectReader dr = delegate().newReader();
 			return new ObjectReader.Filter() {
@@ -157,24 +166,24 @@
 			};
 		}
 
+		@Override
 		public void flush() throws IOException {
 			delegate().flush();
 		}
 
+		@Override
 		public void close() {
 			delegate().close();
 		}
 	}
 
-	/** Digest to compute the name of an object. */
-	private final MessageDigest digest;
+	private final SHA1 hasher = SHA1.newInstance();
 
 	/** Temporary working buffer for streaming data through. */
 	private byte[] tempBuffer;
 
 	/** Create a new inserter for a database. */
 	protected ObjectInserter() {
-		digest = Constants.newMessageDigest();
 	}
 
 	/**
@@ -208,10 +217,12 @@
 		return b;
 	}
 
-	/** @return digest to help compute an ObjectId */
-	protected MessageDigest digest() {
-		digest.reset();
-		return digest;
+	/**
+	 * @return digest to help compute an ObjectId
+	 * @since 4.7
+	 */
+	protected SHA1 digest() {
+		return hasher.reset();
 	}
 
 	/**
@@ -241,13 +252,13 @@
 	 * @return the name of the object.
 	 */
 	public ObjectId idFor(int type, byte[] data, int off, int len) {
-		MessageDigest md = digest();
+		SHA1 md = SHA1.newInstance();
 		md.update(Constants.encodedTypeString(type));
 		md.update((byte) ' ');
 		md.update(Constants.encodeASCII(len));
 		md.update((byte) 0);
 		md.update(data, off, len);
-		return ObjectId.fromRaw(md.digest());
+		return md.toObjectId();
 	}
 
 	/**
@@ -266,7 +277,7 @@
 	 */
 	public ObjectId idFor(int objectType, long length, InputStream in)
 			throws IOException {
-		MessageDigest md = digest();
+		SHA1 md = SHA1.newInstance();
 		md.update(Constants.encodedTypeString(objectType));
 		md.update((byte) ' ');
 		md.update(Constants.encodeASCII(length));
@@ -279,7 +290,7 @@
 			md.update(buf, 0, n);
 			length -= n;
 		}
-		return ObjectId.fromRaw(md.digest());
+		return md.toObjectId();
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
index 372da98..f39f291 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
@@ -134,7 +134,7 @@
 		Collection<ObjectId> matches = resolve(abbrev);
 		while (1 < matches.size() && len < Constants.OBJECT_ID_STRING_LENGTH) {
 			abbrev = objectId.abbreviate(++len);
-			List<ObjectId> n = new ArrayList<ObjectId>(8);
+			List<ObjectId> n = new ArrayList<>(8);
 			for (ObjectId candidate : matches) {
 				if (abbrev.prefixCompare(candidate) == 0)
 					n.add(candidate);
@@ -286,6 +286,7 @@
 		return new AsyncObjectLoaderQueue<T>() {
 			private T cur;
 
+			@Override
 			public boolean next() throws MissingObjectException, IOException {
 				if (idItr.hasNext()) {
 					cur = idItr.next();
@@ -295,22 +296,27 @@
 				}
 			}
 
+			@Override
 			public T getCurrent() {
 				return cur;
 			}
 
+			@Override
 			public ObjectId getObjectId() {
 				return cur;
 			}
 
+			@Override
 			public ObjectLoader open() throws IOException {
 				return ObjectReader.this.open(cur, OBJ_ANY);
 			}
 
+			@Override
 			public boolean cancel(boolean mayInterruptIfRunning) {
 				return true;
 			}
 
+			@Override
 			public void release() {
 				// Since we are sequential by default, we don't
 				// have any state to clean up if we terminate early.
@@ -370,6 +376,7 @@
 
 			private long sz;
 
+			@Override
 			public boolean next() throws MissingObjectException, IOException {
 				if (idItr.hasNext()) {
 					cur = idItr.next();
@@ -380,22 +387,27 @@
 				}
 			}
 
+			@Override
 			public T getCurrent() {
 				return cur;
 			}
 
+			@Override
 			public ObjectId getObjectId() {
 				return cur;
 			}
 
+			@Override
 			public long getSize() {
 				return sz;
 			}
 
+			@Override
 			public boolean cancel(boolean mayInterruptIfRunning) {
 				return true;
 			}
 
+			@Override
 			public void release() {
 				// Since we are sequential by default, we don't
 				// have any state to clean up if we terminate early.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
index 627ccaa..45757e4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
@@ -336,6 +336,7 @@
 	/**
 	 * Hashcode is based only on the email address and timestamp.
 	 */
+	@Override
 	public int hashCode() {
 		int hc = getEmailAddress().hashCode();
 		hc *= 31;
@@ -343,6 +344,7 @@
 		return hc;
 	}
 
+	@Override
 	public boolean equals(final Object o) {
 		if (o instanceof PersonIdent) {
 			final PersonIdent p = (PersonIdent) o;
@@ -370,6 +372,7 @@
 		return r.toString();
 	}
 
+	@Override
 	@SuppressWarnings("nls")
 	public String toString() {
 		final StringBuilder r = new StringBuilder();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
index 75a3592..1047a6d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
@@ -89,7 +89,7 @@
 		byte[] buf = IO.readFully(new File(repo.getDirectory(), path));
 		int ptr = 0;
 		int tokenBegin = 0;
-		List<RebaseTodoLine> r = new LinkedList<RebaseTodoLine>();
+		List<RebaseTodoLine> r = new LinkedList<>();
 		while (ptr < buf.length) {
 			tokenBegin = ptr;
 			ptr = RawParseUtils.nextLF(buf, ptr);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefComparator.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefComparator.java
index c6e1029..95e3386 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefComparator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefComparator.java
@@ -60,6 +60,7 @@
 	/** Singleton instance of RefComparator */
 	public static final RefComparator INSTANCE = new RefComparator();
 
+	@Override
 	public int compare(final Ref o1, final Ref o2) {
 		return compareTo(o1, o2);
 	}
@@ -72,7 +73,7 @@
 	 * @return sorted collection of refs
 	 */
 	public static Collection<Ref> sort(final Collection<Ref> refs) {
-		final List<Ref> r = new ArrayList<Ref>(refs);
+		final List<Ref> r = new ArrayList<>(refs);
 		Collections.sort(r, INSTANCE);
 		return r;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
index 517c8aa..59a104b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -150,7 +150,7 @@
 			lastSlash = name.lastIndexOf('/', lastSlash - 1);
 		}
 
-		List<String> conflicting = new ArrayList<String>();
+		List<String> conflicting = new ArrayList<>();
 		// Cannot be the container of an existing reference.
 		String prefix = name + '/';
 		for (String existing : allRefs.keySet())
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..ea37e79 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
@@ -65,6 +66,7 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Pattern;
 
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.annotations.Nullable;
@@ -110,6 +112,17 @@
 	private static final Logger LOG = LoggerFactory.getLogger(Repository.class);
 	private static final ListenerList globalListeners = new ListenerList();
 
+	/**
+	 * Branch names containing slashes should not have a name component that is
+	 * one of the reserved device names on Windows.
+	 *
+	 * @see #normalizeBranchName(String)
+	 */
+	private static final Pattern FORBIDDEN_BRANCH_NAME_COMPONENTS = Pattern
+			.compile(
+					"(^|/)(aux|com[1-9]|con|lpt[1-9]|nul|prn)(\\.[^/]*)?", //$NON-NLS-1$
+					Pattern.CASE_INSENSITIVE);
+
 	/** @return the global listener list observing all events in this JVM. */
 	public static ListenerList getGlobalListenerList() {
 		return globalListeners;
@@ -888,11 +901,12 @@
 		} else if (newCount == -1) {
 			// should not happen, only log when useCnt became negative to
 			// minimize number of log entries
+			String message = MessageFormat.format(JGitText.get().corruptUseCnt,
+					toString());
 			if (LOG.isDebugEnabled()) {
-				IllegalStateException e = new IllegalStateException();
-				LOG.debug(JGitText.get().corruptUseCnt, e);
+				LOG.debug(message, new IllegalStateException());
 			} else {
-				LOG.warn(JGitText.get().corruptUseCnt);
+				LOG.warn(message);
 			}
 			if (RepositoryCache.isCached(this)) {
 				closedAt.set(System.currentTimeMillis());
@@ -910,7 +924,6 @@
 		getRefDatabase().close();
 	}
 
-	@SuppressWarnings("nls")
 	@Override
 	@NonNull
 	public String toString() {
@@ -921,7 +934,7 @@
 		else
 			desc = getClass().getSimpleName() + "-" //$NON-NLS-1$
 					+ System.identityHashCode(this);
-		return "Repository[" + desc + "]"; //$NON-NLS-1$
+		return "Repository[" + desc + "]"; //$NON-NLS-1$ //$NON-NLS-2$
 	}
 
 	/**
@@ -1051,7 +1064,7 @@
 		try {
 			return getRefDatabase().getRefs(RefDatabase.ALL);
 		} catch (IOException e) {
-			return new HashMap<String, Ref>();
+			return new HashMap<>();
 		}
 	}
 
@@ -1065,7 +1078,7 @@
 		try {
 			return getRefDatabase().getRefs(Constants.R_TAGS);
 		} catch (IOException e) {
-			return new HashMap<String, Ref>();
+			return new HashMap<>();
 		}
 	}
 
@@ -1100,7 +1113,7 @@
 	@NonNull
 	public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
 		Map<String, Ref> allRefs = getAllRefs();
-		Map<AnyObjectId, Set<Ref>> ret = new HashMap<AnyObjectId, Set<Ref>>(allRefs.size());
+		Map<AnyObjectId, Set<Ref>> ret = new HashMap<>(allRefs.size());
 		for (Ref ref : allRefs.values()) {
 			ref = peel(ref);
 			AnyObjectId target = ref.getPeeledObjectId();
@@ -1112,7 +1125,7 @@
 				// that was not the case (rare)
 				if (oset.size() == 1) {
 					// Was a read-only singleton, we must copy to a new Set
-					oset = new HashSet<Ref>(oset);
+					oset = new HashSet<>(oset);
 				}
 				ret.put(target, oset);
 				oset.add(ref);
@@ -1331,12 +1344,105 @@
 	}
 
 	/**
+	 * 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 the trimmed input string if this is
+	 * already a valid branch name. Otherwise it returns a trimmed string with
+	 * special characters not allowed by {@link #isValidRefName(String)}
+	 * replaced by hyphens ('-') and blanks replaced by underscores ('_').
+	 * Leading and trailing slashes, dots, hyphens, and underscores are removed.
+	 *
+	 * @param name
+	 *            to normalize
+	 *
+	 * @return The normalized name or an empty String if it is {@code null} or
+	 *         empty.
+	 * @since 4.7
+	 * @see #isValidRefName(String)
+	 */
+	public static String normalizeBranchName(String name) {
+		if (name == null || name.isEmpty()) {
+			return ""; //$NON-NLS-1$
+		}
+		String result = name.trim();
+		String fullName = result.startsWith(Constants.R_HEADS) ? result
+				: Constants.R_HEADS + result;
+		if (isValidRefName(fullName)) {
+			return result;
+		}
+
+		// All Unicode blanks to underscore
+		result = result.replaceAll("(?:\\h|\\v)+", "_"); //$NON-NLS-1$ //$NON-NLS-2$
+		StringBuilder b = new StringBuilder();
+		char p = '/';
+		for (int i = 0, len = result.length(); i < len; i++) {
+			char c = result.charAt(i);
+			if (c < ' ' || c == 127) {
+				continue;
+			}
+			// Substitute a dash for problematic characters
+			switch (c) {
+			case '\\':
+			case '^':
+			case '~':
+			case ':':
+			case '?':
+			case '*':
+			case '[':
+			case '@':
+			case '<':
+			case '>':
+			case '|':
+			case '"':
+				c = '-';
+				break;
+			default:
+				break;
+			}
+			// Collapse multiple slashes, dashes, dots, underscores, and omit
+			// dashes, dots, and underscores following a slash.
+			switch (c) {
+			case '/':
+				if (p == '/') {
+					continue;
+				}
+				p = '/';
+				break;
+			case '.':
+			case '_':
+			case '-':
+				if (p == '/' || p == '-') {
+					continue;
+				}
+				p = '-';
+				break;
+			default:
+				p = c;
+				break;
+			}
+			b.append(c);
+		}
+		// Strip trailing special characters, and avoid the .lock extension
+		result = b.toString().replaceFirst("[/_.-]+$", "") //$NON-NLS-1$ //$NON-NLS-2$
+				.replaceAll("\\.lock($|/)", "_lock$1"); //$NON-NLS-1$ //$NON-NLS-2$
+		return FORBIDDEN_BRANCH_NAME_COMPONENTS.matcher(result)
+				.replaceAll("$1+$2$3"); //$NON-NLS-1$
+	}
+
+	/**
 	 * Strip work dir and return normalized repository path.
 	 *
-	 * @param workDir Work dir
-	 * @param file File whose path shall be stripped of its workdir
-	 * @return normalized repository relative path or the empty
-	 *         string if the file is not relative to the work directory.
+	 * @param workDir
+	 *            Work dir
+	 * @param file
+	 *            File whose path shall be stripped of its workdir
+	 * @return normalized repository relative path or the empty string if the
+	 *         file is not relative to the work directory.
 	 */
 	@NonNull
 	public static String stripWorkDir(File workDir, File file) {
@@ -1574,7 +1680,7 @@
 		if (raw == null)
 			return null;
 
-		LinkedList<ObjectId> heads = new LinkedList<ObjectId>();
+		LinkedList<ObjectId> heads = new LinkedList<>();
 		for (int p = 0; p < raw.length;) {
 			heads.add(ObjectId.fromString(raw, p));
 			p = RawParseUtils
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
index 2f1a9e1..baa5286 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
@@ -300,7 +300,7 @@
 	}
 
 	private Collection<Key> getKeys() {
-		return new ArrayList<Key>(cacheMap.keySet());
+		return new ArrayList<>(cacheMap.keySet());
 	}
 
 	private void clearAllExpired() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SubmoduleConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SubmoduleConfig.java
new file mode 100644
index 0000000..3126160
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SubmoduleConfig.java
@@ -0,0 +1,86 @@
+/*
+ * 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.lib;
+
+/**
+ * Submodule section of a Git configuration file.
+ *
+ * @since 4.7
+ */
+public class SubmoduleConfig {
+
+	/**
+	 * Config values for submodule.[name].fetchRecurseSubmodules.
+	 */
+	public enum FetchRecurseSubmodulesMode implements Config.ConfigEnum {
+		/** Unconditionally recurse into all populated submodules. */
+		YES("true"), //$NON-NLS-1$
+
+		/**
+		 * Only recurse into a populated submodule when the superproject
+		 * retrieves a commit that updates the submodule's reference to a commit
+		 * that isn't already in the local submodule clone.
+		 */
+		ON_DEMAND("on-demand"), //$NON-NLS-1$
+
+		/** Completely disable recursion. */
+		NO("false"); //$NON-NLS-1$
+
+		private final String configValue;
+
+		private FetchRecurseSubmodulesMode(String configValue) {
+			this.configValue = configValue;
+		}
+
+		@Override
+		public String toConfigValue() {
+			return configValue;
+		}
+
+		@Override
+		public boolean matchConfigValue(String s) {
+			return configValue.equals(s);
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java
index eeab921..71d5cd7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java
@@ -70,15 +70,18 @@
 		this.target = target;
 	}
 
+	@Override
 	@NonNull
 	public String getName() {
 		return name;
 	}
 
+	@Override
 	public boolean isSymbolic() {
 		return true;
 	}
 
+	@Override
 	@NonNull
 	public Ref getLeaf() {
 		Ref dst = getTarget();
@@ -87,26 +90,31 @@
 		return dst;
 	}
 
+	@Override
 	@NonNull
 	public Ref getTarget() {
 		return target;
 	}
 
+	@Override
 	@Nullable
 	public ObjectId getObjectId() {
 		return getLeaf().getObjectId();
 	}
 
+	@Override
 	@NonNull
 	public Storage getStorage() {
 		return Storage.LOOSE;
 	}
 
+	@Override
 	@Nullable
 	public ObjectId getPeeledObjectId() {
 		return getLeaf().getPeeledObjectId();
 	}
 
+	@Override
 	public boolean isPeeled() {
 		return getLeaf().isPeeled();
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
index ff85f9b..5824a55 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ThreadSafeProgressMonitor.java
@@ -87,12 +87,14 @@
 		this.process = new Semaphore(0);
 	}
 
+	@Override
 	public void start(int totalTasks) {
 		if (!isMainThread())
 			throw new IllegalStateException();
 		pm.start(totalTasks);
 	}
 
+	@Override
 	public void beginTask(String title, int totalWork) {
 		if (!isMainThread())
 			throw new IllegalStateException();
@@ -156,11 +158,13 @@
 			pm.update(cnt);
 	}
 
+	@Override
 	public void update(int completed) {
 		if (0 == pendingUpdates.getAndAdd(completed))
 			process.release();
 	}
 
+	@Override
 	public boolean isCancelled() {
 		lock.lock();
 		try {
@@ -170,6 +174,7 @@
 		}
 	}
 
+	@Override
 	public void endTask() {
 		if (!isMainThread())
 			throw new IllegalStateException();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/UserConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/UserConfig.java
index b8d236c..bd393dd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/UserConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/UserConfig.java
@@ -52,6 +52,7 @@
 public class UserConfig {
 	/** Key for {@link Config#get(SectionParser)}. */
 	public static final Config.SectionParser<UserConfig> KEY = new SectionParser<UserConfig>() {
+		@Override
 		public UserConfig parse(final Config cfg) {
 			return new UserConfig(cfg);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkQueue.java
index 9735d19..07b87f5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkQueue.java
@@ -67,6 +67,7 @@
 					private final ThreadFactory baseFactory = Executors
 							.defaultThreadFactory();
 
+					@Override
 					public Thread newThread(Runnable taskBody) {
 						Thread thr = baseFactory.newThread(taskBody);
 						thr.setName("JGit-WorkQueue"); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
index 112550a..04c65ef 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeAlgorithm.java
@@ -98,11 +98,11 @@
 	 */
 	public <S extends Sequence> MergeResult<S> merge(
 			SequenceComparator<S> cmp, S base, S ours, S theirs) {
-		List<S> sequences = new ArrayList<S>(3);
+		List<S> sequences = new ArrayList<>(3);
 		sequences.add(base);
 		sequences.add(ours);
 		sequences.add(theirs);
-		MergeResult<S> result = new MergeResult<S>(sequences);
+		MergeResult<S> result = new MergeResult<>(sequences);
 
 		if (ours.size() == 0) {
 			if (theirs.size() != 0) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java
index 83b143b..d059391 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeConfig.java
@@ -167,6 +167,7 @@
 			this.branch = branch;
 		}
 
+		@Override
 		public MergeConfig parse(Config cfg) {
 			return new MergeConfig(branch, cfg);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
index ee6095a..43876a6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
@@ -105,7 +105,7 @@
 	@SuppressWarnings("unchecked")
 	public void formatMerge(OutputStream out, MergeResult res, String baseName,
 			String oursName, String theirsName, String charsetName) throws IOException {
-		List<String> names = new ArrayList<String>(3);
+		List<String> names = new ArrayList<>(3);
 		names.add(baseName);
 		names.add(oursName);
 		names.add(theirsName);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
index 82cbf36..ca0e18a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
@@ -71,11 +71,11 @@
 		StringBuilder sb = new StringBuilder();
 		sb.append("Merge "); //$NON-NLS-1$
 
-		List<String> branches = new ArrayList<String>();
-		List<String> remoteBranches = new ArrayList<String>();
-		List<String> tags = new ArrayList<String>();
-		List<String> commits = new ArrayList<String>();
-		List<String> others = new ArrayList<String>();
+		List<String> branches = new ArrayList<>();
+		List<String> remoteBranches = new ArrayList<>();
+		List<String> tags = new ArrayList<>();
+		List<String> commits = new ArrayList<>();
+		List<String> others = new ArrayList<>();
 		for (Ref ref : refsToMerge) {
 			if (ref.getName().startsWith(Constants.R_HEADS)) {
 				branches.add("'" + Repository.shortenRefName(ref.getName()) //$NON-NLS-1$
@@ -95,7 +95,7 @@
 			}
 		}
 
-		List<String> listings = new ArrayList<String>();
+		List<String> listings = new ArrayList<>();
 
 		if (!branches.isEmpty())
 			listings.add(joinNames(branches, "branch", "branches")); //$NON-NLS-1$//$NON-NLS-2$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java
index 106f9c7..ff3c8ab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java
@@ -133,14 +133,17 @@
 	 * @return an iterator over the MergeChunks. The iterator does not support
 	 * the remove operation
 	 */
+	@Override
 	public Iterator<MergeChunk> iterator() {
 		return new Iterator<MergeChunk>() {
 			int idx;
 
+			@Override
 			public boolean hasNext() {
 				return (idx < chunks.size());
 			}
 
+			@Override
 			public MergeChunk next() {
 				ConflictState state = states[chunks.get(idx++)];
 				int srcIdx = chunks.get(idx++);
@@ -149,6 +152,7 @@
 				return new MergeChunk(srcIdx, begin, end, state);
 			}
 
+			@Override
 			public void remove() {
 				throw new UnsupportedOperationException();
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java
index c5e615e..656480e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java
@@ -80,7 +80,7 @@
 	 */
 	public static final ThreeWayMergeStrategy RECURSIVE = new StrategyRecursive();
 
-	private static final HashMap<String, MergeStrategy> STRATEGIES = new HashMap<String, MergeStrategy>();
+	private static final HashMap<String, MergeStrategy> STRATEGIES = new HashMap<>();
 
 	static {
 		register(OURS);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
index e055644..f8e1998 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
@@ -147,7 +147,7 @@
 	 */
 	protected RevCommit getBaseCommit(RevCommit a, RevCommit b, int callDepth)
 			throws IOException {
-		ArrayList<RevCommit> baseCommits = new ArrayList<RevCommit>();
+		ArrayList<RevCommit> baseCommits = new ArrayList<>();
 		walk.reset();
 		walk.setRevFilter(RevFilter.MERGE_BASE);
 		walk.markStart(a);
@@ -181,7 +181,7 @@
 			dircache = DirCache.read(reader, currentBase.getTree());
 			inCore = true;
 
-			List<RevCommit> parents = new ArrayList<RevCommit>();
+			List<RevCommit> parents = new ArrayList<>();
 			parents.add(currentBase);
 			for (int commitIdx = 1; commitIdx < baseCommits.size(); commitIdx++) {
 				RevCommit nextBase = baseCommits.get(commitIdx);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index fd5e7ef..f667af2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -180,14 +180,14 @@
 	 *
 	 * @since 3.4
 	 */
-	protected List<String> unmergedPaths = new ArrayList<String>();
+	protected List<String> unmergedPaths = new ArrayList<>();
 
 	/**
 	 * Files modified during this merge operation.
 	 *
 	 * @since 3.4
 	 */
-	protected List<String> modifiedFiles = new LinkedList<String>();
+	protected List<String> modifiedFiles = new LinkedList<>();
 
 	/**
 	 * If the merger has nothing to do for a file but check it out at the end of
@@ -195,7 +195,7 @@
 	 *
 	 * @since 3.4
 	 */
-	protected Map<String, DirCacheEntry> toBeCheckedOut = new HashMap<String, DirCacheEntry>();
+	protected Map<String, DirCacheEntry> toBeCheckedOut = new HashMap<>();
 
 	/**
 	 * Paths in this list will be deleted from the local copy at the end of the
@@ -203,7 +203,7 @@
 	 *
 	 * @since 3.4
 	 */
-	protected List<String> toBeDeleted = new ArrayList<String>();
+	protected List<String> toBeDeleted = new ArrayList<>();
 
 	/**
 	 * Low-level textual merge results. Will be passed on to the callers in case
@@ -211,14 +211,14 @@
 	 *
 	 * @since 3.4
 	 */
-	protected Map<String, MergeResult<? extends Sequence>> mergeResults = new HashMap<String, MergeResult<? extends Sequence>>();
+	protected Map<String, MergeResult<? extends Sequence>> mergeResults = new HashMap<>();
 
 	/**
 	 * Paths for which the merge failed altogether.
 	 *
 	 * @since 3.4
 	 */
-	protected Map<String, MergeFailureReason> failingPaths = new HashMap<String, MergeFailureReason>();
+	protected Map<String, MergeFailureReason> failingPaths = new HashMap<>();
 
 	/**
 	 * Updated as we merge entries of the tree walk. Tells us whether we should
@@ -518,7 +518,7 @@
 					unmergedPaths.add(tw.getPathString());
 					mergeResults.put(
 							tw.getPathString(),
-							new MergeResult<RawText>(Collections
+							new MergeResult<>(Collections
 									.<RawText> emptyList()));
 				}
 				return true;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java
index d880d9b..fd425ab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/nls/GlobalBundleCache.java
@@ -65,7 +65,7 @@
  */
 class GlobalBundleCache {
 	private static final Map<Locale, Map<Class, TranslationBundle>> cachedBundles
-		= new HashMap<Locale, Map<Class, TranslationBundle>>();
+		= new HashMap<>();
 
 	/**
 	 * Looks up for a translation bundle in the global cache. If found returns
@@ -87,7 +87,7 @@
 		try {
 			Map<Class, TranslationBundle> bundles = cachedBundles.get(locale);
 			if (bundles == null) {
-				bundles = new HashMap<Class, TranslationBundle>();
+				bundles = new HashMap<>();
 				cachedBundles.put(locale, bundles);
 			}
 			TranslationBundle bundle = bundles.get(type);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java b/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java
index a768c25..5e7beed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/nls/NLS.java
@@ -72,6 +72,7 @@
 	public static final Locale ROOT_LOCALE = new Locale("", "", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 	private static final InheritableThreadLocal<NLS> local = new InheritableThreadLocal<NLS>() {
+		@Override
 		protected NLS initialValue() {
 			return new NLS(Locale.getDefault());
 		}
@@ -119,7 +120,7 @@
 	}
 
 	final private Locale locale;
-	final private ConcurrentHashMap<Class, TranslationBundle> map = new ConcurrentHashMap<Class, TranslationBundle>();
+	final private ConcurrentHashMap<Class, TranslationBundle> map = new ConcurrentHashMap<>();
 
 	private NLS(Locale locale) {
 		this.locale = locale;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/DefaultNoteMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/DefaultNoteMerger.java
index db49448..2e7327c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/DefaultNoteMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/DefaultNoteMerger.java
@@ -67,6 +67,7 @@
  */
 public class DefaultNoteMerger implements NoteMerger {
 
+	@Override
 	public Note merge(Note base, Note ours, Note theirs, ObjectReader reader,
 			ObjectInserter inserter) throws IOException {
 		if (ours == null)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java
index 79fbb09..7827a9a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java
@@ -140,6 +140,7 @@
 
 			private Iterator<Note> itr;
 
+			@Override
 			public boolean hasNext() {
 				if (itr != null && itr.hasNext())
 					return true;
@@ -164,6 +165,7 @@
 				return false;
 			}
 
+			@Override
 			public Note next() {
 				if (hasNext())
 					return itr.next();
@@ -171,6 +173,7 @@
 					throw new NoSuchElementException();
 			}
 
+			@Override
 			public void remove() {
 				throw new UnsupportedOperationException();
 			}
@@ -259,6 +262,7 @@
 		return inserter.insert(build(true, inserter));
 	}
 
+	@Override
 	ObjectId getTreeId() {
 		try (ObjectInserter.Formatter f = new ObjectInserter.Formatter()) {
 			return f.idFor(build(false, null));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java
index 41f7501..1be5251 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java
@@ -122,10 +122,12 @@
 		return new Iterator<Note>() {
 			private int idx;
 
+			@Override
 			public boolean hasNext() {
 				return idx < cnt;
 			}
 
+			@Override
 			public Note next() {
 				if (hasNext())
 					return notes[idx++];
@@ -133,6 +135,7 @@
 					throw new NoSuchElementException();
 			}
 
+			@Override
 			public void remove() {
 				throw new UnsupportedOperationException();
 			}
@@ -144,6 +147,7 @@
 		return cnt;
 	}
 
+	@Override
 	InMemoryNoteBucket set(AnyObjectId noteOn, AnyObjectId noteData,
 			ObjectReader or) throws IOException {
 		int p = search(noteOn);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMap.java
index 25b2ae8..44c5926 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMap.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMap.java
@@ -201,6 +201,7 @@
 	 * @return an iterator that iterates over notes of this NoteMap. Non note
 	 *         entries are ignored by this iterator.
 	 */
+	@Override
 	public Iterator<Note> iterator() {
 		try {
 			return root.iterator(new MutableObjectId(), reader);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedFileHeader.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedFileHeader.java
index b104a49..2c8f34e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedFileHeader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedFileHeader.java
@@ -183,7 +183,7 @@
 	protected void parseIndexLine(int ptr, final int eol) {
 		// "index $asha1,$bsha1..$csha1"
 		//
-		final List<AbbreviatedObjectId> ids = new ArrayList<AbbreviatedObjectId>();
+		final List<AbbreviatedObjectId> ids = new ArrayList<>();
 		while (ptr < eol) {
 			final int comma = nextLF(buf, ptr, ',');
 			if (eol <= comma)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java
index eb2bfac..ed79787 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/CombinedHunkHeader.java
@@ -266,6 +266,7 @@
 		}
 	}
 
+	@Override
 	void extractFileLines(final StringBuilder sb, final String[] text,
 			final int[] offsets) {
 		final byte[] buf = file.buf;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java
index 534c827..eb28a0e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/FileHeader.java
@@ -305,7 +305,7 @@
 		if (h.getFileHeader() != this)
 			throw new IllegalArgumentException(JGitText.get().hunkBelongsToAnotherFile);
 		if (hunks == null)
-			hunks = new ArrayList<HunkHeader>();
+			hunks = new ArrayList<>();
 		hunks.add(h);
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/FormatError.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/FormatError.java
index 245b3ee..767cb24 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/FormatError.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/FormatError.java
@@ -43,6 +43,8 @@
 
 package org.eclipse.jgit.patch;
 
+import java.util.Locale;
+
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.util.RawParseUtils;
 
@@ -102,7 +104,7 @@
 	@Override
 	public String toString() {
 		final StringBuilder r = new StringBuilder();
-		r.append(getSeverity().name().toLowerCase());
+		r.append(getSeverity().name().toLowerCase(Locale.ROOT));
 		r.append(": at offset "); //$NON-NLS-1$
 		r.append(getOffset());
 		r.append(": "); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java b/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java
index 40ea77e..10ac449 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/patch/Patch.java
@@ -83,8 +83,8 @@
 
 	/** Create an empty patch. */
 	public Patch() {
-		files = new ArrayList<FileHeader>();
-		errors = new ArrayList<FormatError>(0);
+		files = new ArrayList<>();
+		errors = new ArrayList<>(0);
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java
index 6102a81..a8eb86e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotCommitList.java
@@ -74,12 +74,12 @@
 
 	private int positionsAllocated;
 
-	private final TreeSet<Integer> freePositions = new TreeSet<Integer>();
+	private final TreeSet<Integer> freePositions = new TreeSet<>();
 
-	private final HashSet<PlotLane> activeLanes = new HashSet<PlotLane>(32);
+	private final HashSet<PlotLane> activeLanes = new HashSet<>(32);
 
 	/** number of (child) commits on a lane */
-	private final HashMap<PlotLane, Integer> laneLength = new HashMap<PlotLane, Integer>(
+	private final HashMap<PlotLane, Integer> laneLength = new HashMap<>(
 			32);
 
 	@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java
index 55cf235..be1f07a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revplot/PlotWalk.java
@@ -107,7 +107,7 @@
 			if (set == null)
 				set = Collections.singleton(ref);
 			else {
-				set = new HashSet<Ref>(set);
+				set = new HashSet<>(set);
 				set.add(ref);
 			}
 			reverseRefMap.put(ref.getObjectId(), set);
@@ -147,6 +147,7 @@
 	}
 
 	class PlotRefComparator implements Comparator<Ref> {
+		@Override
 		public int compare(Ref o1, Ref o2) {
 			try {
 				RevObject obj1 = parseAny(o1.getObjectId());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java
index 843c2af..4923d0f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/AbstractRevQueue.java
@@ -107,6 +107,7 @@
 	 *
 	 * @return the first commit of this queue.
 	 */
+	@Override
 	public abstract RevCommit next();
 
 	/** Remove all entries from this queue. */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BlockRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BlockRevQueue.java
index 30d140a..db5379e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BlockRevQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BlockRevQueue.java
@@ -83,6 +83,7 @@
 	 * @param q
 	 *            the other queue we will steal entries from.
 	 */
+	@Override
 	public void shareFreeList(final BlockRevQueue q) {
 		free = q.free;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java
index 0802bfa..cd7c074 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DateRevQueue.java
@@ -82,6 +82,7 @@
 		}
 	}
 
+	@Override
 	public void add(final RevCommit c) {
 		sinceLastIndex++;
 		if (++inQueue > REBUILD_INDEX_COUNT
@@ -126,6 +127,7 @@
 		}
 	}
 
+	@Override
 	public RevCommit next() {
 		final Entry q = head;
 		if (q == null)
@@ -161,6 +163,7 @@
 		return head != null ? head.commit : null;
 	}
 
+	@Override
 	public void clear() {
 		head = null;
 		free = null;
@@ -170,6 +173,7 @@
 		last = -1;
 	}
 
+	@Override
 	boolean everbodyHasFlag(final int f) {
 		for (Entry q = head; q != null; q = q.next) {
 			if ((q.commit.flags & f) == 0)
@@ -178,6 +182,7 @@
 		return true;
 	}
 
+	@Override
 	boolean anybodyHasFlag(final int f) {
 		for (Entry q = head; q != null; q = q.next) {
 			if ((q.commit.flags & f) != 0)
@@ -191,6 +196,7 @@
 		return outputType | SORT_COMMIT_TIME_DESC;
 	}
 
+	@Override
 	public String toString() {
 		final StringBuilder s = new StringBuilder();
 		for (Entry q = head; q != null; q = q.next)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
index 59a360f..f932593 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
@@ -138,14 +138,17 @@
 			return new Commit(id);
 		}
 
+		@Override
 		public int getDepth() {
 			return depth;
 		}
 
+		@Override
 		public RevFlag getUnshallowFlag() {
 			return UNSHALLOW;
 		}
 
+		@Override
 		public RevFlag getReinterestingFlag() {
 			return REINTERESTING;
 		}
@@ -239,14 +242,17 @@
 			return new Commit(id);
 		}
 
+		@Override
 		public int getDepth() {
 			return depth;
 		}
 
+		@Override
 		public RevFlag getUnshallowFlag() {
 			return UNSHALLOW;
 		}
 
+		@Override
 		public RevFlag getReinterestingFlag() {
 			return REINTERESTING;
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FIFORevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FIFORevQueue.java
index b8f63aa..1415604 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FIFORevQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FIFORevQueue.java
@@ -64,6 +64,7 @@
 		super(s);
 	}
 
+	@Override
 	public void add(final RevCommit c) {
 		Block b = tail;
 		if (b == null) {
@@ -107,6 +108,7 @@
 		head = b;
 	}
 
+	@Override
 	public RevCommit next() {
 		final Block b = head;
 		if (b == null)
@@ -122,12 +124,14 @@
 		return c;
 	}
 
+	@Override
 	public void clear() {
 		head = null;
 		tail = null;
 		free.clear();
 	}
 
+	@Override
 	boolean everbodyHasFlag(final int f) {
 		for (Block b = head; b != null; b = b.next) {
 			for (int i = b.headIndex; i < b.tailIndex; i++)
@@ -137,6 +141,7 @@
 		return true;
 	}
 
+	@Override
 	boolean anybodyHasFlag(final int f) {
 		for (Block b = head; b != null; b = b.next) {
 			for (int i = b.headIndex; i < b.tailIndex; i++)
@@ -154,6 +159,7 @@
 		}
 	}
 
+	@Override
 	public String toString() {
 		final StringBuilder s = new StringBuilder();
 		for (Block q = head; q != null; q = q.next) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FooterKey.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FooterKey.java
index 319b819..36965f4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FooterKey.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FooterKey.java
@@ -43,6 +43,8 @@
 
 package org.eclipse.jgit.revwalk;
 
+import java.util.Locale;
+
 import org.eclipse.jgit.lib.Constants;
 
 /** Case insensitive key for a {@link FooterLine}. */
@@ -68,7 +70,7 @@
 	 */
 	public FooterKey(final String keyName) {
 		name = keyName;
-		raw = Constants.encode(keyName.toLowerCase());
+		raw = Constants.encode(keyName.toLowerCase(Locale.ROOT));
 	}
 
 	/** @return name of this footer line. */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/LIFORevQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/LIFORevQueue.java
index 9abaf8d..f9da5b1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/LIFORevQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/LIFORevQueue.java
@@ -63,6 +63,7 @@
 		super(s);
 	}
 
+	@Override
 	public void add(final RevCommit c) {
 		Block b = head;
 		if (b == null || !b.canUnpop()) {
@@ -74,6 +75,7 @@
 		b.unpop(c);
 	}
 
+	@Override
 	public RevCommit next() {
 		final Block b = head;
 		if (b == null)
@@ -87,11 +89,13 @@
 		return c;
 	}
 
+	@Override
 	public void clear() {
 		head = null;
 		free.clear();
 	}
 
+	@Override
 	boolean everbodyHasFlag(final int f) {
 		for (Block b = head; b != null; b = b.next) {
 			for (int i = b.headIndex; i < b.tailIndex; i++)
@@ -101,6 +105,7 @@
 		return true;
 	}
 
+	@Override
 	boolean anybodyHasFlag(final int f) {
 		for (Block b = head; b != null; b = b.next) {
 			for (int i = b.headIndex; i < b.tailIndex; i++)
@@ -110,6 +115,7 @@
 		return false;
 	}
 
+	@Override
 	public String toString() {
 		final StringBuilder s = new StringBuilder();
 		for (Block q = head; q != null; q = q.next) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
index 3609d46..5d7e72d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
@@ -87,7 +87,7 @@
 	private int recarryMask;
 
 	private int mergeBaseAncestor = -1;
-	private LinkedList<RevCommit> ret = new LinkedList<RevCommit>();
+	private LinkedList<RevCommit> ret = new LinkedList<>();
 
 	MergeBaseGenerator(final RevWalk w) {
 		walker = w;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
index 9c3af5a..5868998 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
@@ -133,7 +133,7 @@
 	public ObjectWalk(ObjectReader or) {
 		super(or);
 		setRetainBody(false);
-		rootObjects = new ArrayList<RevObject>();
+		rootObjects = new ArrayList<>();
 		pendingObjects = new BlockObjQueue();
 		objectFilter = ObjectFilter.ALL;
 		pathBuf = new byte[256];
@@ -243,6 +243,7 @@
 			addObject(o);
 	}
 
+	@Override
 	public void sort(RevSort s) {
 		super.sort(s);
 		boundary = hasRevSort(RevSort.BOUNDARY);
@@ -681,7 +682,7 @@
 		for (RevObject obj : rootObjects)
 			obj.flags &= ~IN_PENDING;
 
-		rootObjects = new ArrayList<RevObject>();
+		rootObjects = new ArrayList<>();
 		pendingObjects = new BlockObjQueue();
 		currVisit = null;
 		freeVisit = null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
index 1e91006..c641a13 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
@@ -565,7 +565,7 @@
 			ptr--;
 
 		final int msgB = RawParseUtils.commitMessage(raw, 0);
-		final ArrayList<FooterLine> r = new ArrayList<FooterLine>(4);
+		final ArrayList<FooterLine> r = new ArrayList<>(4);
 		final Charset enc = guessEncoding();
 		for (;;) {
 			ptr = RawParseUtils.prevLF(raw, ptr);
@@ -628,7 +628,7 @@
 		final List<FooterLine> src = getFooterLines();
 		if (src.isEmpty())
 			return Collections.emptyList();
-		final ArrayList<String> r = new ArrayList<String>(src.size());
+		final ArrayList<String> r = new ArrayList<>(src.size());
 		for (final FooterLine f : src) {
 			if (f.matches(keyName))
 				r.add(f.getValue());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlag.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlag.java
index 0998a3a..dae52f8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlag.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlag.java
@@ -99,6 +99,7 @@
 		return walker;
 	}
 
+	@Override
 	public String toString() {
 		return name;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlagSet.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlagSet.java
index ca30fd9..bb699e0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlagSet.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevFlagSet.java
@@ -61,7 +61,7 @@
 
 	/** Create an empty set of flags. */
 	public RevFlagSet() {
-		active = new ArrayList<RevFlag>();
+		active = new ArrayList<>();
 	}
 
 	/**
@@ -72,7 +72,7 @@
 	 */
 	public RevFlagSet(final RevFlagSet s) {
 		mask = s.mask;
-		active = new ArrayList<RevFlag>(s.active);
+		active = new ArrayList<>(s.active);
 	}
 
 	/**
@@ -132,14 +132,17 @@
 		return new Iterator<RevFlag>() {
 			private RevFlag current;
 
+			@Override
 			public boolean hasNext() {
 				return i.hasNext();
 			}
 
+			@Override
 			public RevFlag next() {
 				return current = i.next();
 			}
 
+			@Override
 			public void remove() {
 				mask &= ~current.mask;
 				i.remove();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevObjectList.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevObjectList.java
index 5052a4d..9598678 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevObjectList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevObjectList.java
@@ -78,6 +78,7 @@
 		// Initialized above.
 	}
 
+	@Override
 	public void add(final int index, final E element) {
 		if (index != size)
 			throw new UnsupportedOperationException(MessageFormat.format(
@@ -87,6 +88,7 @@
 		size++;
 	}
 
+	@Override
 	@SuppressWarnings("unchecked")
 	public E set(int index, E element) {
 		Block s = contents;
@@ -107,6 +109,7 @@
 		return (E) old;
 	}
 
+	@Override
 	@SuppressWarnings("unchecked")
 	public E get(int index) {
 		Block s = contents;
@@ -120,6 +123,7 @@
 		return s != null ? (E) s.contents[index] : null;
 	}
 
+	@Override
 	public int size() {
 		return size;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index a7f7cd4..320f05f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -226,8 +226,8 @@
 	private RevWalk(ObjectReader or, boolean closeReader) {
 		reader = or;
 		idBuffer = new MutableObjectId();
-		objects = new ObjectIdOwnerMap<RevObject>();
-		roots = new ArrayList<RevCommit>();
+		objects = new ObjectIdOwnerMap<>();
+		roots = new ArrayList<>();
 		queue = new DateRevQueue();
 		pending = new StartGenerator(this);
 		sorting = EnumSet.of(RevSort.NONE);
@@ -931,8 +931,8 @@
 	 */
 	public <T extends ObjectId> AsyncRevObjectQueue parseAny(
 			Iterable<T> objectIds, boolean reportMissing) {
-		List<T> need = new ArrayList<T>();
-		List<RevObject> have = new ArrayList<RevObject>();
+		List<T> need = new ArrayList<>();
+		List<RevObject> have = new ArrayList<>();
 		for (T id : objectIds) {
 			RevObject r = objects.get(id);
 			if (r != null && (r.flags & PARSED) != 0)
@@ -944,14 +944,17 @@
 		final Iterator<RevObject> objItr = have.iterator();
 		if (need.isEmpty()) {
 			return new AsyncRevObjectQueue() {
+				@Override
 				public RevObject next() {
 					return objItr.hasNext() ? objItr.next() : null;
 				}
 
+				@Override
 				public boolean cancel(boolean mayInterruptIfRunning) {
 					return true;
 				}
 
+				@Override
 				public void release() {
 					// In-memory only, no action required.
 				}
@@ -960,6 +963,7 @@
 
 		final AsyncObjectLoaderQueue<T> lItr = reader.open(need, reportMissing);
 		return new AsyncRevObjectQueue() {
+			@Override
 			public RevObject next() throws MissingObjectException,
 					IncorrectObjectTypeException, IOException {
 				if (objItr.hasNext())
@@ -983,10 +987,12 @@
 				return r;
 			}
 
+			@Override
 			public boolean cancel(boolean mayInterruptIfRunning) {
 				return lItr.cancel(mayInterruptIfRunning);
 			}
 
+			@Override
 			public void release() {
 				lItr.release();
 			}
@@ -1316,6 +1322,7 @@
 	 * @return an iterator over this walker's commits.
 	 * @see RevWalkException
 	 */
+	@Override
 	public Iterator<RevCommit> iterator() {
 		final RevCommit first;
 		try {
@@ -1331,10 +1338,12 @@
 		return new Iterator<RevCommit>() {
 			RevCommit next = first;
 
+			@Override
 			public boolean hasNext() {
 				return next != null;
 			}
 
+			@Override
 			public RevCommit next() {
 				try {
 					final RevCommit r = next;
@@ -1349,6 +1358,7 @@
 				}
 			}
 
+			@Override
 			public void remove() {
 				throw new UnsupportedOperationException();
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java
index 11fec31..e751d77 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java
@@ -121,7 +121,7 @@
 		if (end != null)
 			walk.markUninteresting(end);
 
-		List<RevCommit> commits = new ArrayList<RevCommit>();
+		List<RevCommit> commits = new ArrayList<>();
 		for (RevCommit c : walk)
 			commits.add(c);
 		return commits;
@@ -155,7 +155,7 @@
 		// Make sure commit is from the same RevWalk
 		commit = revWalk.parseCommit(commit.getId());
 		revWalk.reset();
-		List<Ref> result = new ArrayList<Ref>();
+		List<Ref> result = new ArrayList<>();
 
 		final int SKEW = 24*3600; // one day clock skew
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/RevFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/RevFilter.java
index 14a98a1..6b90d29 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/RevFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/RevFilter.java
@@ -286,6 +286,7 @@
 	 *
 	 * @return another copy of this filter, suitable for another thread.
 	 */
+	@Override
 	public abstract RevFilter clone();
 
 	@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index 38a0fa2..6cfd352 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -185,6 +185,7 @@
 	 * @throws IOException
 	 *             the file could not be written.
 	 */
+	@Override
 	public void save() throws IOException {
 		final byte[] out;
 		final String text = toText();
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..c64aa2d 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 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 @@
 
 	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 @@
 		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 @@
 	}
 
 	/**
+	 * 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
@@ -969,6 +1044,7 @@
 						getBitmapInactiveBranchAgeInDays()));
 	}
 
+	@Override
 	public String toString() {
 		final StringBuilder b = new StringBuilder();
 		b.append("maxDeltaDepth=").append(getMaxDeltaDepth()); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
index 5b9e8d9..a10f3d7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
@@ -45,6 +45,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.text.MessageFormat;
+import java.util.Locale;
 
 import org.eclipse.jgit.dircache.DirCache;
 import org.eclipse.jgit.dircache.DirCacheIterator;
@@ -663,7 +664,8 @@
 				ConfigConstants.CONFIG_KEY_IGNORE);
 		if (name == null)
 			return null;
-		return IgnoreSubmoduleMode.valueOf(name.trim().toUpperCase());
+		return IgnoreSubmoduleMode
+				.valueOf(name.trim().toUpperCase(Locale.ROOT));
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AbstractAdvertiseRefsHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AbstractAdvertiseRefsHook.java
index 2d730a1..6f13694 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AbstractAdvertiseRefsHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AbstractAdvertiseRefsHook.java
@@ -57,12 +57,14 @@
  * @since 2.0
  */
 public abstract class AbstractAdvertiseRefsHook implements AdvertiseRefsHook {
+	@Override
 	public void advertiseRefs(UploadPack uploadPack)
 			throws ServiceMayNotContinueException {
 		uploadPack.setAdvertisedRefs(getAdvertisedRefs(
 				uploadPack.getRepository(), uploadPack.getRevWalk()));
 	}
 
+	@Override
 	public void advertiseRefs(BaseReceivePack receivePack)
 			throws ServiceMayNotContinueException {
 		Map<String, Ref> refs = getAdvertisedRefs(receivePack.getRepository(),
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java
index c3af74a..96d7c24 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHook.java
@@ -56,10 +56,12 @@
 	 * {@link BaseReceivePack#setAdvertisedRefs(java.util.Map,java.util.Set)}.
 	 */
 	public static final AdvertiseRefsHook DEFAULT = new AdvertiseRefsHook() {
+		@Override
 		public void advertiseRefs(UploadPack uploadPack) {
 			// Do nothing.
 		}
 
+		@Override
 		public void advertiseRefs(BaseReceivePack receivePack) {
 			// Do nothing.
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java
index 00942ab..22ea5cb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AdvertiseRefsHookChain.java
@@ -79,12 +79,14 @@
 			return new AdvertiseRefsHookChain(newHooks, i);
 	}
 
+	@Override
 	public void advertiseRefs(BaseReceivePack rp)
 			throws ServiceMayNotContinueException {
 		for (int i = 0; i < count; i++)
 			hooks[i].advertiseRefs(rp);
 	}
 
+	@Override
 	public void advertiseRefs(UploadPack rp)
 			throws ServiceMayNotContinueException {
 		for (int i = 0; i < count; i++)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
index 1aebadd..64cb4dd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
@@ -120,7 +120,7 @@
 	private static final String X_AMZ_META = "x-amz-meta-"; //$NON-NLS-1$
 
 	static {
-		SIGNED_HEADERS = new HashSet<String>();
+		SIGNED_HEADERS = new HashSet<>();
 		SIGNED_HEADERS.add("content-type"); //$NON-NLS-1$
 		SIGNED_HEADERS.add("content-md5"); //$NON-NLS-1$
 		SIGNED_HEADERS.add("date"); //$NON-NLS-1$
@@ -606,7 +606,7 @@
 
 	void authorize(final HttpURLConnection c) throws IOException {
 		final Map<String, List<String>> reqHdr = c.getRequestProperties();
-		final SortedMap<String, String> sigHdr = new TreeMap<String, String>();
+		final SortedMap<String, String> sigHdr = new TreeMap<>();
 		for (final Map.Entry<String, List<String>> entry : reqHdr.entrySet()) {
 			final String hdr = entry.getKey();
 			if (isSignedHeader(hdr))
@@ -664,7 +664,7 @@
 	}
 
 	private final class ListParser extends DefaultHandler {
-		final List<String> entries = new ArrayList<String>();
+		final List<String> entries = new ArrayList<>();
 
 		private final String bucket;
 
@@ -680,7 +680,7 @@
 		}
 
 		void list() throws IOException {
-			final Map<String, String> args = new TreeMap<String, String>();
+			final Map<String, String> args = new TreeMap<>();
 			if (prefix.length() > 0)
 				args.put("prefix", prefix); //$NON-NLS-1$
 			if (!entries.isEmpty())
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseConnection.java
index 59ff1bd..69028fa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseConnection.java
@@ -71,18 +71,22 @@
 
 	private Writer messageWriter;
 
+	@Override
 	public Map<String, Ref> getRefsMap() {
 		return advertisedRefs;
 	}
 
+	@Override
 	public final Collection<Ref> getRefs() {
 		return advertisedRefs.values();
 	}
 
+	@Override
 	public final Ref getRef(final String name) {
 		return advertisedRefs.get(name);
 	}
 
+	@Override
 	public String getMessages() {
 		return messageWriter != null ? messageWriter.toString() : ""; //$NON-NLS-1$
 	}
@@ -94,6 +98,7 @@
 	 *         server does not advertise this version.
 	 * @since 4.0
 	 */
+	@Override
 	public String getPeerUserAgent() {
 		return peerUserAgent;
 	}
@@ -109,6 +114,7 @@
 		peerUserAgent = agent;
 	}
 
+	@Override
 	public abstract void close();
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseFetchConnection.java
index cc27707..41b8c2d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseFetchConnection.java
@@ -64,12 +64,14 @@
  */
 abstract class BaseFetchConnection extends BaseConnection implements
 		FetchConnection {
+	@Override
 	public final void fetch(final ProgressMonitor monitor,
 			final Collection<Ref> want, final Set<ObjectId> have)
 			throws TransportException {
 		fetch(monitor, want, have, null);
 	}
 
+	@Override
 	public final void fetch(final ProgressMonitor monitor,
 			final Collection<Ref> want, final Set<ObjectId> have,
 			OutputStream out) throws TransportException {
@@ -81,6 +83,7 @@
 	 * Default implementation of {@link FetchConnection#didFetchIncludeTags()} -
 	 * returning false.
 	 */
+	@Override
 	public boolean didFetchIncludeTags() {
 		return false;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
index aa36aeb..c695449 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
@@ -117,10 +117,10 @@
 	protected boolean statelessRPC;
 
 	/** Capability tokens advertised by the remote side. */
-	private final Set<String> remoteCapablities = new HashSet<String>();
+	private final Set<String> remoteCapablities = new HashSet<>();
 
 	/** Extra objects the remote has, but which aren't offered as refs. */
-	protected final Set<ObjectId> additionalHaves = new HashSet<ObjectId>();
+	protected final Set<ObjectId> additionalHaves = new HashSet<>();
 
 	BasePackConnection(final PackTransport packTransport) {
 		transport = (Transport) packTransport;
@@ -191,7 +191,7 @@
 	}
 
 	private void readAdvertisedRefsImpl() throws IOException {
-		final LinkedHashMap<String, Ref> avail = new LinkedHashMap<String, Ref>();
+		final LinkedHashMap<String, Ref> avail = new LinkedHashMap<>();
 		for (;;) {
 			String line;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
index 0dd907f..e8d1881 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
@@ -260,7 +260,7 @@
 
 		if (local != null) {
 			walk = new RevWalk(local);
-			reachableCommits = new RevCommitList<RevCommit>();
+			reachableCommits = new RevCommitList<>();
 			REACHABLE = walk.newFlag("REACHABLE"); //$NON-NLS-1$
 			COMMON = walk.newFlag("COMMON"); //$NON-NLS-1$
 			STATE = walk.newFlag("STATE"); //$NON-NLS-1$
@@ -280,6 +280,7 @@
 
 	private static class FetchConfig {
 		static final SectionParser<FetchConfig> KEY = new SectionParser<FetchConfig>() {
+			@Override
 			public FetchConfig parse(final Config cfg) {
 				return new FetchConfig(cfg);
 			}
@@ -292,6 +293,7 @@
 		}
 	}
 
+	@Override
 	public final void fetch(final ProgressMonitor monitor,
 			final Collection<Ref> want, final Set<ObjectId> have)
 			throws TransportException {
@@ -301,6 +303,7 @@
 	/**
 	 * @since 3.0
 	 */
+	@Override
 	public final void fetch(final ProgressMonitor monitor,
 			final Collection<Ref> want, final Set<ObjectId> have,
 			OutputStream outputStream) throws TransportException {
@@ -308,18 +311,22 @@
 		doFetch(monitor, want, have, outputStream);
 	}
 
+	@Override
 	public boolean didFetchIncludeTags() {
 		return false;
 	}
 
+	@Override
 	public boolean didFetchTestConnectivity() {
 		return false;
 	}
 
+	@Override
 	public void setPackLockMessage(final String message) {
 		lockMessage = message;
 	}
 
+	@Override
 	public Collection<PackLock> getPackLocks() {
 		if (packLock != null)
 			return Collections.singleton(packLock);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
index 86cc484..679ea0c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
@@ -152,6 +152,7 @@
 		pushOptions = transport.getPushOptions();
 	}
 
+	@Override
 	public void push(final ProgressMonitor monitor,
 			final Map<String, RemoteRefUpdate> refUpdates)
 			throws TransportException {
@@ -161,6 +162,7 @@
 	/**
 	 * @since 3.0
 	 */
+	@Override
 	public void push(final ProgressMonitor monitor,
 			final Map<String, RemoteRefUpdate> refUpdates, OutputStream outputStream)
 			throws TransportException {
@@ -326,8 +328,8 @@
 
 	private void writePack(final Map<String, RemoteRefUpdate> refUpdates,
 			final ProgressMonitor monitor) throws IOException {
-		Set<ObjectId> remoteObjects = new HashSet<ObjectId>();
-		Set<ObjectId> newObjects = new HashSet<ObjectId>();
+		Set<ObjectId> remoteObjects = new HashSet<>();
+		Set<ObjectId> newObjects = new HashSet<>();
 
 		try (final PackWriter writer = new PackWriter(transport.getPackConfig(),
 				local.newObjectReader())) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
index 4d0803a..6f94dbb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
@@ -97,6 +97,7 @@
 import org.eclipse.jgit.revwalk.RevSort;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.PacketLineIn.InputOverLimitIOException;
 import org.eclipse.jgit.transport.ReceiveCommand.Result;
 import org.eclipse.jgit.util.io.InterruptTimer;
 import org.eclipse.jgit.util.io.LimitedInputStream;
@@ -123,7 +124,7 @@
 		 *            line from the client.
 		 */
 		public FirstLine(String line) {
-			final HashSet<String> caps = new HashSet<String>();
+			final HashSet<String> caps = new HashSet<>();
 			final int nul = line.indexOf('\0');
 			if (nul >= 0) {
 				for (String c : line.substring(nul + 1).split(" ")) //$NON-NLS-1$
@@ -244,6 +245,8 @@
 	String userAgent;
 	private Set<ObjectId> clientShallowCommits;
 	private List<ReceiveCommand> commands;
+	private long maxCommandBytes;
+	private long maxDiscardBytes;
 
 	private StringBuilder advertiseError;
 
@@ -318,16 +321,19 @@
 		allowNonFastForwards = rc.allowNonFastForwards;
 		allowOfsDelta = rc.allowOfsDelta;
 		allowPushOptions = rc.allowPushOptions;
+		maxCommandBytes = rc.maxCommandBytes;
+		maxDiscardBytes = rc.maxDiscardBytes;
 		advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
 		refFilter = RefFilter.DEFAULT;
-		advertisedHaves = new HashSet<ObjectId>();
-		clientShallowCommits = new HashSet<ObjectId>();
+		advertisedHaves = new HashSet<>();
+		clientShallowCommits = new HashSet<>();
 		signedPushConfig = rc.signedPush;
 	}
 
 	/** Configuration for receive operations. */
 	protected static class ReceiveConfig {
 		static final SectionParser<ReceiveConfig> KEY = new SectionParser<ReceiveConfig>() {
+			@Override
 			public ReceiveConfig parse(final Config cfg) {
 				return new ReceiveConfig(cfg);
 			}
@@ -338,7 +344,8 @@
 		final boolean allowNonFastForwards;
 		final boolean allowOfsDelta;
 		final boolean allowPushOptions;
-
+		final long maxCommandBytes;
+		final long maxDiscardBytes;
 		final SignedPushConfig signedPush;
 
 		ReceiveConfig(final Config config) {
@@ -350,6 +357,12 @@
 					true);
 			allowPushOptions = config.getBoolean("receive", "pushoptions", //$NON-NLS-1$ //$NON-NLS-2$
 					false);
+			maxCommandBytes = config.getLong("receive", //$NON-NLS-1$
+					"maxCommandBytes", //$NON-NLS-1$
+					3 << 20);
+			maxDiscardBytes = config.getLong("receive", //$NON-NLS-1$
+					"maxCommandDiscardBytes", //$NON-NLS-1$
+					-1);
 			signedPush = SignedPushConfig.KEY.parse(config);
 		}
 	}
@@ -729,6 +742,38 @@
 	}
 
 	/**
+	 * Set the maximum number of command bytes to read from the client.
+	 *
+	 * @param limit
+	 *            command limit in bytes; if 0 there is no limit.
+	 * @since 4.7
+	 */
+	public void setMaxCommandBytes(long limit) {
+		maxCommandBytes = limit;
+	}
+
+	/**
+	 * Set the maximum number of command bytes to discard from the client.
+	 * <p>
+	 * Discarding remaining bytes allows this instance to consume the rest of
+	 * the command block and send a human readable over-limit error via the
+	 * side-band channel. If the client sends an excessive number of bytes this
+	 * limit kicks in and the instance disconnects, resulting in a non-specific
+	 * 'pipe closed', 'end of stream', or similar generic error at the client.
+	 * <p>
+	 * When the limit is set to {@code -1} the implementation will default to
+	 * the larger of {@code 3 * maxCommandBytes} or {@code 3 MiB}.
+	 *
+	 * @param limit
+	 *            discard limit in bytes; if 0 there is no limit; if -1 the
+	 *            implementation tries to set a reasonable default.
+	 * @since 4.7
+	 */
+	public void setMaxCommandDiscardBytes(long limit) {
+		maxDiscardBytes = limit;
+	}
+
+	/**
 	 * Set the maximum allowed Git object size.
 	 * <p>
 	 * If an object is larger than the given size the pack-parsing will throw an
@@ -741,7 +786,6 @@
 		maxObjectSizeLimit = limit;
 	}
 
-
 	/**
 	 * Set the maximum allowed pack size.
 	 * <p>
@@ -1014,20 +1058,12 @@
 			rawOut = o;
 		}
 
-		if (maxPackSizeLimit >= 0)
-			rawIn = new LimitedInputStream(rawIn, maxPackSizeLimit) {
-				@Override
-				protected void limitExceeded() throws TooLargePackException {
-					throw new TooLargePackException(limit);
-				}
-			};
-
 		pckIn = new PacketLineIn(rawIn);
 		pckOut = new PacketLineOut(rawOut);
 		pckOut.setFlushOnEnd(false);
 
-		enabledCapabilities = new HashSet<String>();
-		commands = new ArrayList<ReceiveCommand>();
+		enabledCapabilities = new HashSet<>();
+		commands = new ArrayList<>();
 	}
 
 	/** @return advertised refs, or the default if not explicitly advertised. */
@@ -1134,13 +1170,16 @@
 	 * @throws IOException
 	 */
 	protected void recvCommands() throws IOException {
+		PacketLineIn pck = maxCommandBytes > 0
+				? new PacketLineIn(rawIn, maxCommandBytes)
+				: pckIn;
 		PushCertificateParser certParser = getPushCertificateParser();
 		boolean firstPkt = true;
 		try {
 			for (;;) {
 				String line;
 				try {
-					line = pckIn.readString();
+					line = pck.readString();
 				} catch (EOFException eof) {
 					if (commands.isEmpty())
 						return;
@@ -1163,13 +1202,13 @@
 					enableCapabilities();
 
 					if (line.equals(GitProtocolConstants.OPTION_PUSH_CERT)) {
-						certParser.receiveHeader(pckIn, !isBiDirectionalPipe());
+						certParser.receiveHeader(pck, !isBiDirectionalPipe());
 						continue;
 					}
 				}
 
 				if (line.equals(PushCertificateParser.BEGIN_SIGNATURE)) {
-					certParser.receiveSignature(pckIn);
+					certParser.receiveSignature(pck);
 					continue;
 				}
 
@@ -1186,18 +1225,31 @@
 			}
 			pushCert = certParser.build();
 			if (hasCommands()) {
-				readPostCommands(pckIn);
+				readPostCommands(pck);
 			}
 		} catch (PackProtocolException e) {
-			if (sideBand) {
-				try {
-					pckIn.discardUntilEnd();
-				} catch (IOException e2) {
-					// Ignore read failures attempting to discard.
-				}
-			}
+			discardCommands();
 			fatalError(e.getMessage());
 			throw e;
+		} catch (InputOverLimitIOException e) {
+			String msg = JGitText.get().tooManyCommands;
+			discardCommands();
+			fatalError(msg);
+			throw new PackProtocolException(msg);
+		}
+	}
+
+	private void discardCommands() {
+		if (sideBand) {
+			long max = maxDiscardBytes;
+			if (max < 0) {
+				max = Math.max(3 * maxCommandBytes, 3L << 20);
+			}
+			try {
+				new PacketLineIn(rawIn, max).discardUntilEnd();
+			} catch (IOException e) {
+				// Ignore read failures attempting to discard.
+			}
 		}
 	}
 
@@ -1309,7 +1361,7 @@
 			if (getRefLogIdent() != null)
 				lockMsg += " from " + getRefLogIdent().toExternalString(); //$NON-NLS-1$
 
-			parser = ins.newPackParser(rawIn);
+			parser = ins.newPackParser(packInputStream());
 			parser.setAllowThin(true);
 			parser.setNeedNewObjectIds(checkReferencedIsReachable);
 			parser.setNeedBaseObjectIds(checkReferencedIsReachable);
@@ -1329,6 +1381,19 @@
 			timeoutIn.setTimeout(timeout * 1000);
 	}
 
+	private InputStream packInputStream() {
+		InputStream packIn = rawIn;
+		if (maxPackSizeLimit >= 0) {
+			packIn = new LimitedInputStream(packIn, maxPackSizeLimit) {
+				@Override
+				protected void limitExceeded() throws TooLargePackException {
+					throw new TooLargePackException(limit);
+				}
+			};
+		}
+		return packIn;
+	}
+
 	private boolean needCheckConnectivity() {
 		return isCheckReceivedObjects()
 				|| isCheckReferencedObjectsAreReachable()
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
index 8038fa4..f37ba01 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
@@ -92,7 +92,7 @@
 
 	InputStream bin;
 
-	final Map<ObjectId, String> prereqs = new HashMap<ObjectId, String>();
+	final Map<ObjectId, String> prereqs = new HashMap<>();
 
 	private String lockMessage;
 
@@ -130,7 +130,7 @@
 
 	private void readBundleV2() throws IOException {
 		final byte[] hdrbuf = new byte[1024];
-		final LinkedHashMap<String, Ref> avail = new LinkedHashMap<String, Ref>();
+		final LinkedHashMap<String, Ref> avail = new LinkedHashMap<>();
 		for (;;) {
 			String line = readLine(hdrbuf);
 			if (line.length() == 0)
@@ -180,6 +180,7 @@
 		return line.toString();
 	}
 
+	@Override
 	public boolean didFetchTestConnectivity() {
 		return false;
 	}
@@ -207,10 +208,12 @@
 		}
 	}
 
+	@Override
 	public void setPackLockMessage(final String message) {
 		lockMessage = message;
 	}
 
+	@Override
 	public Collection<PackLock> getPackLocks() {
 		if (packLock != null)
 			return Collections.singleton(packLock);
@@ -225,8 +228,8 @@
 			final RevFlag PREREQ = rw.newFlag("PREREQ"); //$NON-NLS-1$
 			final RevFlag SEEN = rw.newFlag("SEEN"); //$NON-NLS-1$
 
-			final Map<ObjectId, String> missing = new HashMap<ObjectId, String>();
-			final List<RevObject> commits = new ArrayList<RevObject>();
+			final Map<ObjectId, String> missing = new HashMap<>();
+			final List<RevObject> commits = new ArrayList<>();
 			for (final Map.Entry<ObjectId, String> e : prereqs.entrySet()) {
 				ObjectId p = e.getKey();
 				try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
index ca624c0..37d70e3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
@@ -102,9 +102,9 @@
 	 */
 	public BundleWriter(final Repository repo) {
 		db = repo;
-		include = new TreeMap<String, ObjectId>();
-		assume = new HashSet<RevCommit>();
-		tagTargets = new HashSet<ObjectId>();
+		include = new TreeMap<>();
+		assume = new HashSet<>();
+		tagTargets = new HashSet<>();
 	}
 
 	/**
@@ -202,8 +202,8 @@
 		try (PackWriter packWriter = new PackWriter(pc, db.newObjectReader())) {
 			packWriter.setObjectCountCallback(callback);
 
-			final HashSet<ObjectId> inc = new HashSet<ObjectId>();
-			final HashSet<ObjectId> exc = new HashSet<ObjectId>();
+			final HashSet<ObjectId> inc = new HashSet<>();
+			final HashSet<ObjectId> exc = new HashSet<>();
 			inc.addAll(include.values());
 			for (final RevCommit r : assume)
 				exc.add(r.getId());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java
index 739ddcc..8cb3d60 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java
@@ -68,7 +68,7 @@
 	 *            here
 	 */
 	public ChainingCredentialsProvider(CredentialsProvider... providers) {
-		this.credentialProviders = new ArrayList<CredentialsProvider>(
+		this.credentialProviders = new ArrayList<>(
 				Arrays.asList(providers));
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java
index da288ec..9a272a4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java
@@ -112,6 +112,7 @@
 	 * Implementers shouldn't throw checked exceptions. This override narrows
 	 * the signature to prevent them from doing so.
 	 */
+	@Override
 	public void close();
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java
index 6757aaf..8358635 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java
@@ -85,14 +85,17 @@
 		return uri;
 	}
 
+	@Override
 	public String getPassword() {
 		return password;
 	}
 
+	@Override
 	public String getPassphrase() {
 		return passphrase;
 	}
 
+	@Override
 	public boolean promptPassphrase(String msg) {
 		CredentialItem.StringType v = newPrompt(msg);
 		if (provider.get(uri, v)) {
@@ -104,6 +107,7 @@
 		}
 	}
 
+	@Override
 	public boolean promptPassword(String msg) {
 		CredentialItem.Password p = new CredentialItem.Password(msg);
 		if (provider.get(uri, p)) {
@@ -119,22 +123,25 @@
 		return new CredentialItem.StringType(msg, true);
 	}
 
+	@Override
 	public boolean promptYesNo(String msg) {
 		CredentialItem.YesNoType v = new CredentialItem.YesNoType(msg);
 		return provider.get(uri, v) && v.getValue();
 	}
 
+	@Override
 	public void showMessage(String msg) {
 		provider.get(uri, new CredentialItem.InformationalMessage(msg));
 	}
 
+	@Override
 	public String[] promptKeyboardInteractive(String destination, String name,
 			String instruction, String[] prompt, boolean[] echo) {
 		CredentialItem.StringType[] v = new CredentialItem.StringType[prompt.length];
 		for (int i = 0; i < prompt.length; i++)
 			v[i] = new CredentialItem.StringType(prompt[i], !echo[i]);
 
-		List<CredentialItem> items = new ArrayList<CredentialItem>();
+		List<CredentialItem> items = new ArrayList<>();
 		if (instruction != null && instruction.length() > 0)
 			items.add(new CredentialItem.InformationalMessage(instruction));
 		items.addAll(Arrays.asList(v));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
index 1beec90..40b2c47 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
@@ -111,6 +111,7 @@
 		repositoryResolver = (RepositoryResolver<DaemonClient>) RepositoryResolver.NONE;
 
 		uploadPackFactory = new UploadPackFactory<DaemonClient>() {
+			@Override
 			public UploadPack create(DaemonClient req, Repository db)
 					throws ServiceNotEnabledException,
 					ServiceNotAuthorizedException {
@@ -122,6 +123,7 @@
 		};
 
 		receivePackFactory = new ReceivePackFactory<DaemonClient>() {
+			@Override
 			public ReceivePack create(DaemonClient req, Repository db)
 					throws ServiceNotEnabledException,
 					ServiceNotAuthorizedException {
@@ -298,6 +300,7 @@
 
 		run = true;
 		acceptThread = new Thread(processors, "Git-Daemon-Accept") { //$NON-NLS-1$
+			@Override
 			public void run() {
 				while (isRunning()) {
 					try {
@@ -344,6 +347,7 @@
 			dc.setRemoteAddress(((InetSocketAddress) peer).getAddress());
 
 		new Thread(processors, "Git-Daemon-Client " + peer.toString()) { //$NON-NLS-1$
+			@Override
 			public void run() {
 				try {
 					dc.execute(s);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java
index ec6f242..80b2cae 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java
@@ -65,6 +65,7 @@
 	DaemonService(final String cmdName, final String cfgName) {
 		command = cmdName.startsWith("git-") ? cmdName : "git-" + cmdName; //$NON-NLS-1$ //$NON-NLS-2$
 		configKey = new SectionParser<ServiceConfig>() {
+			@Override
 			public ServiceConfig parse(final Config cfg) {
 				return new ServiceConfig(DaemonService.this, cfg, cfgName);
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DefaultSshSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/DefaultSshSessionFactory.java
index 7012807..23fd7e3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DefaultSshSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/DefaultSshSessionFactory.java
@@ -59,6 +59,7 @@
  * connection will immediately fail.
  */
 class DefaultSshSessionFactory extends JschConfigSessionFactory {
+	@Override
 	protected void configure(final OpenSshConfig.Host hc, final Session session) {
 		// No additional configuration required.
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
index 8cb36c7..280e6d4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -88,18 +88,18 @@
 	private final Collection<RefSpec> toFetch;
 
 	/** Set of refs we will actually wind up asking to obtain. */
-	private final HashMap<ObjectId, Ref> askFor = new HashMap<ObjectId, Ref>();
+	private final HashMap<ObjectId, Ref> askFor = new HashMap<>();
 
 	/** Objects we know we have locally. */
-	private final HashSet<ObjectId> have = new HashSet<ObjectId>();
+	private final HashSet<ObjectId> have = new HashSet<>();
 
 	/** Updates to local tracking branches (if any). */
-	private final ArrayList<TrackingRefUpdate> localUpdates = new ArrayList<TrackingRefUpdate>();
+	private final ArrayList<TrackingRefUpdate> localUpdates = new ArrayList<>();
 
 	/** Records to be recorded into FETCH_HEAD. */
-	private final ArrayList<FetchHeadRecord> fetchHeadUpdates = new ArrayList<FetchHeadRecord>();
+	private final ArrayList<FetchHeadRecord> fetchHeadUpdates = new ArrayList<>();
 
-	private final ArrayList<PackLock> packLocks = new ArrayList<PackLock>();
+	private final ArrayList<PackLock> packLocks = new ArrayList<>();
 
 	private FetchConnection conn;
 
@@ -137,7 +137,7 @@
 		try {
 			result.setAdvertisedRefs(transport.getURI(), conn.getRefsMap());
 			result.peerUserAgent = conn.getPeerUserAgent();
-			final Set<Ref> matched = new HashSet<Ref>();
+			final Set<Ref> matched = new HashSet<>();
 			for (final RefSpec spec : toFetch) {
 				if (spec.getSource() == null)
 					throw new TransportException(MessageFormat.format(
@@ -275,11 +275,11 @@
 		// We rebuild our askFor list using only the refs that the
 		// new connection has offered to us.
 		//
-		final HashMap<ObjectId, Ref> avail = new HashMap<ObjectId, Ref>();
+		final HashMap<ObjectId, Ref> avail = new HashMap<>();
 		for (final Ref r : conn.getRefs())
 			avail.put(r.getObjectId(), r);
 
-		final Collection<Ref> wants = new ArrayList<Ref>(askFor.values());
+		final Collection<Ref> wants = new ArrayList<>(askFor.values());
 		askFor.clear();
 		for (final Ref want : wants) {
 			final Ref newRef = avail.get(want.getObjectId());
@@ -369,7 +369,7 @@
 	}
 
 	private Collection<Ref> expandAutoFollowTags() throws TransportException {
-		final Collection<Ref> additionalTags = new ArrayList<Ref>();
+		final Collection<Ref> additionalTags = new ArrayList<>();
 		final Map<String, Ref> haveRefs = localRefs();
 		for (final Ref r : conn.getRefs()) {
 			if (!isTag(r))
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchResult.java
index 3d95edd..2667ec3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchResult.java
@@ -48,7 +48,10 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Final status after a successful fetch from a remote repository.
@@ -58,12 +61,39 @@
 public class FetchResult extends OperationResult {
 	private final List<FetchHeadRecord> forMerge;
 
+	private final Map<String, FetchResult> submodules;
+
 	FetchResult() {
-		forMerge = new ArrayList<FetchHeadRecord>();
+		forMerge = new ArrayList<>();
+		submodules = new HashMap<>();
 	}
 
 	void add(final FetchHeadRecord r) {
 		if (!r.notForMerge)
 			forMerge.add(r);
 	}
+
+	/**
+	 * Add fetch results for a submodule.
+	 *
+	 * @param path
+	 *            the submodule path
+	 * @param result
+	 *            the fetch result
+	 * @since 4.7
+	 */
+	public void addSubmodule(String path, FetchResult result) {
+		submodules.put(path, result);
+	}
+
+	/**
+	 * Get fetch results for submodules.
+	 *
+	 * @return Fetch results for submodules as a map of submodule paths to fetch
+	 *         results.
+	 * @since 4.7
+	 */
+	public Map<String, FetchResult> submoduleResults() {
+		return Collections.unmodifiableMap(submodules);
+	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
index 319ae1e..d43be89 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
@@ -82,6 +82,7 @@
 		}
 	}
 
+	@Override
 	public synchronized String createNonce(Repository repo, long timestamp)
 			throws IllegalStateException {
 		String path;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
index 81e6904..c97daa9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
@@ -56,6 +56,7 @@
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Random;
@@ -168,7 +169,8 @@
 									SCHEMA_NAME_SEPARATOR, 2);
 
 							try {
-								Type methodType = Type.valueOf(valuePart[0].toUpperCase());
+								Type methodType = Type.valueOf(
+										valuePart[0].toUpperCase(Locale.ROOT));
 
 								if ((ignoreTypes != null)
 										&& (ignoreTypes.contains(methodType))) {
@@ -346,7 +348,7 @@
 		@SuppressWarnings("boxing")
 		@Override
 		void configureRequest(final HttpConnection conn) throws IOException {
-			final Map<String, String> r = new LinkedHashMap<String, String>();
+			final Map<String, String> r = new LinkedHashMap<>();
 
 			final String realm = params.get("realm"); //$NON-NLS-1$
 			final String nonce = params.get("nonce"); //$NON-NLS-1$
@@ -465,7 +467,7 @@
 		}
 
 		private static Map<String, String> parse(String auth) {
-			Map<String, String> p = new HashMap<String, String>();
+			Map<String, String> p = new HashMap<>();
 			int next = 0;
 			while (next < auth.length()) {
 				if (next < auth.length() && auth.charAt(next) == ',') {
@@ -540,7 +542,7 @@
 			GSSManager gssManager = GSS_MANAGER_FACTORY.newInstance(conn
 					.getURL());
 			String host = conn.getURL().getHost();
-			String peerName = "HTTP@" + host.toLowerCase(); //$NON-NLS-1$
+			String peerName = "HTTP@" + host.toLowerCase(Locale.ROOT); //$NON-NLS-1$
 			try {
 				GSSName gssName = gssManager.createName(peerName,
 						GSSName.NT_HOSTBASED_SERVICE);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java
index 7fc4048..bea8d60 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java
@@ -87,6 +87,7 @@
 		}
 
 		worker = new Thread("JGit-Upload-Pack") { //$NON-NLS-1$
+			@Override
 			public void run() {
 				try {
 					final UploadPack rp = uploadPackFactory.create(req, remote);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java
index ab76e0f..8a1884e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java
@@ -79,6 +79,7 @@
 		}
 
 		worker = new Thread("JGit-Receive-Pack") { //$NON-NLS-1$
+			@Override
 			public void run() {
 				try {
 					final ReceivePack rp = receivePackFactory.create(req, remote);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
index d1cbd8d..ce14183 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java
@@ -80,7 +80,7 @@
  * to supply appropriate {@link UserInfo} to the session.
  */
 public abstract class JschConfigSessionFactory extends SshSessionFactory {
-	private final Map<String, JSch> byIdentityFile = new HashMap<String, JSch>();
+	private final Map<String, JSch> byIdentityFile = new HashMap<>();
 
 	private JSch defaultJSch;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java
index fa27bfc..f445bcb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java
@@ -87,10 +87,12 @@
 		this.uri = uri;
 	}
 
+	@Override
 	public Process exec(String command, int timeout) throws IOException {
 		return new JschProcess(command, timeout);
 	}
 
+	@Override
 	public void disconnect() {
 		if (sock.isConnected())
 			sock.disconnect();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/LongMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/LongMap.java
index 88b4b07..4d60202 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/LongMap.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/LongMap.java
@@ -106,7 +106,7 @@
 
 		if (++size == growAt)
 			grow();
-		insert(new Node<V>(key, value));
+		insert(new Node<>(key, value));
 		return null;
 	}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRC.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRC.java
index bacab7e..bab5bf0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRC.java
@@ -48,6 +48,7 @@
 import java.io.IOException;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 import java.util.TreeMap;
 import java.util.regex.Matcher;
@@ -124,7 +125,7 @@
 
 	private long lastModified;
 
-	private Map<String, NetRCEntry> hosts = new HashMap<String, NetRCEntry>();
+	private Map<String, NetRCEntry> hosts = new HashMap<>();
 
 	private static final TreeMap<String, State> STATE = new TreeMap<String, NetRC.State>() {
 		private static final long serialVersionUID = -4285910831814853334L;
@@ -230,7 +231,7 @@
 
 				matcher.reset(line);
 				while (matcher.find()) {
-					String command = matcher.group().toLowerCase();
+					String command = matcher.group().toLowerCase(Locale.ROOT);
 					if (command.startsWith("#")) { //$NON-NLS-1$
 						matcher.reset(""); //$NON-NLS-1$
 						continue;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java
index 38f3a2a..8b7b60d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java
@@ -175,9 +175,9 @@
 	}
 
 	private Map<String, Host> parse(final InputStream in) throws IOException {
-		final Map<String, Host> m = new LinkedHashMap<String, Host>();
+		final Map<String, Host> m = new LinkedHashMap<>();
 		final BufferedReader br = new BufferedReader(new InputStreamReader(in));
-		final List<Host> current = new ArrayList<Host>(4);
+		final List<Host> current = new ArrayList<>(4);
 		String line;
 
 		while ((line = br.readLine()) != null) {
@@ -311,6 +311,7 @@
 
 	static String userName() {
 		return AccessController.doPrivileged(new PrivilegedAction<String>() {
+			@Override
 			public String run() {
 				return System.getProperty("user.name"); //$NON-NLS-1$
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OperationResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OperationResult.java
index ad51f3e..4231798 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OperationResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OperationResult.java
@@ -64,7 +64,7 @@
 
 	URIish uri;
 
-	final SortedMap<String, TrackingRefUpdate> updates = new TreeMap<String, TrackingRefUpdate>();
+	final SortedMap<String, TrackingRefUpdate> updates = new TreeMap<>();
 
 	StringBuilder messageBuffer;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
index 4bbe3f8..c82b389 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
@@ -83,6 +83,7 @@
 import org.eclipse.jgit.util.BlockList;
 import org.eclipse.jgit.util.IO;
 import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.sha1.SHA1;
 
 /**
  * Parses a pack stream and imports it for an {@link ObjectInserter}.
@@ -116,8 +117,7 @@
 
 	private byte[] hdrBuf;
 
-	private final MessageDigest objectDigest;
-
+	private final SHA1 objectHasher = SHA1.newInstance();
 	private final MutableObjectId tempObjectId;
 
 	private InputStream in;
@@ -206,7 +206,6 @@
 		buf = new byte[BUFFER_SIZE];
 		tempBuffer = new byte[BUFFER_SIZE];
 		hdrBuf = new byte[64];
-		objectDigest = Constants.newMessageDigest();
 		tempObjectId = new MutableObjectId();
 		packDigest = Constants.newMessageDigest();
 		checkObjectCollisions = true;
@@ -275,7 +274,7 @@
 	 */
 	public void setNeedNewObjectIds(boolean b) {
 		if (b)
-			newObjectIds = new ObjectIdSubclassMap<ObjectId>();
+			newObjectIds = new ObjectIdSubclassMap<>();
 		else
 			newObjectIds = null;
 	}
@@ -333,14 +332,14 @@
 	public ObjectIdSubclassMap<ObjectId> getNewObjectIds() {
 		if (newObjectIds != null)
 			return newObjectIds;
-		return new ObjectIdSubclassMap<ObjectId>();
+		return new ObjectIdSubclassMap<>();
 	}
 
 	/** @return set of objects the incoming pack assumed for delta purposes */
 	public ObjectIdSubclassMap<ObjectId> getBaseObjectIds() {
 		if (baseObjectIds != null)
 			return baseObjectIds;
-		return new ObjectIdSubclassMap<ObjectId>();
+		return new ObjectIdSubclassMap<>();
 	}
 
 	/**
@@ -527,9 +526,9 @@
 			readPackHeader();
 
 			entries = new PackedObjectInfo[(int) objectCount];
-			baseById = new ObjectIdOwnerMap<DeltaChain>();
-			baseByPos = new LongMap<UnresolvedDelta>();
-			deferredCheckBlobs = new BlockList<PackedObjectInfo>();
+			baseById = new ObjectIdOwnerMap<>();
+			baseByPos = new LongMap<>();
+			deferredCheckBlobs = new BlockList<>();
 
 			receiving.beginTask(JGitText.get().receivingObjects,
 					(int) objectCount);
@@ -667,12 +666,13 @@
 						JGitText.get().corruptionDetectedReReadingAt,
 						Long.valueOf(visit.delta.position)));
 
+			SHA1 objectDigest = objectHasher.reset();
 			objectDigest.update(Constants.encodedTypeString(type));
 			objectDigest.update((byte) ' ');
 			objectDigest.update(Constants.encodeASCII(visit.data.length));
 			objectDigest.update((byte) 0);
 			objectDigest.update(visit.data);
-			tempObjectId.fromRaw(objectDigest.digest(), 0);
+			objectDigest.digest(tempObjectId);
 
 			verifySafeObject(tempObjectId, type, visit.data);
 
@@ -826,9 +826,9 @@
 		growEntries(baseById.size());
 
 		if (needBaseObjectIds)
-			baseObjectIds = new ObjectIdSubclassMap<ObjectId>();
+			baseObjectIds = new ObjectIdSubclassMap<>();
 
-		final List<DeltaChain> missing = new ArrayList<DeltaChain>(64);
+		final List<DeltaChain> missing = new ArrayList<>(64);
 		for (final DeltaChain baseId : baseById) {
 			if (baseId.head == null)
 				continue;
@@ -1024,6 +1024,7 @@
 
 	private void whole(final long pos, final int type, final long sz)
 			throws IOException {
+		SHA1 objectDigest = objectHasher.reset();
 		objectDigest.update(Constants.encodedTypeString(type));
 		objectDigest.update((byte) ' ');
 		objectDigest.update(Constants.encodeASCII(sz));
@@ -1043,7 +1044,7 @@
 				cnt += r;
 			}
 			inf.close();
-			tempObjectId.fromRaw(objectDigest.digest(), 0);
+			objectDigest.digest(tempObjectId);
 			checkContentLater = isCheckObjectCollisions()
 					&& readCurs.has(tempObjectId);
 			data = null;
@@ -1051,7 +1052,7 @@
 		} else {
 			data = inflateAndReturn(Source.INPUT, sz);
 			objectDigest.update(data);
-			tempObjectId.fromRaw(objectDigest.digest(), 0);
+			objectDigest.digest(tempObjectId);
 			verifySafeObject(tempObjectId, type, data);
 		}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
index e142bab..e709256 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
@@ -87,19 +87,32 @@
 		ACK_READY;
 	}
 
+	private final byte[] lineBuffer = new byte[SideBandOutputStream.SMALL_BUF];
 	private final InputStream in;
-
-	private final byte[] lineBuffer;
+	private long limit;
 
 	/**
 	 * Create a new packet line reader.
 	 *
-	 * @param i
+	 * @param in
 	 *            the input stream to consume.
 	 */
-	public PacketLineIn(final InputStream i) {
-		in = i;
-		lineBuffer = new byte[SideBandOutputStream.SMALL_BUF];
+	public PacketLineIn(InputStream in) {
+		this(in, 0);
+	}
+
+	/**
+	 * Create a new packet line reader.
+	 *
+	 * @param in
+	 *            the input stream to consume.
+	 * @param limit
+	 *            bytes to read from the input; unlimited if set to 0.
+	 * @since 4.7
+	 */
+	public PacketLineIn(InputStream in, long limit) {
+		this.in = in;
+		this.limit = limit;
 	}
 
 	AckNackResult readACK(final MutableObjectId returnedId) throws IOException {
@@ -210,15 +223,48 @@
 
 	int readLength() throws IOException {
 		IO.readFully(in, lineBuffer, 0, 4);
+		int len;
 		try {
-			final int len = RawParseUtils.parseHexInt16(lineBuffer, 0);
-			if (len != 0 && len < 4)
-				throw new ArrayIndexOutOfBoundsException();
-			return len;
+			len = RawParseUtils.parseHexInt16(lineBuffer, 0);
 		} catch (ArrayIndexOutOfBoundsException err) {
-			throw new IOException(MessageFormat.format(JGitText.get().invalidPacketLineHeader,
-					"" + (char) lineBuffer[0] + (char) lineBuffer[1] //$NON-NLS-1$
-					+ (char) lineBuffer[2] + (char) lineBuffer[3]));
+			throw invalidHeader();
 		}
+
+		if (len == 0) {
+			return 0;
+		} else if (len < 4) {
+			throw invalidHeader();
+		}
+
+		if (limit != 0) {
+			int n = len - 4;
+			if (limit < n) {
+				limit = -1;
+				try {
+					IO.skipFully(in, n);
+				} catch (IOException e) {
+					// Ignore failure discarding packet over limit.
+				}
+				throw new InputOverLimitIOException();
+			}
+			// if set limit must not be 0 (means unlimited).
+			limit = n < limit ? limit - n : -1;
+		}
+		return len;
+	}
+
+	private IOException invalidHeader() {
+		return new IOException(MessageFormat.format(JGitText.get().invalidPacketLineHeader,
+				"" + (char) lineBuffer[0] + (char) lineBuffer[1] //$NON-NLS-1$
+				+ (char) lineBuffer[2] + (char) lineBuffer[3]));
+	}
+
+	/**
+	 * IOException thrown by read when the configured input limit is exceeded.
+	 *
+	 * @since 4.7
+	 */
+	public static class InputOverLimitIOException extends IOException {
+		private static final long serialVersionUID = 1L;
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java
index 1e66275..8e39501 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHook.java
@@ -62,6 +62,7 @@
 public interface PostReceiveHook {
 	/** A simple no-op hook. */
 	public static final PostReceiveHook NULL = new PostReceiveHook() {
+		@Override
 		public void onPostReceive(final ReceivePack rp,
 				final Collection<ReceiveCommand> commands) {
 			// Do nothing.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHookChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHookChain.java
index da86525..3bdcd68 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHookChain.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostReceiveHookChain.java
@@ -77,6 +77,7 @@
 			return new PostReceiveHookChain(newHooks, i);
 	}
 
+	@Override
 	public void onPostReceive(ReceivePack rp,
 			Collection<ReceiveCommand> commands) {
 		for (int i = 0; i < count; i++)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java
index 53eeab1..5b37bcd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java
@@ -58,6 +58,7 @@
 public interface PostUploadHook {
 	/** A simple no-op hook. */
 	public static final PostUploadHook NULL = new PostUploadHook() {
+		@Override
 		public void onPostUpload(PackStatistics stats) {
 			// Do nothing.
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHookChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHookChain.java
index 4e2eaea..26323ba 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHookChain.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHookChain.java
@@ -78,6 +78,7 @@
 			return new PostUploadHookChain(newHooks, i);
 	}
 
+	@Override
 	public void onPostUpload(PackStatistics stats) {
 		for (int i = 0; i < count; i++)
 			hooks[i].onPostUpload(stats);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java
index 9a743a5..27b2df0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHook.java
@@ -78,6 +78,7 @@
 public interface PreReceiveHook {
 	/** A simple no-op hook. */
 	public static final PreReceiveHook NULL = new PreReceiveHook() {
+		@Override
 		public void onPreReceive(final ReceivePack rp,
 				final Collection<ReceiveCommand> commands) {
 			// Do nothing.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHookChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHookChain.java
index bd4441f..7b1c8fb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHookChain.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreReceiveHookChain.java
@@ -76,6 +76,7 @@
 			return new PreReceiveHookChain(newHooks, i);
 	}
 
+	@Override
 	public void onPreReceive(ReceivePack rp,
 			Collection<ReceiveCommand> commands) {
 		for (int i = 0; i < count; i++)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java
index 0360f12..7d9638c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHook.java
@@ -58,12 +58,14 @@
 public interface PreUploadHook {
 	/** A simple no-op hook. */
 	public static final PreUploadHook NULL = new PreUploadHook() {
+		@Override
 		public void onBeginNegotiateRound(UploadPack up,
 				Collection<? extends ObjectId> wants, int cntOffered)
 				throws ServiceMayNotContinueException {
 			// Do nothing.
 		}
 
+		@Override
 		public void onEndNegotiateRound(UploadPack up,
 				Collection<? extends ObjectId> wants, int cntCommon,
 				int cntNotFound, boolean ready)
@@ -71,6 +73,7 @@
 			// Do nothing.
 		}
 
+		@Override
 		public void onSendPack(UploadPack up,
 				Collection<? extends ObjectId> wants,
 				Collection<? extends ObjectId> haves)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHookChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHookChain.java
index 7f515e0..c9f88dd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHookChain.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PreUploadHookChain.java
@@ -79,6 +79,7 @@
 			return new PreUploadHookChain(newHooks, i);
 	}
 
+	@Override
 	public void onBeginNegotiateRound(UploadPack up,
 			Collection<? extends ObjectId> wants, int cntOffered)
 			throws ServiceMayNotContinueException {
@@ -86,6 +87,7 @@
 			hooks[i].onBeginNegotiateRound(up, wants, cntOffered);
 	}
 
+	@Override
 	public void onEndNegotiateRound(UploadPack up,
 			Collection<? extends ObjectId> wants, int cntCommon,
 			int cntNotFound, boolean ready)
@@ -94,6 +96,7 @@
 			hooks[i].onEndNegotiateRound(up, wants, cntCommon, cntNotFound, ready);
 	}
 
+	@Override
 	public void onSendPack(UploadPack up,
 			Collection<? extends ObjectId> wants,
 			Collection<? extends ObjectId> haves)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
index d436e08..706e727 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
@@ -140,6 +140,7 @@
 	 * If {@link #get(String)} was called, closes the cached object reader created
 	 * by that method. Does not close the underlying repository.
 	 */
+	@Override
 	public void close() {
 		if (reader != null) {
 			reader.close();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
index 5590c2d..3201732 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
@@ -124,7 +124,7 @@
 			throws TransportException {
 		this.walker = new RevWalk(transport.local);
 		this.transport = transport;
-		this.toPush = new HashMap<String, RemoteRefUpdate>();
+		this.toPush = new HashMap<>();
 		this.out = out;
 		this.pushOptions = transport.getPushOptions();
 		for (final RemoteRefUpdate rru : toPush) {
@@ -190,7 +190,7 @@
 	private Map<String, RemoteRefUpdate> prepareRemoteUpdates()
 			throws TransportException {
 		boolean atomic = transport.isPushAtomic();
-		final Map<String, RemoteRefUpdate> result = new HashMap<String, RemoteRefUpdate>();
+		final Map<String, RemoteRefUpdate> result = new HashMap<>();
 		for (final RemoteRefUpdate rru : toPush.values()) {
 			final Ref advertisedRef = connection.getRef(rru.getRemoteName());
 			ObjectId advertisedOld = null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
index 393e25a..169df3b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -273,6 +273,7 @@
 			if (reportStatus) {
 				if (echoCommandFailures && msgOut != null) {
 					sendStatusReport(false, unpackError, new Reporter() {
+						@Override
 						void sendString(final String s) throws IOException {
 							msgOut.write(Constants.encode(s + "\n")); //$NON-NLS-1$
 						}
@@ -285,6 +286,7 @@
 					}
 				}
 				sendStatusReport(true, unpackError, new Reporter() {
+					@Override
 					void sendString(final String s) throws IOException {
 						pckOut.writeString(s + "\n"); //$NON-NLS-1$
 					}
@@ -292,6 +294,7 @@
 				pckOut.end();
 			} else if (msgOut != null) {
 				sendStatusReport(false, unpackError, new Reporter() {
+					@Override
 					void sendString(final String s) throws IOException {
 						msgOut.write(Constants.encode(s + "\n")); //$NON-NLS-1$
 					}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
index 0cd720c..745e813 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
@@ -163,9 +163,9 @@
 
 	private final char[] tmpId = new char[Constants.OBJECT_ID_STRING_LENGTH];
 
-	final Set<String> capablities = new LinkedHashSet<String>();
+	final Set<String> capablities = new LinkedHashSet<>();
 
-	private final Set<ObjectId> sent = new HashSet<ObjectId>();
+	private final Set<ObjectId> sent = new HashSet<>();
 
 	private Repository repository;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java
index e46195f..d4f85f2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefFilter.java
@@ -59,6 +59,7 @@
 public interface RefFilter {
 	/** The default filter, allows all refs to be shown. */
 	public static final RefFilter DEFAULT = new RefFilter() {
+		@Override
 		public Map<String, Ref> filter (final Map<String, Ref> refs) {
 			return refs;
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefLeaseSpec.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefLeaseSpec.java
new file mode 100644
index 0000000..734f523
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefLeaseSpec.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 Two Sigma Open Source
+ * 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.transport;
+
+import java.io.Serializable;
+
+/**
+ * Describes the expected value for a ref being pushed.
+ * @since 4.7
+ */
+public class RefLeaseSpec implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/** Name of the ref whose value we want to check. */
+	private final String ref;
+
+	/** Local commitish to get expected value from. */
+	private final String expected;
+
+	/**
+	 *
+	 * @param ref
+	 *            ref being pushed
+	 * @param expected
+	 *            the expected value of the ref
+	 */
+	public RefLeaseSpec(String ref, String expected) {
+		this.ref = ref;
+		this.expected = expected;
+	}
+
+	/**
+	 * Get the ref to protect.
+	 *
+	 * @return name of ref to check.
+	 */
+	public String getRef() {
+		return ref;
+	}
+
+	/**
+	 * Get the expected value of the ref, in the form
+	 * of a local committish
+	 *
+	 * @return expected ref value.
+	 */
+	public String getExpected() {
+		return expected;
+	}
+
+	@Override
+	public String toString() {
+		final StringBuilder r = new StringBuilder();
+		r.append(getRef());
+		r.append(':');
+		r.append(getExpected());
+		return r.toString();
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
index 1440b83..64f6c3f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
@@ -556,6 +556,7 @@
 		return true;
 	}
 
+	@Override
 	public int hashCode() {
 		int hc = 0;
 		if (getSource() != null)
@@ -565,6 +566,7 @@
 		return hc;
 	}
 
+	@Override
 	public boolean equals(final Object obj) {
 		if (!(obj instanceof RefSpec))
 			return false;
@@ -588,6 +590,7 @@
 		return a.equals(b);
 	}
 
+	@Override
 	public String toString() {
 		final StringBuilder r = new StringBuilder();
 		if (isForceUpdate())
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java
index ba0931b..d91684e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java
@@ -114,11 +114,11 @@
 	 */
 	public static List<RemoteConfig> getAllRemoteConfigs(final Config rc)
 			throws URISyntaxException {
-		final List<String> names = new ArrayList<String>(rc
+		final List<String> names = new ArrayList<>(rc
 				.getSubsections(SECTION));
 		Collections.sort(names);
 
-		final List<RemoteConfig> result = new ArrayList<RemoteConfig>(names
+		final List<RemoteConfig> result = new ArrayList<>(names
 				.size());
 		for (final String name : names)
 			result.add(new RemoteConfig(rc, name));
@@ -169,24 +169,24 @@
 
 		vlst = rc.getStringList(SECTION, name, KEY_URL);
 		Map<String, String> insteadOf = getReplacements(rc, KEY_INSTEADOF);
-		uris = new ArrayList<URIish>(vlst.length);
+		uris = new ArrayList<>(vlst.length);
 		for (final String s : vlst)
 			uris.add(new URIish(replaceUri(s, insteadOf)));
 
 		Map<String, String> pushInsteadOf = getReplacements(rc,
 				KEY_PUSHINSTEADOF);
 		vlst = rc.getStringList(SECTION, name, KEY_PUSHURL);
-		pushURIs = new ArrayList<URIish>(vlst.length);
+		pushURIs = new ArrayList<>(vlst.length);
 		for (final String s : vlst)
 			pushURIs.add(new URIish(replaceUri(s, pushInsteadOf)));
 
 		vlst = rc.getStringList(SECTION, name, KEY_FETCH);
-		fetch = new ArrayList<RefSpec>(vlst.length);
+		fetch = new ArrayList<>(vlst.length);
 		for (final String s : vlst)
 			fetch.add(new RefSpec(s));
 
 		vlst = rc.getStringList(SECTION, name, KEY_PUSH);
-		push = new ArrayList<RefSpec>(vlst.length);
+		push = new ArrayList<>(vlst.length);
 		for (final String s : vlst)
 			push.add(new RefSpec(s));
 
@@ -213,7 +213,7 @@
 	 *            the configuration file to store ourselves into.
 	 */
 	public void update(final Config rc) {
-		final List<String> vlst = new ArrayList<String>();
+		final List<String> vlst = new ArrayList<>();
 
 		vlst.clear();
 		for (final URIish u : getURIs())
@@ -272,7 +272,7 @@
 
 	private Map<String, String> getReplacements(final Config config,
 			final String keyName) {
-		final Map<String, String> replacements = new HashMap<String, String>();
+		final Map<String, String> replacements = new HashMap<>();
 		for (String url : config.getSubsections(KEY_URL))
 			for (String insteadOf : config.getStringList(KEY_URL, url, keyName))
 				replacements.put(insteadOf, url);
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..87c9a50 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 @@
 	/** Initialize with no message. */
 	public ServiceMayNotContinueException() {
 		// Do not set a message.
-		statusCode = HttpServletResponse.SC_FORBIDDEN;
+		statusCode = FORBIDDEN;
 	}
 
 	/**
@@ -73,7 +72,7 @@
 	 */
 	public ServiceMayNotContinueException(String msg) {
 		super(msg);
-		statusCode = HttpServletResponse.SC_FORBIDDEN;
+		statusCode = FORBIDDEN;
 	}
 
 	/**
@@ -99,7 +98,7 @@
 	 */
 	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/transport/SignedPushConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SignedPushConfig.java
index 942e7d7..83b4aca 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SignedPushConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SignedPushConfig.java
@@ -55,6 +55,7 @@
 	/** Key for {@link Config#get(SectionParser)}. */
 	public static final SectionParser<SignedPushConfig> KEY =
 			new SectionParser<SignedPushConfig>() {
+		@Override
 		public SignedPushConfig parse(Config cfg) {
 			return new SignedPushConfig(cfg);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java
index 5fd2f84..8a28e3a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java
@@ -103,7 +103,7 @@
 			ReceivePackFactory<C> receivePackFactory) {
 		this.uploadPackFactory = uploadPackFactory;
 		this.receivePackFactory = receivePackFactory;
-		this.handles = new HashMap<URIish, Handle>();
+		this.handles = new HashMap<>();
 	}
 
 	@Override
@@ -174,7 +174,7 @@
 		public FetchConnection openFetch() throws NotSupportedException,
 				TransportException {
 			handle.remote.incrementOpen();
-			return new InternalFetchConnection<C>(
+			return new InternalFetchConnection<>(
 					this, uploadPackFactory, handle.req, handle.remote);
 		}
 
@@ -182,7 +182,7 @@
 		public PushConnection openPush() throws NotSupportedException,
 				TransportException {
 			handle.remote.incrementOpen();
-			return new InternalPushConnection<C>(
+			return new InternalPushConnection<>(
 					this, receivePackFactory, handle.req, handle.remote);
 		}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
index 72c9c8b..2198b50 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
@@ -70,6 +70,7 @@
 
 	/** Key for {@link Config#get(SectionParser)}. */
 	public static final Config.SectionParser<TransferConfig> KEY = new SectionParser<TransferConfig>() {
+		@Override
 		public TransferConfig parse(final Config cfg) {
 			return new TransferConfig(cfg);
 		}
@@ -207,8 +208,9 @@
 			return RefFilter.DEFAULT;
 
 		return new RefFilter() {
+			@Override
 			public Map<String, Ref> filter(Map<String, Ref> refs) {
-				Map<String, Ref> result = new HashMap<String, Ref>();
+				Map<String, Ref> result = new HashMap<>();
 				for (Map.Entry<String, Ref> e : refs.entrySet()) {
 					boolean add = true;
 					for (String hide : hideRefs) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
index df86069..649e840 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -80,6 +80,7 @@
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectChecker;
+import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
@@ -108,7 +109,7 @@
 	}
 
 	private static final List<WeakReference<TransportProtocol>> protocols =
-		new CopyOnWriteArrayList<WeakReference<TransportProtocol>>();
+		new CopyOnWriteArrayList<>();
 
 	static {
 		// Registration goes backwards in order of priority.
@@ -225,7 +226,7 @@
 	 *            the protocol definition. Must not be null.
 	 */
 	public static void register(TransportProtocol proto) {
-		protocols.add(0, new WeakReference<TransportProtocol>(proto));
+		protocols.add(0, new WeakReference<>(proto));
 	}
 
 	/**
@@ -255,7 +256,7 @@
 	 */
 	public static List<TransportProtocol> getTransportProtocols() {
 		int cnt = protocols.size();
-		List<TransportProtocol> res = new ArrayList<TransportProtocol>(cnt);
+		List<TransportProtocol> res = new ArrayList<>(cnt);
 		for (WeakReference<TransportProtocol> ref : protocols) {
 			TransportProtocol proto = ref.get();
 			if (proto != null)
@@ -379,7 +380,7 @@
 			TransportException {
 		final RemoteConfig cfg = new RemoteConfig(local.getConfig(), remote);
 		if (doesNotExist(cfg)) {
-			final ArrayList<Transport> transports = new ArrayList<Transport>(1);
+			final ArrayList<Transport> transports = new ArrayList<>(1);
 			transports.add(open(local, new URIish(remote), null));
 			return transports;
 		}
@@ -489,7 +490,7 @@
 			final RemoteConfig cfg, final Operation op)
 			throws NotSupportedException, TransportException {
 		final List<URIish> uris = getURIs(cfg, op);
-		final List<Transport> transports = new ArrayList<Transport>(uris.size());
+		final List<Transport> transports = new ArrayList<>(uris.size());
 		for (final URIish uri : uris) {
 			final Transport tn = open(local, uri, cfg.getName());
 			tn.applyConfig(cfg);
@@ -603,14 +604,16 @@
 	 * Convert push remote refs update specification from {@link RefSpec} form
 	 * to {@link RemoteRefUpdate}. Conversion expands wildcards by matching
 	 * source part to local refs. expectedOldObjectId in RemoteRefUpdate is
-	 * always set as null. Tracking branch is configured if RefSpec destination
-	 * matches source of any fetch ref spec for this transport remote
-	 * configuration.
+	 * set when specified in leases. Tracking branch is configured if RefSpec
+	 * destination matches source of any fetch ref spec for this transport
+	 * remote configuration.
 	 *
 	 * @param db
 	 *            local database.
 	 * @param specs
 	 *            collection of RefSpec to convert.
+	 * @param leases
+	 *            map from ref to lease (containing expected old object id)
 	 * @param fetchSpecs
 	 *            fetch specifications used for finding localtracking refs. May
 	 *            be null or empty collection.
@@ -618,13 +621,15 @@
 	 * @throws IOException
 	 *             when problem occurred during conversion or specification set
 	 *             up: most probably, missing objects or refs.
+	 * @since 4.7
 	 */
 	public static Collection<RemoteRefUpdate> findRemoteRefUpdatesFor(
 			final Repository db, final Collection<RefSpec> specs,
+			final Map<String, RefLeaseSpec> leases,
 			Collection<RefSpec> fetchSpecs) throws IOException {
 		if (fetchSpecs == null)
 			fetchSpecs = Collections.emptyList();
-		final List<RemoteRefUpdate> result = new LinkedList<RemoteRefUpdate>();
+		final List<RemoteRefUpdate> result = new LinkedList<>();
 		final Collection<RefSpec> procRefs = expandPushWildcardsFor(db, specs);
 
 		for (final RefSpec spec : procRefs) {
@@ -652,18 +657,48 @@
 
 			final boolean forceUpdate = spec.isForceUpdate();
 			final String localName = findTrackingRefName(destSpec, fetchSpecs);
+			final RefLeaseSpec leaseSpec = leases.get(destSpec);
+			final ObjectId expected = leaseSpec == null ? null :
+				db.resolve(leaseSpec.getExpected());
 			final RemoteRefUpdate rru = new RemoteRefUpdate(db, srcSpec,
-					destSpec, forceUpdate, localName, null);
+					destSpec, forceUpdate, localName, expected);
 			result.add(rru);
 		}
 		return result;
 	}
 
+	/**
+	 * Convert push remote refs update specification from {@link RefSpec} form
+	 * to {@link RemoteRefUpdate}. Conversion expands wildcards by matching
+	 * source part to local refs. expectedOldObjectId in RemoteRefUpdate is
+	 * always set as null. Tracking branch is configured if RefSpec destination
+	 * matches source of any fetch ref spec for this transport remote
+	 * configuration.
+	 *
+	 * @param db
+	 *            local database.
+	 * @param specs
+	 *            collection of RefSpec to convert.
+	 * @param fetchSpecs
+	 *            fetch specifications used for finding localtracking refs. May
+	 *            be null or empty collection.
+	 * @return collection of set up {@link RemoteRefUpdate}.
+	 * @throws IOException
+	 *             when problem occurred during conversion or specification set
+	 *             up: most probably, missing objects or refs.
+	 */
+	public static Collection<RemoteRefUpdate> findRemoteRefUpdatesFor(
+			final Repository db, final Collection<RefSpec> specs,
+			Collection<RefSpec> fetchSpecs) throws IOException {
+		return findRemoteRefUpdatesFor(db, specs, Collections.emptyMap(),
+					       fetchSpecs);
+	}
+
 	private static Collection<RefSpec> expandPushWildcardsFor(
 			final Repository db, final Collection<RefSpec> specs)
 			throws IOException {
 		final Map<String, Ref> localRefs = db.getRefDatabase().getRefs(ALL);
-		final Collection<RefSpec> procRefs = new HashSet<RefSpec>();
+		final Collection<RefSpec> procRefs = new HashSet<>();
 
 		for (final RefSpec spec : specs) {
 			if (spec.isWildcard()) {
@@ -1182,7 +1217,7 @@
 			// the local tracking branches without incurring additional
 			// object transfer overheads.
 			//
-			final Collection<RefSpec> tmp = new ArrayList<RefSpec>(toFetch);
+			final Collection<RefSpec> tmp = new ArrayList<>(toFetch);
 			for (final RefSpec requested : toFetch) {
 				final String reqSrc = requested.getSource();
 				for (final RefSpec configured : fetch) {
@@ -1341,7 +1376,36 @@
 	 */
 	public Collection<RemoteRefUpdate> findRemoteRefUpdatesFor(
 			final Collection<RefSpec> specs) throws IOException {
-		return findRemoteRefUpdatesFor(local, specs, fetch);
+		return findRemoteRefUpdatesFor(local, specs, Collections.emptyMap(),
+					       fetch);
+	}
+
+	/**
+	 * Convert push remote refs update specification from {@link RefSpec} form
+	 * to {@link RemoteRefUpdate}. Conversion expands wildcards by matching
+	 * source part to local refs. expectedOldObjectId in RemoteRefUpdate is
+	 * set according to leases. Tracking branch is configured if RefSpec destination
+	 * matches source of any fetch ref spec for this transport remote
+	 * configuration.
+	 * <p>
+	 * Conversion is performed for context of this transport (database, fetch
+	 * specifications).
+	 *
+	 * @param specs
+	 *            collection of RefSpec to convert.
+	 * @param leases
+	 *            map from ref to lease (containing expected old object id)
+	 * @return collection of set up {@link RemoteRefUpdate}.
+	 * @throws IOException
+	 *             when problem occurred during conversion or specification set
+	 *             up: most probably, missing objects or refs.
+	 * @since 4.7
+	 */
+	public Collection<RemoteRefUpdate> findRemoteRefUpdatesFor(
+			final Collection<RefSpec> specs,
+			final Map<String, RefLeaseSpec> leases) throws IOException {
+		return findRemoteRefUpdatesFor(local, specs, leases,
+					       fetch);
 	}
 
 	/**
@@ -1383,5 +1447,6 @@
 	 * Implementers shouldn't throw checked exceptions. This override narrows
 	 * the signature to prevent them from doing so.
 	 */
+	@Override
 	public abstract void close();
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
index 23c506b..6cd119b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
@@ -101,23 +101,28 @@
 	static final String S3_SCHEME = "amazon-s3"; //$NON-NLS-1$
 
 	static final TransportProtocol PROTO_S3 = new TransportProtocol() {
+		@Override
 		public String getName() {
 			return "Amazon S3"; //$NON-NLS-1$
 		}
 
+		@Override
 		public Set<String> getSchemes() {
 			return Collections.singleton(S3_SCHEME);
 		}
 
+		@Override
 		public Set<URIishField> getRequiredFields() {
 			return Collections.unmodifiableSet(EnumSet.of(URIishField.USER,
 					URIishField.HOST, URIishField.PATH));
 		}
 
+		@Override
 		public Set<URIishField> getOptionalFields() {
 			return Collections.unmodifiableSet(EnumSet.of(URIishField.PASS));
 		}
 
+		@Override
 		public Transport open(URIish uri, Repository local, String remoteName)
 				throws NotSupportedException {
 			return new TransportAmazonS3(local, uri);
@@ -265,10 +270,10 @@
 
 		@Override
 		Collection<String> getPackNames() throws IOException {
-			final HashSet<String> have = new HashSet<String>();
+			final HashSet<String> have = new HashSet<>();
 			have.addAll(s3.list(bucket, resolveKey("pack"))); //$NON-NLS-1$
 
-			final Collection<String> packs = new ArrayList<String>();
+			final Collection<String> packs = new ArrayList<>();
 			for (final String n : have) {
 				if (!n.startsWith("pack-") || !n.endsWith(".pack")) //$NON-NLS-1$ //$NON-NLS-2$
 					continue;
@@ -307,7 +312,7 @@
 		}
 
 		Map<String, Ref> readAdvertisedRefs() throws TransportException {
-			final TreeMap<String, Ref> avail = new TreeMap<String, Ref>();
+			final TreeMap<String, Ref> avail = new TreeMap<>();
 			readPackedRefs(avail);
 			readLooseRefs(avail);
 			readRef(avail, Constants.HEAD);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundleFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundleFile.java
index 9b08341..f2ddc0d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundleFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundleFile.java
@@ -66,7 +66,7 @@
 		private final String[] schemeNames = { "bundle", "file" }; //$NON-NLS-1$ //$NON-NLS-2$
 
 		private final Set<String> schemeSet = Collections
-				.unmodifiableSet(new LinkedHashSet<String>(Arrays
+				.unmodifiableSet(new LinkedHashSet<>(Arrays
 						.asList(schemeNames)));
 
 		@Override
@@ -74,6 +74,7 @@
 			return JGitText.get().transportProtoBundleFile;
 		}
 
+		@Override
 		public Set<String> getSchemes() {
 			return schemeSet;
 		}
@@ -106,6 +107,7 @@
 			return TransportLocal.PROTO_LOCAL.open(uri, local, remoteName);
 		}
 
+		@Override
 		public Transport open(URIish uri) throws NotSupportedException,
 				TransportException {
 			if ("bundle".equals(uri.getScheme())) { //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitAnon.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitAnon.java
index c6e4c50..7bf5b94 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitAnon.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitAnon.java
@@ -75,27 +75,33 @@
 	static final int GIT_PORT = Daemon.DEFAULT_PORT;
 
 	static final TransportProtocol PROTO_GIT = new TransportProtocol() {
+		@Override
 		public String getName() {
 			return JGitText.get().transportProtoGitAnon;
 		}
 
+		@Override
 		public Set<String> getSchemes() {
 			return Collections.singleton("git"); //$NON-NLS-1$
 		}
 
+		@Override
 		public Set<URIishField> getRequiredFields() {
 			return Collections.unmodifiableSet(EnumSet.of(URIishField.HOST,
 					URIishField.PATH));
 		}
 
+		@Override
 		public Set<URIishField> getOptionalFields() {
 			return Collections.unmodifiableSet(EnumSet.of(URIishField.PORT));
 		}
 
+		@Override
 		public int getDefaultPort() {
 			return GIT_PORT;
 		}
 
+		@Override
 		public Transport open(URIish uri, Repository local, String remoteName)
 				throws NotSupportedException {
 			return new TransportGitAnon(local, uri);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
index da98e8c..9a40f47 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
@@ -56,6 +56,7 @@
 import java.util.EnumSet;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Set;
 
 import org.eclipse.jgit.errors.NoRemoteRepositoryException;
@@ -86,27 +87,32 @@
 		private final String[] schemeNames = { "ssh", "ssh+git", "git+ssh" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 
 		private final Set<String> schemeSet = Collections
-				.unmodifiableSet(new LinkedHashSet<String>(Arrays
+				.unmodifiableSet(new LinkedHashSet<>(Arrays
 						.asList(schemeNames)));
 
+		@Override
 		public String getName() {
 			return JGitText.get().transportProtoSSH;
 		}
 
+		@Override
 		public Set<String> getSchemes() {
 			return schemeSet;
 		}
 
+		@Override
 		public Set<URIishField> getRequiredFields() {
 			return Collections.unmodifiableSet(EnumSet.of(URIishField.HOST,
 					URIishField.PATH));
 		}
 
+		@Override
 		public Set<URIishField> getOptionalFields() {
 			return Collections.unmodifiableSet(EnumSet.of(URIishField.USER,
 					URIishField.PASS, URIishField.PORT));
 		}
 
+		@Override
 		public int getDefaultPort() {
 			return 22;
 		}
@@ -123,6 +129,7 @@
 			return super.canHandle(uri, local, remoteName);
 		}
 
+		@Override
 		public Transport open(URIish uri, Repository local, String remoteName)
 				throws NotSupportedException {
 			return new TransportGitSsh(local, uri);
@@ -214,14 +221,16 @@
 	}
 
 	private class ExtSession implements RemoteSession {
+		@Override
 		public Process exec(String command, int timeout)
 				throws TransportException {
 			String ssh = SystemReader.getInstance().getenv("GIT_SSH"); //$NON-NLS-1$
-			boolean putty = ssh.toLowerCase().contains("plink"); //$NON-NLS-1$
+			boolean putty = ssh.toLowerCase(Locale.ROOT).contains("plink"); //$NON-NLS-1$
 
-			List<String> args = new ArrayList<String>();
+			List<String> args = new ArrayList<>();
 			args.add(ssh);
-			if (putty && !ssh.toLowerCase().contains("tortoiseplink")) //$NON-NLS-1$
+			if (putty
+					&& !ssh.toLowerCase(Locale.ROOT).contains("tortoiseplink")) //$NON-NLS-1$
 				args.add("-batch"); //$NON-NLS-1$
 			if (0 < getURI().getPort()) {
 				args.add(putty ? "-P" : "-p"); //$NON-NLS-1$ //$NON-NLS-2$
@@ -248,6 +257,7 @@
 			}
 		}
 
+		@Override
 		public void disconnect() {
 			// Nothing to do
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
index 96a6fe1..26a254d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
@@ -52,6 +52,7 @@
 import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING;
 import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING;
 import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE;
+import static org.eclipse.jgit.util.HttpSupport.HDR_LOCATION;
 import static org.eclipse.jgit.util.HttpSupport.HDR_PRAGMA;
 import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT;
 import static org.eclipse.jgit.util.HttpSupport.HDR_WWW_AUTHENTICATE;
@@ -153,64 +154,77 @@
 		private final String[] schemeNames = { "http", "https" }; //$NON-NLS-1$ //$NON-NLS-2$
 
 		private final Set<String> schemeSet = Collections
-				.unmodifiableSet(new LinkedHashSet<String>(Arrays
+				.unmodifiableSet(new LinkedHashSet<>(Arrays
 						.asList(schemeNames)));
 
+		@Override
 		public String getName() {
 			return JGitText.get().transportProtoHTTP;
 		}
 
+		@Override
 		public Set<String> getSchemes() {
 			return schemeSet;
 		}
 
+		@Override
 		public Set<URIishField> getRequiredFields() {
 			return Collections.unmodifiableSet(EnumSet.of(URIishField.HOST,
 					URIishField.PATH));
 		}
 
+		@Override
 		public Set<URIishField> getOptionalFields() {
 			return Collections.unmodifiableSet(EnumSet.of(URIishField.USER,
 					URIishField.PASS, URIishField.PORT));
 		}
 
+		@Override
 		public int getDefaultPort() {
 			return 80;
 		}
 
+		@Override
 		public Transport open(URIish uri, Repository local, String remoteName)
 				throws NotSupportedException {
 			return new TransportHttp(local, uri);
 		}
 
+		@Override
 		public Transport open(URIish uri) throws NotSupportedException {
 			return new TransportHttp(uri);
 		}
 	};
 
 	static final TransportProtocol PROTO_FTP = new TransportProtocol() {
+		@Override
 		public String getName() {
 			return JGitText.get().transportProtoFTP;
 		}
 
+		@Override
 		public Set<String> getSchemes() {
 			return Collections.singleton("ftp"); //$NON-NLS-1$
 		}
 
+		@Override
 		public Set<URIishField> getRequiredFields() {
 			return Collections.unmodifiableSet(EnumSet.of(URIishField.HOST,
 					URIishField.PATH));
 		}
 
+		@Override
 		public Set<URIishField> getOptionalFields() {
 			return Collections.unmodifiableSet(EnumSet.of(URIishField.USER,
 					URIishField.PASS, URIishField.PORT));
 		}
 
+		@Override
 		public int getDefaultPort() {
 			return 21;
 		}
 
+		@Override
 		public Transport open(URIish uri, Repository local, String remoteName)
 				throws NotSupportedException {
 			return new TransportHttp(local, uri);
@@ -218,6 +232,7 @@
 	};
 
 	private static final Config.SectionParser<HttpConfig> HTTP_KEY = new SectionParser<HttpConfig>() {
+		@Override
 		public HttpConfig parse(final Config cfg) {
 			return new HttpConfig(cfg);
 		}
@@ -536,7 +551,7 @@
 			} catch (IOException e) {
 				if (authMethod.getType() != HttpAuthMethod.Type.NONE) {
 					if (ignoreTypes == null) {
-						ignoreTypes = new HashSet<Type>();
+						ignoreTypes = new HashSet<>();
 					}
 
 					ignoreTypes.add(authMethod.getType());
@@ -711,7 +726,7 @@
 
 		@Override
 		Collection<String> getPackNames() throws IOException {
-			final Collection<String> packs = new ArrayList<String>();
+			final Collection<String> packs = new ArrayList<>();
 			try {
 				final BufferedReader br = openReader(INFO_PACKS);
 				try {
@@ -764,7 +779,7 @@
 
 		Map<String, Ref> readAdvertisedImpl(final BufferedReader br)
 				throws IOException, PackProtocolException {
-			final TreeMap<String, Ref> avail = new TreeMap<String, Ref>();
+			final TreeMap<String, Ref> avail = new TreeMap<>();
 			for (;;) {
 				String line = br.readLine();
 				if (line == null)
@@ -862,6 +877,7 @@
 			readAdvertisedRefs();
 		}
 
+		@Override
 		protected void doPush(final ProgressMonitor monitor,
 				final Map<String, RemoteRefUpdate> refUpdates,
 				OutputStream outputStream) throws TransportException {
@@ -898,9 +914,13 @@
 		}
 
 		void openStream() throws IOException {
+			openStream(null);
+		}
+
+		void openStream(final String redirectUrl) throws IOException {
 			conn = httpOpen(
 					METHOD_POST,
-					new URL(baseUrl, serviceName),
+					redirectUrl == null ? new URL(baseUrl, serviceName) : new URL(redirectUrl),
 					AcceptEncoding.GZIP);
 			conn.setInstanceFollowRedirects(false);
 			conn.setDoOutput(true);
@@ -909,6 +929,10 @@
 		}
 
 		void sendRequest() throws IOException {
+			sendRequest(null);
+		}
+
+		void sendRequest(final String redirectUrl) throws IOException {
 			// Try to compress the content, but only if that is smaller.
 			TemporaryBuffer buf = new TemporaryBuffer.Heap(http.postBuffer);
 			try {
@@ -923,7 +947,7 @@
 				buf = out;
 			}
 
-			openStream();
+			openStream(redirectUrl);
 			if (buf != out)
 				conn.setRequestProperty(HDR_CONTENT_ENCODING, ENCODING_GZIP);
 			conn.setFixedLengthStreamingMode((int) buf.length());
@@ -933,6 +957,12 @@
 			} finally {
 				httpOut.close();
 			}
+
+			final int status = HttpSupport.response(conn);
+			if (status == HttpConnection.HTTP_MOVED_PERM) {
+				String locationHeader = HttpSupport.responseHeader(conn, HDR_LOCATION);
+				sendRequest(locationHeader);
+			}
 		}
 
 		void openResponse() throws IOException {
@@ -960,16 +990,19 @@
 		abstract void execute() throws IOException;
 
 		class HttpExecuteStream extends InputStream {
+			@Override
 			public int read() throws IOException {
 				execute();
 				return -1;
 			}
 
+			@Override
 			public int read(byte[] b, int off, int len) throws IOException {
 				execute();
 				return -1;
 			}
 
+			@Override
 			public long skip(long n) throws IOException {
 				execute();
 				return 0;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
index 1528c71..f483ec7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
@@ -100,6 +100,7 @@
 			return JGitText.get().transportProtoLocal;
 		}
 
+		@Override
 		public Set<String> getSchemes() {
 			return Collections.singleton("file"); //$NON-NLS-1$
 		}
@@ -132,6 +133,7 @@
 			return new TransportLocal(local, uri, gitDir);
 		}
 
+		@Override
 		public Transport open(URIish uri) throws NotSupportedException,
 				TransportException {
 			File path = FS.DETECTED.resolve(new File("."), uri.getPath()); //$NON-NLS-1$
@@ -189,7 +191,7 @@
 				return createUploadPack(db);
 			}
 		};
-		return new InternalFetchConnection<Void>(this, upf, null, openRepo());
+		return new InternalFetchConnection<>(this, upf, null, openRepo());
 	}
 
 	@Override
@@ -205,7 +207,7 @@
 				return createReceivePack(db);
 			}
 		};
-		return new InternalPushConnection<Void>(this, rpf, null, openRepo());
+		return new InternalPushConnection<>(this, rpf, null, openRepo());
 	}
 
 	@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java
index fa073ae..c46f94b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java
@@ -99,28 +99,34 @@
  */
 public class TransportSftp extends SshTransport implements WalkTransport {
 	static final TransportProtocol PROTO_SFTP = new TransportProtocol() {
+		@Override
 		public String getName() {
 			return JGitText.get().transportProtoSFTP;
 		}
 
+		@Override
 		public Set<String> getSchemes() {
 			return Collections.singleton("sftp"); //$NON-NLS-1$
 		}
 
+		@Override
 		public Set<URIishField> getRequiredFields() {
 			return Collections.unmodifiableSet(EnumSet.of(URIishField.HOST,
 					URIishField.PATH));
 		}
 
+		@Override
 		public Set<URIishField> getOptionalFields() {
 			return Collections.unmodifiableSet(EnumSet.of(URIishField.USER,
 					URIishField.PASS, URIishField.PORT));
 		}
 
+		@Override
 		public int getDefaultPort() {
 			return 22;
 		}
 
+		@Override
 		public Transport open(URIish uri, Repository local, String remoteName)
 				throws NotSupportedException {
 			return new TransportSftp(local, uri);
@@ -225,15 +231,15 @@
 
 		@Override
 		Collection<String> getPackNames() throws IOException {
-			final List<String> packs = new ArrayList<String>();
+			final List<String> packs = new ArrayList<>();
 			try {
 				@SuppressWarnings("unchecked")
 				final Collection<ChannelSftp.LsEntry> list = ftp.ls("pack"); //$NON-NLS-1$
 				final HashMap<String, ChannelSftp.LsEntry> files;
 				final HashMap<String, Integer> mtimes;
 
-				files = new HashMap<String, ChannelSftp.LsEntry>();
-				mtimes = new HashMap<String, Integer>();
+				files = new HashMap<>();
+				mtimes = new HashMap<>();
 
 				for (final ChannelSftp.LsEntry ent : list)
 					files.put(ent.getFilename(), ent);
@@ -251,6 +257,7 @@
 				}
 
 				Collections.sort(packs, new Comparator<String>() {
+					@Override
 					public int compare(final String o1, final String o2) {
 						return mtimes.get(o2).intValue()
 								- mtimes.get(o1).intValue();
@@ -381,7 +388,7 @@
 		}
 
 		Map<String, Ref> readAdvertisedRefs() throws TransportException {
-			final TreeMap<String, Ref> avail = new TreeMap<String, Ref>();
+			final TreeMap<String, Ref> avail = new TreeMap<>();
 			readPackedRefs(avail);
 			readRef(avail, ROOT_DIR + Constants.HEAD, Constants.HEAD);
 			readLooseRefs(avail, ROOT_DIR + "refs", "refs/"); //$NON-NLS-1$ //$NON-NLS-2$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
index 3c5c8da..ffd4d41 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
@@ -560,6 +560,7 @@
 		return r;
 	}
 
+	@Override
 	public int hashCode() {
 		int hc = 0;
 		if (getScheme() != null)
@@ -577,6 +578,7 @@
 		return hc;
 	}
 
+	@Override
 	public boolean equals(final Object obj) {
 		if (!(obj instanceof URIish))
 			return false;
@@ -615,6 +617,7 @@
 		return format(true, false);
 	}
 
+	@Override
 	public String toString() {
 		return format(false, false);
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index 201fb18..58fdd25 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -177,7 +177,7 @@
 		 */
 		public FirstLine(String line) {
 			if (line.length() > 45) {
-				final HashSet<String> opts = new HashSet<String>();
+				final HashSet<String> opts = new HashSet<>();
 				String opt = line.substring(45);
 				if (opt.startsWith(" ")) //$NON-NLS-1$
 					opt = opt.substring(1);
@@ -263,19 +263,19 @@
 	String userAgent;
 
 	/** Raw ObjectIds the client has asked for, before validating them. */
-	private final Set<ObjectId> wantIds = new HashSet<ObjectId>();
+	private final Set<ObjectId> wantIds = new HashSet<>();
 
 	/** Objects the client wants to obtain. */
-	private final Set<RevObject> wantAll = new HashSet<RevObject>();
+	private final Set<RevObject> wantAll = new HashSet<>();
 
 	/** Objects on both sides, these don't have to be sent. */
-	private final Set<RevObject> commonBase = new HashSet<RevObject>();
+	private final Set<RevObject> commonBase = new HashSet<>();
 
 	/** Shallow commits the client already has. */
-	private final Set<ObjectId> clientShallowCommits = new HashSet<ObjectId>();
+	private final Set<ObjectId> clientShallowCommits = new HashSet<>();
 
 	/** Shallow commits on the client which are now becoming unshallow */
-	private final List<ObjectId> unshallowCommits = new ArrayList<ObjectId>();
+	private final List<ObjectId> unshallowCommits = new ArrayList<>();
 
 	/** Desired depth from the client on a shallow request. */
 	private int depth;
@@ -776,7 +776,7 @@
 	}
 
 	private static Set<ObjectId> refIdSet(Collection<Ref> refs) {
-		Set<ObjectId> ids = new HashSet<ObjectId>(refs.size());
+		Set<ObjectId> ids = new HashSet<>(refs.size());
 		for (Ref ref : refs) {
 			ObjectId id = ref.getObjectId();
 			if (id != null) {
@@ -1018,7 +1018,7 @@
 		okToGiveUp = Boolean.FALSE;
 
 		ObjectId last = ObjectId.zeroId();
-		List<ObjectId> peerHas = new ArrayList<ObjectId>(64);
+		List<ObjectId> peerHas = new ArrayList<>(64);
 		for (;;) {
 			String line;
 			try {
@@ -1172,7 +1172,7 @@
 		for (ObjectId obj : wantIds) {
 			if (!advertised.contains(obj)) {
 				if (notAdvertisedWants == null)
-					notAdvertisedWants = new ArrayList<ObjectId>();
+					notAdvertisedWants = new ArrayList<>();
 				notAdvertisedWants.add(obj);
 			}
 		}
@@ -1215,6 +1215,7 @@
 	 */
 	public static final class AdvertisedRequestValidator
 			implements RequestValidator {
+		@Override
 		public void checkWants(UploadPack up, List<ObjectId> wants)
 				throws PackProtocolException, IOException {
 			if (!up.isBiDirectionalPipe())
@@ -1231,6 +1232,7 @@
 	 */
 	public static final class ReachableCommitRequestValidator
 			implements RequestValidator {
+		@Override
 		public void checkWants(UploadPack up, List<ObjectId> wants)
 				throws PackProtocolException, IOException {
 			checkNotAdvertisedWants(up.getRevWalk(), wants,
@@ -1244,6 +1246,7 @@
 	 * @since 3.1
 	 */
 	public static final class TipRequestValidator implements RequestValidator {
+		@Override
 		public void checkWants(UploadPack up, List<ObjectId> wants)
 				throws PackProtocolException, IOException {
 			if (!up.isBiDirectionalPipe())
@@ -1266,6 +1269,7 @@
 	 */
 	public static final class ReachableCommitTipRequestValidator
 			implements RequestValidator {
+		@Override
 		public void checkWants(UploadPack up, List<ObjectId> wants)
 				throws PackProtocolException, IOException {
 			checkNotAdvertisedWants(up.getRevWalk(), wants,
@@ -1279,6 +1283,7 @@
 	 * @since 3.1
 	 */
 	public static final class AnyRequestValidator implements RequestValidator {
+		@Override
 		public void checkWants(UploadPack up, List<ObjectId> wants)
 				throws PackProtocolException, IOException {
 			// All requests are valid.
@@ -1483,7 +1488,7 @@
 			pw.setReuseValidatingObjects(false);
 
 			if (commonBase.isEmpty() && refs != null) {
-				Set<ObjectId> tagTargets = new HashSet<ObjectId>();
+				Set<ObjectId> tagTargets = new HashSet<>();
 				for (Ref ref : refs.values()) {
 					if (ref.getPeeledObjectId() != null)
 						tagTargets.add(ref.getPeeledObjectId());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
index 0588634..afc9965 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
@@ -59,6 +59,7 @@
 public interface UploadPackLogger { // TODO remove in JGit 5.0
 	/** A simple no-op logger. */
 	public static final UploadPackLogger NULL = new UploadPackLogger() {
+		@Override
 		public void onPackStatistics(PackWriter.Statistics stats) {
 			// Do nothing.
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java
index 4ea0319..9a8c12c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java
@@ -84,6 +84,7 @@
 	/**
 	 * @since 3.0
 	 */
+	@Override
 	public void onPackStatistics(PackWriter.Statistics stats) {
 		for (int i = 0; i < count; i++)
 			loggers[i].onPackStatistics(stats);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java
index 4c3fdd8..333e09d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java
@@ -52,6 +52,7 @@
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.KeySpec;
 import java.text.MessageFormat;
+import java.util.Locale;
 import java.util.Properties;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -192,7 +193,7 @@
 
 			// Standard names are not case-sensitive.
 			// http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
-			String cryptoName = cryptoAlg.toUpperCase();
+			String cryptoName = cryptoAlg.toUpperCase(Locale.ROOT);
 
 			if (!cryptoName.startsWith("PBE")) //$NON-NLS-1$
 				throw new GeneralSecurityException(JGitText.get().encryptionOnlyPBE);
@@ -373,7 +374,7 @@
 
 			SecretKey keyBase = factory.generateSecret(keySpec);
 
-			String name = cipherAlgo.toUpperCase();
+			String name = cipherAlgo.toUpperCase(Locale.ROOT);
 			Matcher matcherPBE = Pattern.compile(REGEX_PBE).matcher(name);
 			Matcher matcherTrans = Pattern.compile(REGEX_TRANS).matcher(name);
 			if (matcherPBE.matches()) {
@@ -506,7 +507,7 @@
 		JGitV1(String algo, String pass)
 				throws GeneralSecurityException {
 			super(wrap(algo, pass));
-			String name = cipherAlgo.toUpperCase();
+			String name = cipherAlgo.toUpperCase(Locale.ROOT);
 			Matcher matcherPBE = Pattern.compile(REGEX_PBE).matcher(name);
 			if (!matcherPBE.matches())
 				throw new GeneralSecurityException(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
index 13d4a24..3d60aed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
@@ -197,20 +197,20 @@
 		inserter = local.newObjectInserter();
 		reader = inserter.newReader();
 
-		remotes = new ArrayList<WalkRemoteObjectDatabase>();
+		remotes = new ArrayList<>();
 		remotes.add(w);
 
-		unfetchedPacks = new LinkedList<RemotePack>();
-		packsConsidered = new HashSet<String>();
+		unfetchedPacks = new LinkedList<>();
+		packsConsidered = new HashSet<>();
 
-		noPacksYet = new LinkedList<WalkRemoteObjectDatabase>();
+		noPacksYet = new LinkedList<>();
 		noPacksYet.add(w);
 
-		noAlternatesYet = new LinkedList<WalkRemoteObjectDatabase>();
+		noAlternatesYet = new LinkedList<>();
 		noAlternatesYet.add(w);
 
-		fetchErrors = new HashMap<ObjectId, List<Throwable>>();
-		packLocks = new ArrayList<PackLock>(4);
+		fetchErrors = new HashMap<>();
+		packLocks = new ArrayList<>(4);
 
 		revWalk = new RevWalk(reader);
 		revWalk.setRetainBody(false);
@@ -220,9 +220,10 @@
 		LOCALLY_SEEN = revWalk.newFlag("LOCALLY_SEEN"); //$NON-NLS-1$
 
 		localCommitQueue = new DateRevQueue();
-		workQueue = new LinkedList<ObjectId>();
+		workQueue = new LinkedList<>();
 	}
 
+	@Override
 	public boolean didFetchTestConnectivity() {
 		return true;
 	}
@@ -248,10 +249,12 @@
 		}
 	}
 
+	@Override
 	public Collection<PackLock> getPackLocks() {
 		return packLocks;
 	}
 
+	@Override
 	public void setPackLockMessage(final String message) {
 		lockMessage = message;
 	}
@@ -270,7 +273,7 @@
 
 	private void queueWants(final Collection<Ref> want)
 			throws TransportException {
-		final HashSet<ObjectId> inWorkQueue = new HashSet<ObjectId>();
+		final HashSet<ObjectId> inWorkQueue = new HashSet<>();
 		for (final Ref r : want) {
 			final ObjectId id = r.getObjectId();
 			if (id == null) {
@@ -594,7 +597,7 @@
 
 	private Iterator<ObjectId> swapFetchQueue() {
 		final Iterator<ObjectId> r = workQueue.iterator();
-		workQueue = new LinkedList<ObjectId>();
+		workQueue = new LinkedList<>();
 		return r;
 	}
 
@@ -791,7 +794,7 @@
 		final ObjectId objId = id.copy();
 		List<Throwable> errors = fetchErrors.get(objId);
 		if (errors == null) {
-			errors = new ArrayList<Throwable>(2);
+			errors = new ArrayList<>(2);
 			fetchErrors.put(objId, errors);
 		}
 		errors.add(what);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
index 7b449c7..5c4e14c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
@@ -134,25 +134,27 @@
 		dest = w;
 	}
 
+	@Override
 	public void push(final ProgressMonitor monitor,
 			final Map<String, RemoteRefUpdate> refUpdates)
 			throws TransportException {
 		push(monitor, refUpdates, null);
 	}
 
+	@Override
 	public void push(final ProgressMonitor monitor,
 			final Map<String, RemoteRefUpdate> refUpdates, OutputStream out)
 			throws TransportException {
 		markStartedOperation();
 		packNames = null;
-		newRefs = new TreeMap<String, Ref>(getRefsMap());
-		packedRefUpdates = new ArrayList<RemoteRefUpdate>(refUpdates.size());
+		newRefs = new TreeMap<>(getRefsMap());
+		packedRefUpdates = new ArrayList<>(refUpdates.size());
 
 		// Filter the commands and issue all deletes first. This way we
 		// can correctly handle a directory being cleared out and a new
 		// ref using the directory name being created.
 		//
-		final List<RemoteRefUpdate> updates = new ArrayList<RemoteRefUpdate>();
+		final List<RemoteRefUpdate> updates = new ArrayList<>();
 		for (final RemoteRefUpdate u : refUpdates.values()) {
 			final String n = u.getRemoteName();
 			if (!n.startsWith("refs/") || !Repository.isValidRefName(n)) { //$NON-NLS-1$
@@ -223,8 +225,8 @@
 		try (final PackWriter writer = new PackWriter(transport.getPackConfig(),
 				local.newObjectReader())) {
 
-			final Set<ObjectId> need = new HashSet<ObjectId>();
-			final Set<ObjectId> have = new HashSet<ObjectId>();
+			final Set<ObjectId> need = new HashSet<>();
+			final Set<ObjectId> have = new HashSet<>();
 			for (final RemoteRefUpdate r : updates)
 				need.add(r.getNewObjectId());
 			for (final Ref r : getRefs()) {
@@ -241,7 +243,7 @@
 			if (writer.getObjectCount() == 0)
 				return;
 
-			packNames = new LinkedHashMap<String, String>();
+			packNames = new LinkedHashMap<>();
 			for (final String n : dest.getPackNames())
 				packNames.put(n, n);
 
@@ -277,7 +279,7 @@
 			// way clients are likely to consult the newest pack first,
 			// and discover the most recent objects there.
 			//
-			final ArrayList<String> infoPacks = new ArrayList<String>();
+			final ArrayList<String> infoPacks = new ArrayList<>();
 			infoPacks.add(packName);
 			infoPacks.addAll(packNames.keySet());
 			dest.writeInfoPacks(infoPacks);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
index 24f30ed..17f8c3e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkRemoteObjectDatabase.java
@@ -396,7 +396,7 @@
 			throws IOException {
 		final BufferedReader br = openReader(listPath);
 		try {
-			final Collection<WalkRemoteObjectDatabase> alts = new ArrayList<WalkRemoteObjectDatabase>();
+			final Collection<WalkRemoteObjectDatabase> alts = new ArrayList<>();
 			for (;;) {
 				String line = br.readLine();
 				if (line == null)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
index 09613fd..58081c1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/HttpConnection.java
@@ -73,6 +73,12 @@
 	public static final int HTTP_OK = java.net.HttpURLConnection.HTTP_OK;
 
 	/**
+	 * @see HttpURLConnection#HTTP_MOVED_PERM
+	 * @since 4.7
+	 */
+	public static final int HTTP_MOVED_PERM = java.net.HttpURLConnection.HTTP_MOVED_PERM;
+
+	/**
 	 * @see HttpURLConnection#HTTP_NOT_FOUND
 	 */
 	public static final int HTTP_NOT_FOUND = java.net.HttpURLConnection.HTTP_NOT_FOUND;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java
index ed37fea..534e3d7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnection.java
@@ -94,95 +94,118 @@
 				.openConnection(proxy);
 	}
 
+	@Override
 	public int getResponseCode() throws IOException {
 		return wrappedUrlConnection.getResponseCode();
 	}
 
+	@Override
 	public URL getURL() {
 		return wrappedUrlConnection.getURL();
 	}
 
+	@Override
 	public String getResponseMessage() throws IOException {
 		return wrappedUrlConnection.getResponseMessage();
 	}
 
+	@Override
 	public Map<String, List<String>> getHeaderFields() {
 		return wrappedUrlConnection.getHeaderFields();
 	}
 
+	@Override
 	public void setRequestProperty(String key, String value) {
 		wrappedUrlConnection.setRequestProperty(key, value);
 	}
 
+	@Override
 	public void setRequestMethod(String method) throws ProtocolException {
 		wrappedUrlConnection.setRequestMethod(method);
 	}
 
+	@Override
 	public void setUseCaches(boolean usecaches) {
 		wrappedUrlConnection.setUseCaches(usecaches);
 	}
 
+	@Override
 	public void setConnectTimeout(int timeout) {
 		wrappedUrlConnection.setConnectTimeout(timeout);
 	}
 
+	@Override
 	public void setReadTimeout(int timeout) {
 		wrappedUrlConnection.setReadTimeout(timeout);
 	}
 
+	@Override
 	public String getContentType() {
 		return wrappedUrlConnection.getContentType();
 	}
 
+	@Override
 	public InputStream getInputStream() throws IOException {
 		return wrappedUrlConnection.getInputStream();
 	}
 
+	@Override
 	public String getHeaderField(String name) {
 		return wrappedUrlConnection.getHeaderField(name);
 	}
 
+	@Override
 	public int getContentLength() {
 		return wrappedUrlConnection.getContentLength();
 	}
 
+	@Override
 	public void setInstanceFollowRedirects(boolean followRedirects) {
 		wrappedUrlConnection.setInstanceFollowRedirects(followRedirects);
 	}
 
+	@Override
 	public void setDoOutput(boolean dooutput) {
 		wrappedUrlConnection.setDoOutput(dooutput);
 	}
 
+	@Override
 	public void setFixedLengthStreamingMode(int contentLength) {
 		wrappedUrlConnection.setFixedLengthStreamingMode(contentLength);
 	}
 
+	@Override
 	public OutputStream getOutputStream() throws IOException {
 		return wrappedUrlConnection.getOutputStream();
 	}
 
+	@Override
 	public void setChunkedStreamingMode(int chunklen) {
 		wrappedUrlConnection.setChunkedStreamingMode(chunklen);
 	}
 
+	@Override
 	public String getRequestMethod() {
 		return wrappedUrlConnection.getRequestMethod();
 	}
 
+	@Override
 	public boolean usingProxy() {
 		return wrappedUrlConnection.usingProxy();
 	}
 
+	@Override
 	public void connect() throws IOException {
 		wrappedUrlConnection.connect();
 	}
 
+	@Override
 	public void setHostnameVerifier(HostnameVerifier hostnameverifier) {
 		((HttpsURLConnection) wrappedUrlConnection)
 				.setHostnameVerifier(hostnameverifier);
 	}
 
+	@Override
 	public void configure(KeyManager[] km, TrustManager[] tm,
 			SecureRandom random) throws NoSuchAlgorithmException,
 			KeyManagementException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnectionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnectionFactory.java
index d1c875d..b9f009f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnectionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/http/JDKHttpConnectionFactory.java
@@ -52,10 +52,12 @@
  * @since 3.3
  */
 public class JDKHttpConnectionFactory implements HttpConnectionFactory {
+	@Override
 	public HttpConnection create(URL url) throws IOException {
 		return new JDKHttpConnection(url);
 	}
 
+	@Override
 	public HttpConnection create(URL url, Proxy proxy)
 			throws IOException {
 		return new JDKHttpConnection(url, proxy);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java
index 6964e7f..7654d46 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java
@@ -72,8 +72,8 @@
 
 	/** Initialize an empty file based resolver. */
 	public FileResolver() {
-		exports = new ConcurrentHashMap<String, Repository>();
-		exportBase = new CopyOnWriteArrayList<File>();
+		exports = new ConcurrentHashMap<>();
+		exportBase = new CopyOnWriteArrayList<>();
 	}
 
 	/**
@@ -91,6 +91,7 @@
 		setExportAll(exportAll);
 	}
 
+	@Override
 	public Repository open(final C req, final String name)
 			throws RepositoryNotFoundException, ServiceNotEnabledException {
 		if (isUnreasonableName(name))
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java
index 4cf49f5..73c2ed8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java
@@ -55,6 +55,7 @@
 public interface ReceivePackFactory<C> {
 	/** A factory disabling the ReceivePack service for all repositories */
 	public static final ReceivePackFactory<?> DISABLED = new ReceivePackFactory<Object>() {
+		@Override
 		public ReceivePack create(Object req, Repository db)
 				throws ServiceNotEnabledException {
 			throw new ServiceNotEnabledException();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java
index c7f0d32..80211e5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java
@@ -56,6 +56,7 @@
 public interface RepositoryResolver<C> {
 	/** Resolver configured to open nothing. */
 	public static final RepositoryResolver<?> NONE = new RepositoryResolver<Object>() {
+		@Override
 		public Repository open(Object req, String name)
 				throws RepositoryNotFoundException {
 			throw new RepositoryNotFoundException(name);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java
index f0d2ba8..d7ed0f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java
@@ -55,6 +55,7 @@
 public interface UploadPackFactory<C> {
 	/** A factory disabling the UploadPack service for all repositories. */
 	public static final UploadPackFactory<?> DISABLED = new UploadPackFactory<Object>() {
+		@Override
 		public UploadPack create(Object req, Repository db)
 				throws ServiceNotEnabledException {
 			throw new ServiceNotEnabledException();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
index c038f07..2d6abd1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
@@ -249,6 +249,7 @@
 		return p;
 	}
 
+	@Override
 	public CanonicalTreeParser createSubtreeIterator(final ObjectReader reader)
 			throws IncorrectObjectTypeException, IOException {
 		return createSubtreeIterator(reader, new MutableObjectId());
@@ -280,6 +281,7 @@
 		return currPtr == 0;
 	}
 
+	@Override
 	public boolean eof() {
 		return currPtr == raw.length;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
index b9293eb..c0b29ef 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
@@ -353,6 +353,7 @@
 			dfConflict = null;
 	}
 
+	@Override
 	void stopWalk() throws IOException {
 		if (!needsStopWalk()) {
 			return;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
index 5dfebe9..c54e148 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
@@ -128,7 +128,7 @@
 	 * The filter command as defined in gitattributes. The keys are
 	 * filterName+"."+filterCommandType. E.g. "lfs.clean"
 	 */
-	private Map<String, String> filterCommandsByNameDotType = new HashMap<String, String>();
+	private Map<String, String> filterCommandsByNameDotType = new HashMap<>();
 
 	/**
 	 * @param operationType
@@ -559,6 +559,7 @@
 	 * @return a {@link Set} of {@link Attribute}s that match the current entry.
 	 * @since 4.2
 	 */
+	@Override
 	public Attributes getAttributes() {
 		if (attrs != null)
 			return attrs;
@@ -825,7 +826,7 @@
 				}
 
 				currentHead = t;
-				if (!filter.include(this)) {
+				if (filter.matchFilter(this) == 1) {
 					skipEntriesEqual();
 					continue;
 				}
@@ -1061,6 +1062,60 @@
 	/**
 	 * Test if the supplied path matches the current entry's path.
 	 * <p>
+	 * This method detects if the supplied path is equal to, a subtree of, or
+	 * not similar at all to the current entry. It is faster to use this
+	 * method than to use {@link #getPathString()} to first create a String
+	 * object, then test <code>startsWith</code> or some other type of string
+	 * match function.
+	 * <p>
+	 * If the current entry is a subtree, then all paths within the subtree
+	 * are considered to match it.
+	 *
+	 * @param p
+	 *            path buffer to test. Callers should ensure the path does not
+	 *            end with '/' prior to invocation.
+	 * @param pLen
+	 *            number of bytes from <code>buf</code> to test.
+	 * @return -1 if the current path is a parent to p; 0 if p matches the current
+	 *         path; 1 if the current path is different and will never match
+	 *         again on this tree walk.
+	 * @since 4.7
+	 */
+	public int isPathMatch(final byte[] p, final int pLen) {
+		final AbstractTreeIterator t = currentHead;
+		final byte[] c = t.path;
+		final int cLen = t.pathLen;
+		int ci;
+
+		for (ci = 0; ci < cLen && ci < pLen; ci++) {
+			final int c_value = (c[ci] & 0xff) - (p[ci] & 0xff);
+			if (c_value != 0) {
+				// Paths do not and will never match
+				return 1;
+			}
+		}
+
+		if (ci < cLen) {
+			// Ran out of pattern but we still had current data.
+			// If c[ci] == '/' then pattern matches the subtree.
+			// Otherwise it is a partial match == miss
+			return c[ci] == '/' ? 0 : 1;
+		}
+
+		if (ci < pLen) {
+			// Ran out of current, but we still have pattern data.
+			// If p[ci] == '/' then this subtree is a parent in the pattern,
+			// otherwise it's a miss.
+			return p[ci] == '/' && FileMode.TREE.equals(t.mode) ? -1 : 1;
+		}
+
+		// Both strings are identical.
+		return 0;
+	}
+
+	/**
+	 * Test if the supplied path matches the current entry's path.
+	 * <p>
 	 * This method tests that the supplied path is exactly equal to the current
 	 * entry or is one of its parent directories. It is faster to use this
 	 * method then to use {@link #getPathString()} to first create a String
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index 52477cb..b1b146c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -56,7 +56,6 @@
 import java.nio.CharBuffer;
 import java.nio.charset.CharacterCodingException;
 import java.nio.charset.CharsetEncoder;
-import java.security.MessageDigest;
 import java.text.MessageFormat;
 import java.util.Arrays;
 import java.util.Collections;
@@ -99,6 +98,7 @@
 import org.eclipse.jgit.util.TemporaryBuffer.LocalFile;
 import org.eclipse.jgit.util.io.AutoLFInputStream;
 import org.eclipse.jgit.util.io.EolStreamTypeUtil;
+import org.eclipse.jgit.util.sha1.SHA1;
 
 /**
  * Walks a working directory tree as part of a {@link TreeWalk}.
@@ -364,7 +364,7 @@
 			if (is == null)
 				return zeroid;
 			try {
-				state.initializeDigestAndReadBuffer();
+				state.initializeReadBuffer();
 
 				final long len = e.getLength();
 				InputStream filteredIs = possiblyFilteredInputStream(e, is, len,
@@ -715,6 +715,7 @@
 	}
 
 	private static final Comparator<Entry> ENTRY_CMP = new Comparator<Entry>() {
+		@Override
 		public int compare(Entry a, Entry b) {
 			return Paths.compare(
 					a.encodedName, 0, a.encodedNameLen, a.getMode().getBits(),
@@ -1098,10 +1099,9 @@
 	}
 
 	private byte[] computeHash(InputStream in, long length) throws IOException {
-		final MessageDigest contentDigest = state.contentDigest;
+		SHA1 contentDigest = SHA1.newInstance();
 		final byte[] contentReadBuffer = state.contentReadBuffer;
 
-		contentDigest.reset();
 		contentDigest.update(hblob);
 		contentDigest.update((byte) ' ');
 
@@ -1154,6 +1154,7 @@
 				b.get(encodedName = new byte[encodedNameLen]);
 		}
 
+		@Override
 		public String toString() {
 			return getMode().toString() + " " + getName(); //$NON-NLS-1$
 		}
@@ -1328,9 +1329,6 @@
 		/** File name character encoder. */
 		final CharsetEncoder nameEncoder;
 
-		/** Digest computer for {@link #contentId} computations. */
-		MessageDigest contentDigest;
-
 		/** Buffer used to perform {@link #contentId} computations. */
 		byte[] contentReadBuffer;
 
@@ -1345,9 +1343,8 @@
 			this.nameEncoder = Constants.CHARSET.newEncoder();
 		}
 
-		void initializeDigestAndReadBuffer() {
-			if (contentDigest == null) {
-				contentDigest = Constants.newMessageDigest();
+		void initializeReadBuffer() {
+			if (contentReadBuffer == null) {
 				contentReadBuffer = new byte[BUFFER_SIZE];
 			}
 		}
@@ -1366,7 +1363,7 @@
 				cmd = state.walk
 						.getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN);
 			}
-			cleanFilterCommandHolder = new Holder<String>(cmd);
+			cleanFilterCommandHolder = new Holder<>(cmd);
 		}
 		return cleanFilterCommandHolder.get();
 	}
@@ -1413,7 +1410,7 @@
 					break;
 				}
 			}
-			eolStreamTypeHolder = new Holder<EolStreamType>(type);
+			eolStreamTypeHolder = new Holder<>(type);
 		}
 		return eolStreamTypeHolder.get();
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java
index 112ce8f..7d2b33f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java
@@ -56,6 +56,7 @@
 public class WorkingTreeOptions {
 	/** Key for {@link Config#get(SectionParser)}. */
 	public static final Config.SectionParser<WorkingTreeOptions> KEY = new SectionParser<WorkingTreeOptions>() {
+		@Override
 		public WorkingTreeOptions parse(final Config cfg) {
 			return new WorkingTreeOptions(cfg);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/AndTreeFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/AndTreeFilter.java
index d5e7464..9658166 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/AndTreeFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/AndTreeFilter.java
@@ -128,7 +128,25 @@
 		public boolean include(final TreeWalk walker)
 				throws MissingObjectException, IncorrectObjectTypeException,
 				IOException {
-			return a.include(walker) && b.include(walker);
+			return matchFilter(walker) <= 0;
+		}
+
+		@Override
+		public int matchFilter(TreeWalk walker)
+				throws MissingObjectException, IncorrectObjectTypeException,
+				IOException {
+			final int ra = a.matchFilter(walker);
+			if (ra == 1) {
+				return 1;
+			}
+			final int rb = b.matchFilter(walker);
+			if (rb == 1) {
+				return 1;
+			}
+			if (ra == -1 || rb == -1) {
+				return -1;
+			}
+			return 0;
 		}
 
 		@Override
@@ -159,11 +177,24 @@
 		public boolean include(final TreeWalk walker)
 				throws MissingObjectException, IncorrectObjectTypeException,
 				IOException {
+			return matchFilter(walker) <= 0;
+		}
+
+		@Override
+		public int matchFilter(TreeWalk walker)
+				throws MissingObjectException, IncorrectObjectTypeException,
+				IOException {
+			int m = 0;
 			for (final TreeFilter f : subfilters) {
-				if (!f.include(walker))
-					return false;
+				int r = f.matchFilter(walker);
+				if (r == 1) {
+					return 1;
+				}
+				if (r == -1) {
+					m = -1;
+				}
 			}
-			return true;
+			return m;
 		}
 
 		@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java
index 42725bc..b821a16 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java
@@ -86,11 +86,11 @@
 
 	private final boolean honorIgnores;
 
-	private final Set<String> ignoredPaths = new HashSet<String>();
+	private final Set<String> ignoredPaths = new HashSet<>();
 
-	private final LinkedList<String> untrackedParentFolders = new LinkedList<String>();
+	private final LinkedList<String> untrackedParentFolders = new LinkedList<>();
 
-	private final LinkedList<String> untrackedFolders = new LinkedList<String>();
+	private final LinkedList<String> untrackedFolders = new LinkedList<>();
 
 	/**
 	 * Creates a new instance of this filter. Do not use an instance of this
@@ -292,7 +292,7 @@
 	 *         empty list will be returned.
 	 */
 	public List<String> getUntrackedFolders() {
-		LinkedList<String> ret = new LinkedList<String>(untrackedFolders);
+		LinkedList<String> ret = new LinkedList<>(untrackedFolders);
 		if (!untrackedParentFolders.isEmpty()) {
 			String toBeAdded = untrackedParentFolders.getLast();
 			while (!ret.isEmpty() && ret.getLast().startsWith(toBeAdded))
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/NotTreeFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/NotTreeFilter.java
index 8ec04bb..80c0b87 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/NotTreeFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/NotTreeFilter.java
@@ -78,7 +78,23 @@
 	public boolean include(final TreeWalk walker)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException {
-		return !a.include(walker);
+		return matchFilter(walker) == 0;
+	}
+
+	@Override
+	public int matchFilter(TreeWalk walker)
+			throws MissingObjectException, IncorrectObjectTypeException,
+			IOException {
+		final int r = a.matchFilter(walker);
+		// switch 0 and 1, keep -1 as that defines a subpath that must be
+		// traversed before a final verdict can be made.
+		if (r == 0) {
+			return 1;
+		}
+		if (r == 1) {
+			return 0;
+		}
+		return -1;
 	}
 
 	@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/OrTreeFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/OrTreeFilter.java
index 270633c..2c1a9d4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/OrTreeFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/OrTreeFilter.java
@@ -126,7 +126,25 @@
 		public boolean include(final TreeWalk walker)
 				throws MissingObjectException, IncorrectObjectTypeException,
 				IOException {
-			return a.include(walker) || b.include(walker);
+			return matchFilter(walker) <= 0;
+		}
+
+		@Override
+		public int matchFilter(TreeWalk walker)
+				throws MissingObjectException, IncorrectObjectTypeException,
+				IOException {
+			final int ra = a.matchFilter(walker);
+			if (ra == 0) {
+				return 0;
+			}
+			final int rb = b.matchFilter(walker);
+			if (rb == 0) {
+				return 0;
+			}
+			if (ra == -1 || rb == -1) {
+				return -1;
+			}
+			return 1;
 		}
 
 		@Override
@@ -157,11 +175,24 @@
 		public boolean include(final TreeWalk walker)
 				throws MissingObjectException, IncorrectObjectTypeException,
 				IOException {
+			return matchFilter(walker) <= 0;
+		}
+
+		@Override
+		public int matchFilter(TreeWalk walker)
+				throws MissingObjectException, IncorrectObjectTypeException,
+				IOException {
+			int m = 1;
 			for (final TreeFilter f : subfilters) {
-				if (f.include(walker))
-					return true;
+				int r = f.matchFilter(walker);
+				if (r == 0) {
+					return 0;
+				}
+				if (r == -1) {
+					m = -1;
+				}
 			}
-			return false;
+			return m;
 		}
 
 		@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilter.java
index d85ea8c..445ba15 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilter.java
@@ -97,7 +97,12 @@
 
 	@Override
 	public boolean include(final TreeWalk walker) {
-		return walker.isPathPrefix(pathRaw, pathRaw.length) == 0;
+		return matchFilter(walker) <= 0;
+	}
+
+	@Override
+	public int matchFilter(final TreeWalk walker) {
+		return walker.isPathMatch(pathRaw, pathRaw.length);
 	}
 
 	@Override
@@ -113,6 +118,7 @@
 		return this;
 	}
 
+	@Override
 	@SuppressWarnings("nls")
 	public String toString() {
 		return "PATH(\"" + pathStr + "\")";
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java
index 7601956..174a4f5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java
@@ -173,6 +173,7 @@
 			return this;
 		}
 
+		@Override
 		public String toString() {
 			return "FAST_" + path.toString(); //$NON-NLS-1$
 		}
@@ -267,6 +268,7 @@
 			return this;
 		}
 
+		@Override
 		public String toString() {
 			final StringBuilder r = new StringBuilder();
 			r.append("FAST("); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilter.java
index 7d99e58..2c2fb47 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilter.java
@@ -199,6 +199,34 @@
 			IOException;
 
 	/**
+	 * Determine if the current entry is a parent, a match, or no match.
+	 * <p>
+	 * This method extends the result returned by {@link #include(TreeWalk)}
+	 * with a third option (-1), splitting the value true. This gives the
+	 * application a possibility to distinguish between an exact match
+	 * and the case when a subtree to the current entry might be a match.
+	 *
+	 * @param walker
+	 *            the walker the filter needs to examine.
+	 * @return -1 if the current entry is a parent of the filter but no
+	 *         exact match has been made; 0 if the current entry should
+	 *         be seen by the application; 1 if it should be hidden.
+	 * @throws MissingObjectException
+	 *             as thrown by {@link #include(TreeWalk)}
+	 * @throws IncorrectObjectTypeException
+	 *             as thrown by {@link #include(TreeWalk)}
+	 * @throws IOException
+	 *             as thrown by {@link #include(TreeWalk)}
+	 * @since 4.7
+	 */
+	public int matchFilter(final TreeWalk walker)
+			throws MissingObjectException, IncorrectObjectTypeException,
+			IOException
+	{
+		return include(walker) ? 0 : 1;
+	}
+
+	/**
 	 * Does this tree filter require a recursive walk to match everything?
 	 * <p>
 	 * If this tree filter is matching on full entry path names and its pattern
@@ -220,6 +248,7 @@
 	 *
 	 * @return another copy of this filter, suitable for another thread.
 	 */
+	@Override
 	public abstract TreeFilter clone();
 
 	@Override
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 @@
 			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 @@
 	 * @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/BlockList.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/BlockList.java
index 9d0ad73..c86c588 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/BlockList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/BlockList.java
@@ -309,10 +309,12 @@
 
 		private T[] block = directory[0];
 
+		@Override
 		public boolean hasNext() {
 			return index < size;
 		}
 
+		@Override
 		public T next() {
 			if (size <= index)
 				throw new NoSuchElementException();
@@ -329,6 +331,7 @@
 			return res;
 		}
 
+		@Override
 		public void remove() {
 			if (index == 0)
 				throw new IllegalStateException();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/CachedAuthenticator.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/CachedAuthenticator.java
index 6828185..8677c69 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/CachedAuthenticator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/CachedAuthenticator.java
@@ -51,7 +51,7 @@
 
 /** Abstract authenticator which remembers prior authentications. */
 public abstract class CachedAuthenticator extends Authenticator {
-	private static final Collection<CachedAuthentication> cached = new CopyOnWriteArrayList<CachedAuthentication>();
+	private static final Collection<CachedAuthentication> cached = new CopyOnWriteArrayList<>();
 
 	/**
 	 * Add a cached authentication for future use.
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..68b7130 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;
@@ -376,7 +375,7 @@
 	public File userHome() {
 		Holder<File> p = userHome;
 		if (p == null) {
-			p = new Holder<File>(userHomeImpl());
+			p = new Holder<>(userHomeImpl());
 			userHome = p;
 		}
 		return p.value;
@@ -391,7 +390,7 @@
 	 * @return {@code this}.
 	 */
 	public FS setUserHome(File path) {
-		userHome = new Holder<File>(path);
+		userHome = new Holder<>(path);
 		return this;
 	}
 
@@ -410,6 +409,7 @@
 	protected File userHomeImpl() {
 		final String home = AccessController
 				.doPrivileged(new PrivilegedAction<String>() {
+					@Override
 					public String run() {
 						return System.getProperty("user.home"); //$NON-NLS-1$
 					}
@@ -650,7 +650,7 @@
 	 */
 	public File getGitSystemConfig() {
 		if (gitSystemConfig == null) {
-			gitSystemConfig = new Holder<File>(discoverGitSystemConfig());
+			gitSystemConfig = new Holder<>(discoverGitSystemConfig());
 		}
 		return gitSystemConfig.value;
 	}
@@ -664,7 +664,7 @@
 	 * @since 4.0
 	 */
 	public FS setGitSystemConfig(File configFile) {
-		gitSystemConfig = new Holder<File>(configFile);
+		gitSystemConfig = new Holder<>(configFile);
 		return this;
 	}
 
@@ -1011,16 +1011,13 @@
 		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 +1333,7 @@
 	 * streams.
 	 * </p>
 	 */
-	private static class StreamGobbler implements Callable<Void> {
+	private static class StreamGobbler implements Runnable {
 		private InputStream in;
 
 		private OutputStream out;
@@ -1346,7 +1343,16 @@
 			this.out = output;
 		}
 
-		public Void call() throws IOException {
+		@Override
+		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 +1369,6 @@
 					}
 				}
 			}
-			return null;
 		}
 	}
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
index cb4868c..0780d2b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
@@ -225,7 +225,7 @@
 
 	@Override
 	public ProcessBuilder runInShell(String cmd, String[] args) {
-		List<String> argv = new ArrayList<String>(4 + args.length);
+		List<String> argv = new ArrayList<>(4 + args.length);
 		argv.add("sh"); //$NON-NLS-1$
 		argv.add("-c"); //$NON-NLS-1$
 		argv.add(cmd + " \"$@\""); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
index 0e9172e..0602921 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
@@ -83,18 +83,22 @@
 		super(src);
 	}
 
+	@Override
 	public FS newInstance() {
 		return new FS_Win32(this);
 	}
 
+	@Override
 	public boolean supportsExecute() {
 		return false;
 	}
 
+	@Override
 	public boolean canExecute(final File f) {
 		return false;
 	}
 
+	@Override
 	public boolean setExecute(final File f, final boolean canExec) {
 		return false;
 	}
@@ -158,7 +162,7 @@
 
 	@Override
 	public ProcessBuilder runInShell(String cmd, String[] args) {
-		List<String> argv = new ArrayList<String>(3 + args.length);
+		List<String> argv = new ArrayList<>(3 + args.length);
 		argv.add("cmd.exe"); //$NON-NLS-1$
 		argv.add("/c"); //$NON-NLS-1$
 		argv.add(cmd);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
index f8ea5d0..545cc01 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
@@ -77,6 +77,7 @@
 	public static boolean isCygwin() {
 		final String path = AccessController
 				.doPrivileged(new PrivilegedAction<String>() {
+					@Override
 					public String run() {
 						return System.getProperty("java.library.path"); //$NON-NLS-1$
 					}
@@ -106,10 +107,12 @@
 		super(src);
 	}
 
+	@Override
 	public FS newInstance() {
 		return new FS_Win32_Cygwin(this);
 	}
 
+	@Override
 	public File resolve(final File dir, final String pn) {
 		String useCygPath = System.getProperty("jgit.usecygpath"); //$NON-NLS-1$
 		if (useCygPath != null && useCygPath.equals("true")) { //$NON-NLS-1$
@@ -133,6 +136,7 @@
 	protected File userHomeImpl() {
 		final String home = AccessController
 				.doPrivileged(new PrivilegedAction<String>() {
+					@Override
 					public String run() {
 						return System.getenv("HOME"); //$NON-NLS-1$
 					}
@@ -144,7 +148,7 @@
 
 	@Override
 	public ProcessBuilder runInShell(String cmd, String[] args) {
-		List<String> argv = new ArrayList<String>(4 + args.length);
+		List<String> argv = new ArrayList<>(4 + args.length);
 		argv.add("sh.exe"); //$NON-NLS-1$
 		argv.add("-c"); //$NON-NLS-1$
 		argv.add(cmd + " \"$@\""); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
index aa101f7..1f20e97 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -65,6 +65,7 @@
 import java.text.Normalizer.Form;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 import java.util.regex.Pattern;
 
 import org.eclipse.jgit.internal.JGitText;
@@ -150,8 +151,8 @@
 		if ((options & RECURSIVE) != 0 && fs.isDirectory(f)) {
 			final File[] items = f.listFiles();
 			if (items != null) {
-				List<File> files = new ArrayList<File>();
-				List<File> dirs = new ArrayList<File>();
+				List<File> files = new ArrayList<>();
+				List<File> dirs = new ArrayList<>();
 				for (File c : items)
 					if (c.isFile())
 						files.add(c);
@@ -542,7 +543,28 @@
 	public static boolean isStaleFileHandle(IOException ioe) {
 		String msg = ioe.getMessage();
 		return msg != null
-				&& msg.toLowerCase().matches("stale .*file .*handle"); //$NON-NLS-1$
+				&& msg.toLowerCase(Locale.ROOT)
+						.matches("stale .*file .*handle"); //$NON-NLS-1$
+	}
+
+	/**
+	 * Determine if a throwable or a cause in its causal chain is a Stale NFS
+	 * File Handle
+	 *
+	 * @param throwable
+	 * @return a boolean true if the throwable or a cause in its causal chain is
+	 *         a Stale NFS File Handle
+	 * @since 4.7
+	 */
+	public static boolean isStaleFileHandleInCausalChain(Throwable throwable) {
+		while (throwable != null) {
+			if (throwable instanceof IOException
+					&& isStaleFileHandle((IOException) throwable)) {
+				return true;
+			}
+			throwable = throwable.getCause();
+		}
+		return false;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
index 7bc3c88..da78008 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/GitDateParser.java
@@ -75,8 +75,9 @@
 	private static ThreadLocal<Map<Locale, Map<ParseableSimpleDateFormat, SimpleDateFormat>>> formatCache =
 			new ThreadLocal<Map<Locale, Map<ParseableSimpleDateFormat, SimpleDateFormat>>>() {
 
+		@Override
 		protected Map<Locale, Map<ParseableSimpleDateFormat, SimpleDateFormat>> initialValue() {
-			return new HashMap<Locale, Map<ParseableSimpleDateFormat, SimpleDateFormat>>();
+			return new HashMap<>();
 		}
 	};
 
@@ -90,7 +91,7 @@
 		Map<ParseableSimpleDateFormat, SimpleDateFormat> map = cache
 				.get(locale);
 		if (map == null) {
-			map = new HashMap<ParseableSimpleDateFormat, SimpleDateFormat>();
+			map = new HashMap<>();
 			cache.put(locale, map);
 			return getNewSimpleDateFormat(f, locale, map);
 		}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
index 251381a..13e61a7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
@@ -141,6 +141,12 @@
 	/** The {@code Accept-Encoding} header. */
 	public static final String HDR_ACCEPT_ENCODING = "Accept-Encoding"; //$NON-NLS-1$
 
+	/**
+	 * The {@code Location} header.
+	 * @since 4.7
+	 */
+	public static final String HDR_LOCATION = "Location"; //$NON-NLS-1$
+
 	/** The {@code gzip} encoding value for {@link #HDR_ACCEPT_ENCODING}. */
 	public static final String ENCODING_GZIP = "gzip"; //$NON-NLS-1$
 
@@ -235,6 +241,23 @@
 	}
 
 	/**
+	 * Extract a HTTP header from the response.
+	 *
+	 * @param c
+	 *            connection the header should be obtained from.
+	 * @param headerName
+	 *            the header name
+	 * @return the header value
+	 * @throws IOException
+	 *             communications error prevented obtaining the header.
+	 * @since 4.7
+	 */
+	public static String responseHeader(final HttpConnection c,
+			final String headerName) throws IOException {
+		return c.getHeaderField(headerName);
+	}
+
+	/**
 	 * Determine the proxy server (if any) needed to obtain a URL.
 	 *
 	 * @param proxySelector
@@ -280,15 +303,18 @@
 	}
 
 	private static class DummyX509TrustManager implements X509TrustManager {
+		@Override
 		public X509Certificate[] getAcceptedIssuers() {
 			return null;
 		}
 
+		@Override
 		public void checkClientTrusted(X509Certificate[] certs,
 				String authType) {
 			// no check
 		}
 
+		@Override
 		public void checkServerTrusted(X509Certificate[] certs,
 				String authType) {
 			// no check
@@ -296,6 +322,7 @@
 	}
 
 	private static class DummyHostnameVerifier implements HostnameVerifier {
+		@Override
 		public boolean verify(String hostname, SSLSession session) {
 			// always accept
 			return true;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
index 0d283fd..6cff76c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
@@ -344,7 +344,7 @@
 	 * @since 2.0
 	 */
 	public static List<String> readLines(final String s) {
-		List<String> l = new ArrayList<String>();
+		List<String> l = new ArrayList<>();
 		StringBuilder sb = new StringBuilder();
 		for (int i = 0; i < s.length(); i++) {
 			char c = s.charAt(i);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/IntList.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/IntList.java
index f7688e3..658dd06 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/IntList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/IntList.java
@@ -138,6 +138,7 @@
 		entries = n;
 	}
 
+	@Override
 	public String toString() {
 		final StringBuilder r = new StringBuilder();
 		r.append('[');
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/LongList.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/LongList.java
index dc4004f..e3639f5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/LongList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/LongList.java
@@ -159,6 +159,7 @@
 		entries = n;
 	}
 
+	@Override
 	public String toString() {
 		final StringBuilder r = new StringBuilder();
 		r.append('[');
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawCharSequence.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawCharSequence.java
index 4eeecdb..e85bd65 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawCharSequence.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawCharSequence.java
@@ -74,14 +74,17 @@
 		endPtr = end;
 	}
 
+	@Override
 	public char charAt(final int index) {
 		return (char) (buffer[startPtr + index] & 0xff);
 	}
 
+	@Override
 	public int length() {
 		return endPtr - startPtr;
 	}
 
+	@Override
 	public CharSequence subSequence(final int start, final int end) {
 		return new RawCharSequence(buffer, startPtr + start, startPtr + end);
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java
index 0853e95..1597817 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java
@@ -68,7 +68,7 @@
  *            the type of reference being stored in the collection.
  */
 public class RefList<T extends Ref> implements Iterable<Ref> {
-	private static final RefList<Ref> EMPTY = new RefList<Ref>(new Ref[0], 0);
+	private static final RefList<Ref> EMPTY = new RefList<>(new Ref[0], 0);
 
 	/**
 	 * @return an empty unmodifiable reference list.
@@ -100,20 +100,24 @@
 		this.cnt = src.cnt;
 	}
 
+	@Override
 	public Iterator<Ref> iterator() {
 		return new Iterator<Ref>() {
 			private int idx;
 
+			@Override
 			public boolean hasNext() {
 				return idx < cnt;
 			}
 
+			@Override
 			public Ref next() {
 				if (idx < cnt)
 					return list[idx++];
 				throw new NoSuchElementException();
 			}
 
+			@Override
 			public void remove() {
 				throw new UnsupportedOperationException();
 			}
@@ -209,7 +213,7 @@
 	 * @return a new builder with the first {@code n} elements already added.
 	 */
 	public final Builder<T> copy(int n) {
-		Builder<T> r = new Builder<T>(Math.max(16, n));
+		Builder<T> r = new Builder<>(Math.max(16, n));
 		r.addAll(list, 0, n);
 		return r;
 	}
@@ -230,7 +234,7 @@
 		Ref[] newList = new Ref[cnt];
 		System.arraycopy(list, 0, newList, 0, cnt);
 		newList[idx] = ref;
-		return new RefList<T>(newList, cnt);
+		return new RefList<>(newList, cnt);
 	}
 
 	/**
@@ -257,7 +261,7 @@
 		newList[idx] = ref;
 		if (idx < cnt)
 			System.arraycopy(list, idx, newList, idx + 1, cnt - idx);
-		return new RefList<T>(newList, cnt + 1);
+		return new RefList<>(newList, cnt + 1);
 	}
 
 	/**
@@ -278,7 +282,7 @@
 			System.arraycopy(list, 0, newList, 0, idx);
 		if (idx + 1 < cnt)
 			System.arraycopy(list, idx + 1, newList, idx, cnt - (idx + 1));
-		return new RefList<T>(newList, cnt - 1);
+		return new RefList<>(newList, cnt - 1);
 	}
 
 	/**
@@ -427,7 +431,7 @@
 
 		/** @return an unmodifiable list using this collection's backing array. */
 		public RefList<T> toRefList() {
-			return new RefList<T>(list, size);
+			return new RefList<>(list, size);
 		}
 
 		@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java
index c72727b..510e818 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java
@@ -304,12 +304,14 @@
 			}
 		}
 
+		@Override
 		public boolean hasNext() {
 			if (next == null)
 				next = peek();
 			return next != null;
 		}
 
+		@Override
 		public Entry<String, Ref> next() {
 			if (hasNext()) {
 				Entry<String, Ref> r = next;
@@ -367,6 +369,7 @@
 			return null;
 		}
 
+		@Override
 		public void remove() {
 			throw new UnsupportedOperationException();
 		}
@@ -379,14 +382,17 @@
 			this.ref = ref;
 		}
 
+		@Override
 		public String getKey() {
 			return toMapKey(ref);
 		}
 
+		@Override
 		public Ref getValue() {
 			return ref;
 		}
 
+		@Override
 		public Ref setValue(Ref value) {
 			Ref prior = put(getKey(), value);
 			ref = value;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index b36fc23..2cd8035 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -87,22 +87,27 @@
 	private static class Default extends SystemReader {
 		private volatile String hostname;
 
+		@Override
 		public String getenv(String variable) {
 			return System.getenv(variable);
 		}
 
+		@Override
 		public String getProperty(String key) {
 			return System.getProperty(key);
 		}
 
+		@Override
 		public FileBasedConfig openSystemConfig(Config parent, FS fs) {
 			File configFile = fs.getGitSystemConfig();
 			if (configFile == null) {
 				return new FileBasedConfig(null, fs) {
+					@Override
 					public void load() {
 						// empty, do not load
 					}
 
+					@Override
 					public boolean isOutdated() {
 						// regular class would bomb here
 						return false;
@@ -112,11 +117,13 @@
 			return new FileBasedConfig(parent, configFile, fs);
 		}
 
+		@Override
 		public FileBasedConfig openUserConfig(Config parent, FS fs) {
 			final File home = fs.userHome();
 			return new FileBasedConfig(parent, new File(home, ".gitconfig"), fs); //$NON-NLS-1$
 		}
 
+		@Override
 		public String getHostname() {
 			if (hostname == null) {
 				try {
@@ -331,6 +338,7 @@
 
 	private String getOsName() {
 		return AccessController.doPrivileged(new PrivilegedAction<String>() {
+			@Override
 			public String run() {
 				return getProperty("os.name"); //$NON-NLS-1$
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
index 57bcfbd..e3f1916 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
@@ -325,7 +325,7 @@
 		if (blocks != null)
 			blocks.clear();
 		else
-			blocks = new ArrayList<Block>(initialBlocks);
+			blocks = new ArrayList<>(initialBlocks);
 		blocks.add(new Block(Math.min(inCoreLimit, Block.SZ)));
 	}
 
@@ -363,6 +363,7 @@
 		overflow.write(last.buffer, 0, last.count);
 	}
 
+	@Override
 	public void close() throws IOException {
 		if (overflow != null) {
 			try {
@@ -441,11 +442,13 @@
 			this.directory = directory;
 		}
 
+		@Override
 		protected OutputStream overflow() throws IOException {
 			onDiskFile = File.createTempFile("jgit_", ".buf", directory); //$NON-NLS-1$ //$NON-NLS-2$
 			return new BufferedOutputStream(new FileOutputStream(onDiskFile));
 		}
 
+		@Override
 		public long length() {
 			if (onDiskFile == null) {
 				return super.length();
@@ -453,6 +456,7 @@
 			return onDiskFile.length();
 		}
 
+		@Override
 		public byte[] toByteArray() throws IOException {
 			if (onDiskFile == null) {
 				return super.toByteArray();
@@ -471,6 +475,7 @@
 			return out;
 		}
 
+		@Override
 		public void writeTo(final OutputStream os, ProgressMonitor pm)
 				throws IOException {
 			if (onDiskFile == null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/InterruptTimer.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/InterruptTimer.java
index 1024380..0c5edb0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/InterruptTimer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/InterruptTimer.java
@@ -176,6 +176,7 @@
 			callingThread = Thread.currentThread();
 		}
 
+		@Override
 		public synchronized void run() {
 			while (!terminated && callingThread.isAlive()) {
 				try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TeeInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TeeInputStream.java
index 6adadbb..a7a0347 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TeeInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/TeeInputStream.java
@@ -115,6 +115,7 @@
 		return n;
 	}
 
+	@Override
 	public void close() throws IOException {
 		byte[] b = skipBuffer();
 		for (;;) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java
index c34c1fb..5eca0d9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/ThrowingPrintWriter.java
@@ -69,6 +69,7 @@
 	public ThrowingPrintWriter(Writer out) {
 		this.out = out;
 		LF = AccessController.doPrivileged(new PrivilegedAction<String>() {
+			@Override
 			public String run() {
 				return SystemReader.getInstance().getProperty("line.separator"); //$NON-NLS-1$
 			}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java
index 0319afd..1c46310 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/UnionInputStream.java
@@ -67,7 +67,7 @@
 		}
 	};
 
-	private final LinkedList<InputStream> streams = new LinkedList<InputStream>();
+	private final LinkedList<InputStream> streams = new LinkedList<>();
 
 	/** Create an empty InputStream that is currently at EOF state. */
 	public UnionInputStream() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/SHA1.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/SHA1.java
new file mode 100644
index 0000000..5449711
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/SHA1.java
@@ -0,0 +1,601 @@
+/*
+ * Copyright (C) 2017, Google Inc.
+ * 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.util.sha1;
+
+import static java.lang.Integer.lowestOneBit;
+import static java.lang.Integer.numberOfTrailingZeros;
+import static java.lang.Integer.rotateLeft;
+import static java.lang.Integer.rotateRight;
+
+import java.util.Arrays;
+
+import org.eclipse.jgit.lib.MutableObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.SystemReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Pure Java implementation of SHA-1 from FIPS 180-1 / RFC 3174.
+ *
+ * <p>
+ * See <a href="https://tools.ietf.org/html/rfc3174">RFC 3174</a>.
+ * <p>
+ * Unlike MessageDigest, this implementation includes the algorithm used by
+ * {@code sha1dc} to detect cryptanalytic collision attacks against SHA-1, such
+ * as the one used by <a href="https://shattered.it/">SHAttered</a>. See
+ * <a href="https://github.com/cr-marcstevens/sha1collisiondetection">
+ * sha1collisiondetection</a> for more information.
+ * <p>
+ * When detectCollision is true (default), this implementation throws
+ * {@link Sha1CollisionException} from any digest method if a potential
+ * collision was detected.
+ *
+ * @since 4.7
+ */
+public class SHA1 {
+	private static Logger LOG = LoggerFactory.getLogger(SHA1.class);
+	private static final boolean DETECT_COLLISIONS;
+
+	static {
+		SystemReader sr = SystemReader.getInstance();
+		String v = sr.getProperty("org.eclipse.jgit.util.sha1.detectCollision"); //$NON-NLS-1$
+		DETECT_COLLISIONS = v != null ? Boolean.parseBoolean(v) : true;
+	}
+
+	/** @return a new context to compute a SHA-1 hash of data. */
+	public static SHA1 newInstance() {
+		return new SHA1();
+	}
+
+	private final State h = new State();
+	private final int[] w = new int[80];
+
+	/** Buffer to accumulate partial blocks to 64 byte alignment. */
+	private final byte[] buffer = new byte[64];
+
+	/** Total number of bytes in the message. */
+	private long length;
+
+	private boolean detectCollision = DETECT_COLLISIONS;
+	private boolean foundCollision;
+
+	private final int[] w2 = new int[80];
+	private final State state58 = new State();
+	private final State state65 = new State();
+	private final State hIn = new State();
+	private final State hTmp = new State();
+
+	private SHA1() {
+		h.init();
+	}
+
+	/**
+	 * Enable likely collision detection.
+	 * <p>
+	 * Default is {@code true}.
+	 * <p>
+	 * May also be set by system property:
+	 * {@code -Dorg.eclipse.jgit.util.sha1.detectCollision=true}.
+	 *
+	 * @param detect
+	 * @return {@code this}
+	 */
+	public SHA1 setDetectCollision(boolean detect) {
+		detectCollision = detect;
+		return this;
+	}
+
+	/**
+	 * Update the digest computation by adding a byte.
+	 *
+	 * @param b
+	 */
+	public void update(byte b) {
+		int bufferLen = (int) (length & 63);
+		length++;
+		buffer[bufferLen] = b;
+		if (bufferLen == 63) {
+			compress(buffer, 0);
+		}
+	}
+
+	/**
+	 * Update the digest computation by adding bytes to the message.
+	 *
+	 * @param in
+	 *            input array of bytes.
+	 */
+	public void update(byte[] in) {
+		update(in, 0, in.length);
+	}
+
+	/**
+	 * Update the digest computation by adding bytes to the message.
+	 *
+	 * @param in
+	 *            input array of bytes.
+	 * @param p
+	 *            offset to start at from {@code in}.
+	 * @param len
+	 *            number of bytes to hash.
+	 */
+	public void update(byte[] in, int p, int len) {
+		// SHA-1 compress can only process whole 64 byte blocks.
+		// Hold partial updates in buffer, whose length is the low bits.
+		int bufferLen = (int) (length & 63);
+		length += len;
+
+		if (bufferLen > 0) {
+			int n = Math.min(64 - bufferLen, len);
+			System.arraycopy(in, p, buffer, bufferLen, n);
+			p += n;
+			len -= n;
+			if (bufferLen + n < 64) {
+				return;
+			}
+			compress(buffer, 0);
+		}
+		while (len >= 64) {
+			compress(in, p);
+			p += 64;
+			len -= 64;
+		}
+		if (len > 0) {
+			System.arraycopy(in, p, buffer, 0, len);
+		}
+	}
+
+	private void compress(byte[] block, int p) {
+		initBlock(block, p);
+		int ubcDvMask = detectCollision ? UbcCheck.check(w) : 0;
+		compress();
+
+		while (ubcDvMask != 0) {
+			int b = numberOfTrailingZeros(lowestOneBit(ubcDvMask));
+			UbcCheck.DvInfo dv = UbcCheck.DV[b];
+			for (int i = 0; i < 80; i++) {
+				w2[i] = w[i] ^ dv.dm[i];
+			}
+			recompress(dv.testt);
+			if (eq(hTmp, h)) {
+				foundCollision = true;
+				break;
+			}
+			ubcDvMask &= ~(1 << b);
+		}
+	}
+
+	private void initBlock(byte[] block, int p) {
+		for (int t = 0; t < 16; t++) {
+			w[t] = NB.decodeInt32(block, p + (t << 2));
+		}
+
+		// RFC 3174 6.1.b, extend state vector to 80 words.
+		for (int t = 16; t < 80; t++) {
+			int x = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
+			w[t] = rotateLeft(x, 1); // S^1(...)
+		}
+	}
+
+	private void compress() {
+		// Method 1 from RFC 3174 section 6.1.
+		// Method 2 (circular queue of 16 words) is slower.
+		int a = h.a, b = h.b, c = h.c, d = h.d, e = h.e;
+
+		// @formatter:off
+		 e += s1(a, b, c, d,w[ 0]);  b = rotateLeft( b, 30);
+		 d += s1(e, a, b, c,w[ 1]);  a = rotateLeft( a, 30);
+		 c += s1(d, e, a, b,w[ 2]);  e = rotateLeft( e, 30);
+		 b += s1(c, d, e, a,w[ 3]);  d = rotateLeft( d, 30);
+		 a += s1(b, c, d, e,w[ 4]);  c = rotateLeft( c, 30);
+		 e += s1(a, b, c, d,w[ 5]);  b = rotateLeft( b, 30);
+		 d += s1(e, a, b, c,w[ 6]);  a = rotateLeft( a, 30);
+		 c += s1(d, e, a, b,w[ 7]);  e = rotateLeft( e, 30);
+		 b += s1(c, d, e, a,w[ 8]);  d = rotateLeft( d, 30);
+		 a += s1(b, c, d, e,w[ 9]);  c = rotateLeft( c, 30);
+		 e += s1(a, b, c, d,w[ 10]);  b = rotateLeft( b, 30);
+		 d += s1(e, a, b, c,w[ 11]);  a = rotateLeft( a, 30);
+		 c += s1(d, e, a, b,w[ 12]);  e = rotateLeft( e, 30);
+		 b += s1(c, d, e, a,w[ 13]);  d = rotateLeft( d, 30);
+		 a += s1(b, c, d, e,w[ 14]);  c = rotateLeft( c, 30);
+		 e += s1(a, b, c, d,w[ 15]);  b = rotateLeft( b, 30);
+		 d += s1(e, a, b, c,w[ 16]);  a = rotateLeft( a, 30);
+		 c += s1(d, e, a, b,w[ 17]);  e = rotateLeft( e, 30);
+		 b += s1(c, d, e, a,w[ 18]);  d = rotateLeft( d, 30);
+		 a += s1(b, c, d, e,w[ 19]);  c = rotateLeft( c, 30);
+
+		 e += s2(a, b, c, d,w[ 20]);  b = rotateLeft( b, 30);
+		 d += s2(e, a, b, c,w[ 21]);  a = rotateLeft( a, 30);
+		 c += s2(d, e, a, b,w[ 22]);  e = rotateLeft( e, 30);
+		 b += s2(c, d, e, a,w[ 23]);  d = rotateLeft( d, 30);
+		 a += s2(b, c, d, e,w[ 24]);  c = rotateLeft( c, 30);
+		 e += s2(a, b, c, d,w[ 25]);  b = rotateLeft( b, 30);
+		 d += s2(e, a, b, c,w[ 26]);  a = rotateLeft( a, 30);
+		 c += s2(d, e, a, b,w[ 27]);  e = rotateLeft( e, 30);
+		 b += s2(c, d, e, a,w[ 28]);  d = rotateLeft( d, 30);
+		 a += s2(b, c, d, e,w[ 29]);  c = rotateLeft( c, 30);
+		 e += s2(a, b, c, d,w[ 30]);  b = rotateLeft( b, 30);
+		 d += s2(e, a, b, c,w[ 31]);  a = rotateLeft( a, 30);
+		 c += s2(d, e, a, b,w[ 32]);  e = rotateLeft( e, 30);
+		 b += s2(c, d, e, a,w[ 33]);  d = rotateLeft( d, 30);
+		 a += s2(b, c, d, e,w[ 34]);  c = rotateLeft( c, 30);
+		 e += s2(a, b, c, d,w[ 35]);  b = rotateLeft( b, 30);
+		 d += s2(e, a, b, c,w[ 36]);  a = rotateLeft( a, 30);
+		 c += s2(d, e, a, b,w[ 37]);  e = rotateLeft( e, 30);
+		 b += s2(c, d, e, a,w[ 38]);  d = rotateLeft( d, 30);
+		 a += s2(b, c, d, e,w[ 39]);  c = rotateLeft( c, 30);
+
+		 e += s3(a, b, c, d,w[ 40]);  b = rotateLeft( b, 30);
+		 d += s3(e, a, b, c,w[ 41]);  a = rotateLeft( a, 30);
+		 c += s3(d, e, a, b,w[ 42]);  e = rotateLeft( e, 30);
+		 b += s3(c, d, e, a,w[ 43]);  d = rotateLeft( d, 30);
+		 a += s3(b, c, d, e,w[ 44]);  c = rotateLeft( c, 30);
+		 e += s3(a, b, c, d,w[ 45]);  b = rotateLeft( b, 30);
+		 d += s3(e, a, b, c,w[ 46]);  a = rotateLeft( a, 30);
+		 c += s3(d, e, a, b,w[ 47]);  e = rotateLeft( e, 30);
+		 b += s3(c, d, e, a,w[ 48]);  d = rotateLeft( d, 30);
+		 a += s3(b, c, d, e,w[ 49]);  c = rotateLeft( c, 30);
+		 e += s3(a, b, c, d,w[ 50]);  b = rotateLeft( b, 30);
+		 d += s3(e, a, b, c,w[ 51]);  a = rotateLeft( a, 30);
+		 c += s3(d, e, a, b,w[ 52]);  e = rotateLeft( e, 30);
+		 b += s3(c, d, e, a,w[ 53]);  d = rotateLeft( d, 30);
+		 a += s3(b, c, d, e,w[ 54]);  c = rotateLeft( c, 30);
+		 e += s3(a, b, c, d,w[ 55]);  b = rotateLeft( b, 30);
+		 d += s3(e, a, b, c,w[ 56]);  a = rotateLeft( a, 30);
+		 c += s3(d, e, a, b,w[ 57]);  e = rotateLeft( e, 30);
+		state58.save(a, b, c, d, e);
+		 b += s3(c, d, e, a,w[ 58]);  d = rotateLeft( d, 30);
+		 a += s3(b, c, d, e,w[ 59]);  c = rotateLeft( c, 30);
+
+		 e += s4(a, b, c, d,w[ 60]);  b = rotateLeft( b, 30);
+		 d += s4(e, a, b, c,w[ 61]);  a = rotateLeft( a, 30);
+		 c += s4(d, e, a, b,w[ 62]);  e = rotateLeft( e, 30);
+		 b += s4(c, d, e, a,w[ 63]);  d = rotateLeft( d, 30);
+		 a += s4(b, c, d, e,w[ 64]);  c = rotateLeft( c, 30);
+		state65.save(a, b, c, d, e);
+		 e += s4(a, b, c, d,w[ 65]);  b = rotateLeft( b, 30);
+		 d += s4(e, a, b, c,w[ 66]);  a = rotateLeft( a, 30);
+		 c += s4(d, e, a, b,w[ 67]);  e = rotateLeft( e, 30);
+		 b += s4(c, d, e, a,w[ 68]);  d = rotateLeft( d, 30);
+		 a += s4(b, c, d, e,w[ 69]);  c = rotateLeft( c, 30);
+		 e += s4(a, b, c, d,w[ 70]);  b = rotateLeft( b, 30);
+		 d += s4(e, a, b, c,w[ 71]);  a = rotateLeft( a, 30);
+		 c += s4(d, e, a, b,w[ 72]);  e = rotateLeft( e, 30);
+		 b += s4(c, d, e, a,w[ 73]);  d = rotateLeft( d, 30);
+		 a += s4(b, c, d, e,w[ 74]);  c = rotateLeft( c, 30);
+		 e += s4(a, b, c, d,w[ 75]);  b = rotateLeft( b, 30);
+		 d += s4(e, a, b, c,w[ 76]);  a = rotateLeft( a, 30);
+		 c += s4(d, e, a, b,w[ 77]);  e = rotateLeft( e, 30);
+		 b += s4(c, d, e, a,w[ 78]);  d = rotateLeft( d, 30);
+		 a += s4(b, c, d, e,w[ 79]);  c = rotateLeft( c, 30);
+
+		// @formatter:on
+		h.save(h.a + a, h.b + b, h.c + c, h.d + d, h.e + e);
+	}
+
+	private void recompress(int t) {
+		State s;
+		if (t == 58) {
+			s = state58;
+		} else if (t == 65) {
+			s = state65;
+		} else {
+			throw new IllegalStateException();
+		}
+		int a = s.a, b = s.b, c = s.c, d = s.d, e = s.e;
+
+		// @formatter:off
+	  if (t == 65) {
+		{ c = rotateRight( c, 30);  a -= s4(b, c, d, e,w2[ 64]);}
+		{ d = rotateRight( d, 30);  b -= s4(c, d, e, a,w2[ 63]);}
+		{ e = rotateRight( e, 30);  c -= s4(d, e, a, b,w2[ 62]);}
+		{ a = rotateRight( a, 30);  d -= s4(e, a, b, c,w2[ 61]);}
+		{ b = rotateRight( b, 30);  e -= s4(a, b, c, d,w2[ 60]);}
+
+		{ c = rotateRight( c, 30);  a -= s3(b, c, d, e,w2[ 59]);}
+		{ d = rotateRight( d, 30);  b -= s3(c, d, e, a,w2[ 58]);}
+	  }
+		{ e = rotateRight( e, 30);  c -= s3(d, e, a, b,w2[ 57]);}
+		{ a = rotateRight( a, 30);  d -= s3(e, a, b, c,w2[ 56]);}
+		{ b = rotateRight( b, 30);  e -= s3(a, b, c, d,w2[ 55]);}
+		{ c = rotateRight( c, 30);  a -= s3(b, c, d, e,w2[ 54]);}
+		{ d = rotateRight( d, 30);  b -= s3(c, d, e, a,w2[ 53]);}
+		{ e = rotateRight( e, 30);  c -= s3(d, e, a, b,w2[ 52]);}
+		{ a = rotateRight( a, 30);  d -= s3(e, a, b, c,w2[ 51]);}
+		{ b = rotateRight( b, 30);  e -= s3(a, b, c, d,w2[ 50]);}
+		{ c = rotateRight( c, 30);  a -= s3(b, c, d, e,w2[ 49]);}
+		{ d = rotateRight( d, 30);  b -= s3(c, d, e, a,w2[ 48]);}
+		{ e = rotateRight( e, 30);  c -= s3(d, e, a, b,w2[ 47]);}
+		{ a = rotateRight( a, 30);  d -= s3(e, a, b, c,w2[ 46]);}
+		{ b = rotateRight( b, 30);  e -= s3(a, b, c, d,w2[ 45]);}
+		{ c = rotateRight( c, 30);  a -= s3(b, c, d, e,w2[ 44]);}
+		{ d = rotateRight( d, 30);  b -= s3(c, d, e, a,w2[ 43]);}
+		{ e = rotateRight( e, 30);  c -= s3(d, e, a, b,w2[ 42]);}
+		{ a = rotateRight( a, 30);  d -= s3(e, a, b, c,w2[ 41]);}
+		{ b = rotateRight( b, 30);  e -= s3(a, b, c, d,w2[ 40]);}
+
+		{ c = rotateRight( c, 30);  a -= s2(b, c, d, e,w2[ 39]);}
+		{ d = rotateRight( d, 30);  b -= s2(c, d, e, a,w2[ 38]);}
+		{ e = rotateRight( e, 30);  c -= s2(d, e, a, b,w2[ 37]);}
+		{ a = rotateRight( a, 30);  d -= s2(e, a, b, c,w2[ 36]);}
+		{ b = rotateRight( b, 30);  e -= s2(a, b, c, d,w2[ 35]);}
+		{ c = rotateRight( c, 30);  a -= s2(b, c, d, e,w2[ 34]);}
+		{ d = rotateRight( d, 30);  b -= s2(c, d, e, a,w2[ 33]);}
+		{ e = rotateRight( e, 30);  c -= s2(d, e, a, b,w2[ 32]);}
+		{ a = rotateRight( a, 30);  d -= s2(e, a, b, c,w2[ 31]);}
+		{ b = rotateRight( b, 30);  e -= s2(a, b, c, d,w2[ 30]);}
+		{ c = rotateRight( c, 30);  a -= s2(b, c, d, e,w2[ 29]);}
+		{ d = rotateRight( d, 30);  b -= s2(c, d, e, a,w2[ 28]);}
+		{ e = rotateRight( e, 30);  c -= s2(d, e, a, b,w2[ 27]);}
+		{ a = rotateRight( a, 30);  d -= s2(e, a, b, c,w2[ 26]);}
+		{ b = rotateRight( b, 30);  e -= s2(a, b, c, d,w2[ 25]);}
+		{ c = rotateRight( c, 30);  a -= s2(b, c, d, e,w2[ 24]);}
+		{ d = rotateRight( d, 30);  b -= s2(c, d, e, a,w2[ 23]);}
+		{ e = rotateRight( e, 30);  c -= s2(d, e, a, b,w2[ 22]);}
+		{ a = rotateRight( a, 30);  d -= s2(e, a, b, c,w2[ 21]);}
+		{ b = rotateRight( b, 30);  e -= s2(a, b, c, d,w2[ 20]);}
+
+		{ c = rotateRight( c, 30);  a -= s1(b, c, d, e,w2[ 19]);}
+		{ d = rotateRight( d, 30);  b -= s1(c, d, e, a,w2[ 18]);}
+		{ e = rotateRight( e, 30);  c -= s1(d, e, a, b,w2[ 17]);}
+		{ a = rotateRight( a, 30);  d -= s1(e, a, b, c,w2[ 16]);}
+		{ b = rotateRight( b, 30);  e -= s1(a, b, c, d,w2[ 15]);}
+		{ c = rotateRight( c, 30);  a -= s1(b, c, d, e,w2[ 14]);}
+		{ d = rotateRight( d, 30);  b -= s1(c, d, e, a,w2[ 13]);}
+		{ e = rotateRight( e, 30);  c -= s1(d, e, a, b,w2[ 12]);}
+		{ a = rotateRight( a, 30);  d -= s1(e, a, b, c,w2[ 11]);}
+		{ b = rotateRight( b, 30);  e -= s1(a, b, c, d,w2[ 10]);}
+		{ c = rotateRight( c, 30);  a -= s1(b, c, d, e,w2[ 9]);}
+		{ d = rotateRight( d, 30);  b -= s1(c, d, e, a,w2[ 8]);}
+		{ e = rotateRight( e, 30);  c -= s1(d, e, a, b,w2[ 7]);}
+		{ a = rotateRight( a, 30);  d -= s1(e, a, b, c,w2[ 6]);}
+		{ b = rotateRight( b, 30);  e -= s1(a, b, c, d,w2[ 5]);}
+		{ c = rotateRight( c, 30);  a -= s1(b, c, d, e,w2[ 4]);}
+		{ d = rotateRight( d, 30);  b -= s1(c, d, e, a,w2[ 3]);}
+		{ e = rotateRight( e, 30);  c -= s1(d, e, a, b,w2[ 2]);}
+		{ a = rotateRight( a, 30);  d -= s1(e, a, b, c,w2[ 1]);}
+		{ b = rotateRight( b, 30);  e -= s1(a, b, c, d,w2[ 0]);}
+
+		hIn.save(a, b, c, d, e);
+		a = s.a; b = s.b; c = s.c; d = s.d; e = s.e;
+
+	  if (t == 58) {
+		{ b += s3(c, d, e, a,w2[ 58]);  d = rotateLeft( d, 30);}
+		{ a += s3(b, c, d, e,w2[ 59]);  c = rotateLeft( c, 30);}
+
+		{ e += s4(a, b, c, d,w2[ 60]);  b = rotateLeft( b, 30);}
+		{ d += s4(e, a, b, c,w2[ 61]);  a = rotateLeft( a, 30);}
+		{ c += s4(d, e, a, b,w2[ 62]);  e = rotateLeft( e, 30);}
+		{ b += s4(c, d, e, a,w2[ 63]);  d = rotateLeft( d, 30);}
+		{ a += s4(b, c, d, e,w2[ 64]);  c = rotateLeft( c, 30);}
+	  }
+		{ e += s4(a, b, c, d,w2[ 65]);  b = rotateLeft( b, 30);}
+		{ d += s4(e, a, b, c,w2[ 66]);  a = rotateLeft( a, 30);}
+		{ c += s4(d, e, a, b,w2[ 67]);  e = rotateLeft( e, 30);}
+		{ b += s4(c, d, e, a,w2[ 68]);  d = rotateLeft( d, 30);}
+		{ a += s4(b, c, d, e,w2[ 69]);  c = rotateLeft( c, 30);}
+		{ e += s4(a, b, c, d,w2[ 70]);  b = rotateLeft( b, 30);}
+		{ d += s4(e, a, b, c,w2[ 71]);  a = rotateLeft( a, 30);}
+		{ c += s4(d, e, a, b,w2[ 72]);  e = rotateLeft( e, 30);}
+		{ b += s4(c, d, e, a,w2[ 73]);  d = rotateLeft( d, 30);}
+		{ a += s4(b, c, d, e,w2[ 74]);  c = rotateLeft( c, 30);}
+		{ e += s4(a, b, c, d,w2[ 75]);  b = rotateLeft( b, 30);}
+		{ d += s4(e, a, b, c,w2[ 76]);  a = rotateLeft( a, 30);}
+		{ c += s4(d, e, a, b,w2[ 77]);  e = rotateLeft( e, 30);}
+		{ b += s4(c, d, e, a,w2[ 78]);  d = rotateLeft( d, 30);}
+		{ a += s4(b, c, d, e,w2[ 79]);  c = rotateLeft( c, 30);}
+
+		// @formatter:on
+		hTmp.save(hIn.a + a, hIn.b + b, hIn.c + c, hIn.d + d, hIn.e + e);
+	}
+
+	private static int s1(int a, int b, int c, int d, int w_t) {
+		return rotateLeft(a, 5)
+				// f: 0 <= t <= 19
+				+ ((b & c) | ((~b) & d))
+				+ 0x5A827999 + w_t;
+	}
+
+	private static int s2(int a, int b, int c, int d, int w_t) {
+		return rotateLeft(a, 5)
+				// f: 20 <= t <= 39
+				+ (b ^ c ^ d)
+				+ 0x6ED9EBA1 + w_t;
+	}
+
+	private static int s3(int a, int b, int c, int d, int w_t) {
+		return rotateLeft(a, 5)
+				// f: 40 <= t <= 59
+				+ ((b & c) | (b & d) | (c & d))
+				+ 0x8F1BBCDC + w_t;
+	}
+
+	private static int s4(int a, int b, int c, int d, int w_t) {
+		return rotateLeft(a, 5)
+				// f: 60 <= t <= 79
+				+ (b ^ c ^ d)
+				+ 0xCA62C1D6 + w_t;
+	}
+
+	private static boolean eq(State q, State r) {
+		return q.a == r.a
+				&& q.b == r.b
+				&& q.c == r.c
+				&& q.d == r.d
+				&& q.e == r.e;
+	}
+
+	private void finish() {
+		int bufferLen = (int) (length & 63);
+		if (bufferLen > 55) {
+			// Last block is too small; pad, compress, pad another block.
+			buffer[bufferLen++] = (byte) 0x80;
+			Arrays.fill(buffer, bufferLen, 64, (byte) 0);
+			compress(buffer, 0);
+			Arrays.fill(buffer, 0, 56, (byte) 0);
+		} else {
+			// Last block can hold padding and length.
+			buffer[bufferLen++] = (byte) 0x80;
+			Arrays.fill(buffer, bufferLen, 56, (byte) 0);
+		}
+
+		// SHA-1 appends the length of the message in bits after the
+		// padding block (above). Here length is in bytes. Multiply by
+		// 8 by shifting by 3 as part of storing the 64 bit byte length
+		// into the two words expected in the trailer.
+		NB.encodeInt32(buffer, 56, (int) (length >>> (32 - 3)));
+		NB.encodeInt32(buffer, 60, (int) (length << 3));
+		compress(buffer, 0);
+
+		if (foundCollision) {
+			ObjectId id = h.toObjectId();
+			LOG.warn("possible SHA-1 collision " + id.name()); //$NON-NLS-1$
+			throw new Sha1CollisionException(id);
+		}
+	}
+
+	/**
+	 * Finish the digest and return the resulting hash.
+	 * <p>
+	 * Once {@code digest()} is called, this instance should be discarded.
+	 *
+	 * @return the bytes for the resulting hash.
+	 * @throws Sha1CollisionException
+	 *             if a collision was detected and safeHash is false.
+	 */
+	public byte[] digest() throws Sha1CollisionException {
+		finish();
+
+		byte[] b = new byte[20];
+		NB.encodeInt32(b, 0, h.a);
+		NB.encodeInt32(b, 4, h.b);
+		NB.encodeInt32(b, 8, h.c);
+		NB.encodeInt32(b, 12, h.d);
+		NB.encodeInt32(b, 16, h.e);
+		return b;
+	}
+
+	/**
+	 * Finish the digest and return the resulting hash.
+	 * <p>
+	 * Once {@code digest()} is called, this instance should be discarded.
+	 *
+	 * @return the ObjectId for the resulting hash.
+	 * @throws Sha1CollisionException
+	 *             if a collision was detected and safeHash is false.
+	 */
+	public ObjectId toObjectId() throws Sha1CollisionException {
+		finish();
+		return h.toObjectId();
+	}
+
+	/**
+	 * Finish the digest and return the resulting hash.
+	 * <p>
+	 * Once {@code digest()} is called, this instance should be discarded.
+	 *
+	 * @param id
+	 *            destination to copy the digest to.
+	 * @throws Sha1CollisionException
+	 *             if a collision was detected and safeHash is false.
+	 */
+	public void digest(MutableObjectId id) throws Sha1CollisionException {
+		finish();
+		id.set(h.a, h.b, h.c, h.d, h.e);
+	}
+
+	/**
+	 * Check if a collision was detected.
+	 *
+	 * <p>
+	 * This method only returns an accurate result after the digest was obtained
+	 * through {@link #digest()}, {@link #digest(MutableObjectId)} or
+	 * {@link #toObjectId()}, as the hashing function must finish processing to
+	 * know the final state.
+	 *
+	 * @return {@code true} if a likely collision was detected.
+	 */
+	public boolean hasCollision() {
+		return foundCollision;
+	}
+
+	/**
+	 * Reset this instance to compute another hash.
+	 *
+	 * @return {@code this}.
+	 */
+	public SHA1 reset() {
+		h.init();
+		length = 0;
+		foundCollision = false;
+		return this;
+	}
+
+	private static final class State {
+		int a;
+		int b;
+		int c;
+		int d;
+		int e;
+
+		final void init() {
+			// Magic initialization constants defined by FIPS180.
+			save(0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0);
+		}
+
+		final void save(int a1, int b1, int c1, int d1, int e1) {
+			a = a1;
+			b = b1;
+			c = c1;
+			d = d1;
+			e = e1;
+		}
+
+		ObjectId toObjectId() {
+			return new ObjectId(a, b, c, d, e);
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/Sha1CollisionException.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/Sha1CollisionException.java
new file mode 100644
index 0000000..be126a5
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/Sha1CollisionException.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017, Google Inc.
+ * 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.util.sha1;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Thrown by {@link SHA1} if it detects a likely hash collision.
+ *
+ * @since 4.7
+ */
+public class Sha1CollisionException extends RuntimeException {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Initialize with default message.
+	 *
+	 * @param id
+	 *            object whose contents are a hash collision.
+	 */
+	public Sha1CollisionException(ObjectId id) {
+		super(MessageFormat.format(
+				JGitText.get().sha1CollisionDetected1,
+				id.name()));
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/UbcCheck.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/UbcCheck.java
new file mode 100644
index 0000000..cebdbee
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/sha1/UbcCheck.java
@@ -0,0 +1,1040 @@
+/*
+* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
+* Distributed under the MIT Software License.
+* MIT License
+*
+* Copyright (c) 2017:
+*     Marc Stevens
+*     Cryptology Group
+*     Centrum Wiskunde & Informatica
+*     P.O. Box 94079, 1090 GB Amsterdam, Netherlands
+*     marc@marc-stevens.nl
+*
+*     Dan Shumow
+*     Microsoft Research
+*     danshu@microsoft.com
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all
+* copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*/
+
+package org.eclipse.jgit.util.sha1;
+
+// Converted by hand by Shawn Pearce (Google), using lib/ubc_check.c from
+// https://github.com/cr-marcstevens/sha1collisiondetection/
+//
+// this file was generated by the 'parse_bitrel' program in the tools section
+// using the data files from directory 'tools/data/3565'
+//
+// Array DV contains a list of SHA-1 Disturbance Vectors (DV) to check
+// dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper)
+// dm[80] is the expanded message block XOR-difference defined by the DV
+// testt is the step to do the recompression from for collision detection
+// maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check
+//
+// ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs
+// it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met
+// thus one needs to do the recompression check for each DV that has its bit set
+//
+// ubc_check is programmatically generated and the unavoidable bitconditions have been hardcoded
+// a directly verifiable version named ubc_check_verify can be found in ubc_check_verify.c
+// ubc_check has been verified against ubc_check_verify using the 'ubc_check_test' program in the tools section
+
+final class UbcCheck {
+	private static final int DV_I_43_0_bit = 1 << 0;
+	private static final int DV_I_44_0_bit = 1 << 1;
+	private static final int DV_I_45_0_bit = 1 << 2;
+	private static final int DV_I_46_0_bit = 1 << 3;
+	private static final int DV_I_46_2_bit = 1 << 4;
+	private static final int DV_I_47_0_bit = 1 << 5;
+	private static final int DV_I_47_2_bit = 1 << 6;
+	private static final int DV_I_48_0_bit = 1 << 7;
+	private static final int DV_I_48_2_bit = 1 << 8;
+	private static final int DV_I_49_0_bit = 1 << 9;
+	private static final int DV_I_49_2_bit = 1 << 10;
+	private static final int DV_I_50_0_bit = 1 << 11;
+	private static final int DV_I_50_2_bit = 1 << 12;
+	private static final int DV_I_51_0_bit = 1 << 13;
+	private static final int DV_I_51_2_bit = 1 << 14;
+	private static final int DV_I_52_0_bit = 1 << 15;
+	private static final int DV_II_45_0_bit = 1 << 16;
+	private static final int DV_II_46_0_bit = 1 << 17;
+	private static final int DV_II_46_2_bit = 1 << 18;
+	private static final int DV_II_47_0_bit = 1 << 19;
+	private static final int DV_II_48_0_bit = 1 << 20;
+	private static final int DV_II_49_0_bit = 1 << 21;
+	private static final int DV_II_49_2_bit = 1 << 22;
+	private static final int DV_II_50_0_bit = 1 << 23;
+	private static final int DV_II_50_2_bit = 1 << 24;
+	private static final int DV_II_51_0_bit = 1 << 25;
+	private static final int DV_II_51_2_bit = 1 << 26;
+	private static final int DV_II_52_0_bit = 1 << 27;
+	private static final int DV_II_53_0_bit = 1 << 28;
+	private static final int DV_II_54_0_bit = 1 << 29;
+	private static final int DV_II_55_0_bit = 1 << 30;
+	private static final int DV_II_56_0_bit = 1 << 31;
+
+	static int check(int[] w) {
+		int mask = ~0;
+		mask &= (((((w[44] ^ w[45]) >>> 29) & 1) - 1) | ~(DV_I_48_0_bit
+				| DV_I_51_0_bit | DV_I_52_0_bit | DV_II_45_0_bit
+				| DV_II_46_0_bit | DV_II_50_0_bit | DV_II_51_0_bit));
+		mask &= (((((w[49] ^ w[50]) >>> 29) & 1) - 1)
+				| ~(DV_I_46_0_bit | DV_II_45_0_bit | DV_II_50_0_bit
+						| DV_II_51_0_bit | DV_II_55_0_bit | DV_II_56_0_bit));
+		mask &= (((((w[48] ^ w[49]) >>> 29) & 1) - 1)
+				| ~(DV_I_45_0_bit | DV_I_52_0_bit | DV_II_49_0_bit
+						| DV_II_50_0_bit | DV_II_54_0_bit | DV_II_55_0_bit));
+		mask &= ((((w[47] ^ (w[50] >>> 25)) & (1 << 4)) - (1 << 4))
+				| ~(DV_I_47_0_bit | DV_I_49_0_bit | DV_I_51_0_bit
+						| DV_II_45_0_bit | DV_II_51_0_bit | DV_II_56_0_bit));
+		mask &= (((((w[47] ^ w[48]) >>> 29) & 1) - 1)
+				| ~(DV_I_44_0_bit | DV_I_51_0_bit | DV_II_48_0_bit
+						| DV_II_49_0_bit | DV_II_53_0_bit | DV_II_54_0_bit));
+		mask &= (((((w[46] >>> 4) ^ (w[49] >>> 29)) & 1) - 1)
+				| ~(DV_I_46_0_bit | DV_I_48_0_bit | DV_I_50_0_bit
+						| DV_I_52_0_bit | DV_II_50_0_bit | DV_II_55_0_bit));
+		mask &= (((((w[46] ^ w[47]) >>> 29) & 1) - 1)
+				| ~(DV_I_43_0_bit | DV_I_50_0_bit | DV_II_47_0_bit
+						| DV_II_48_0_bit | DV_II_52_0_bit | DV_II_53_0_bit));
+		mask &= (((((w[45] >>> 4) ^ (w[48] >>> 29)) & 1) - 1)
+				| ~(DV_I_45_0_bit | DV_I_47_0_bit | DV_I_49_0_bit
+						| DV_I_51_0_bit | DV_II_49_0_bit | DV_II_54_0_bit));
+		mask &= (((((w[45] ^ w[46]) >>> 29) & 1) - 1)
+				| ~(DV_I_49_0_bit | DV_I_52_0_bit | DV_II_46_0_bit
+						| DV_II_47_0_bit | DV_II_51_0_bit | DV_II_52_0_bit));
+		mask &= (((((w[44] >>> 4) ^ (w[47] >>> 29)) & 1) - 1)
+				| ~(DV_I_44_0_bit | DV_I_46_0_bit | DV_I_48_0_bit
+						| DV_I_50_0_bit | DV_II_48_0_bit | DV_II_53_0_bit));
+		mask &= (((((w[43] >>> 4) ^ (w[46] >>> 29)) & 1) - 1)
+				| ~(DV_I_43_0_bit | DV_I_45_0_bit | DV_I_47_0_bit
+						| DV_I_49_0_bit | DV_II_47_0_bit | DV_II_52_0_bit));
+		mask &= (((((w[43] ^ w[44]) >>> 29) & 1) - 1)
+				| ~(DV_I_47_0_bit | DV_I_50_0_bit | DV_I_51_0_bit
+						| DV_II_45_0_bit | DV_II_49_0_bit | DV_II_50_0_bit));
+		mask &= (((((w[42] >>> 4) ^ (w[45] >>> 29)) & 1) - 1)
+				| ~(DV_I_44_0_bit | DV_I_46_0_bit | DV_I_48_0_bit
+						| DV_I_52_0_bit | DV_II_46_0_bit | DV_II_51_0_bit));
+		mask &= (((((w[41] >>> 4) ^ (w[44] >>> 29)) & 1) - 1)
+				| ~(DV_I_43_0_bit | DV_I_45_0_bit | DV_I_47_0_bit
+						| DV_I_51_0_bit | DV_II_45_0_bit | DV_II_50_0_bit));
+		mask &= (((((w[40] ^ w[41]) >>> 29) & 1) - 1)
+				| ~(DV_I_44_0_bit | DV_I_47_0_bit | DV_I_48_0_bit
+						| DV_II_46_0_bit | DV_II_47_0_bit | DV_II_56_0_bit));
+		mask &= (((((w[54] ^ w[55]) >>> 29) & 1) - 1)
+				| ~(DV_I_51_0_bit | DV_II_47_0_bit | DV_II_50_0_bit
+						| DV_II_55_0_bit | DV_II_56_0_bit));
+		mask &= (((((w[53] ^ w[54]) >>> 29) & 1) - 1)
+				| ~(DV_I_50_0_bit | DV_II_46_0_bit | DV_II_49_0_bit
+						| DV_II_54_0_bit | DV_II_55_0_bit));
+		mask &= (((((w[52] ^ w[53]) >>> 29) & 1) - 1)
+				| ~(DV_I_49_0_bit | DV_II_45_0_bit | DV_II_48_0_bit
+						| DV_II_53_0_bit | DV_II_54_0_bit));
+		mask &= ((((w[50] ^ (w[53] >>> 25)) & (1 << 4)) - (1 << 4))
+				| ~(DV_I_50_0_bit | DV_I_52_0_bit | DV_II_46_0_bit
+						| DV_II_48_0_bit | DV_II_54_0_bit));
+		mask &= (((((w[50] ^ w[51]) >>> 29) & 1) - 1)
+				| ~(DV_I_47_0_bit | DV_II_46_0_bit | DV_II_51_0_bit
+						| DV_II_52_0_bit | DV_II_56_0_bit));
+		mask &= ((((w[49] ^ (w[52] >>> 25)) & (1 << 4)) - (1 << 4))
+				| ~(DV_I_49_0_bit | DV_I_51_0_bit | DV_II_45_0_bit
+						| DV_II_47_0_bit | DV_II_53_0_bit));
+		mask &= ((((w[48] ^ (w[51] >>> 25)) & (1 << 4)) - (1 << 4))
+				| ~(DV_I_48_0_bit | DV_I_50_0_bit | DV_I_52_0_bit
+						| DV_II_46_0_bit | DV_II_52_0_bit));
+		mask &= (((((w[42] ^ w[43]) >>> 29) & 1) - 1)
+				| ~(DV_I_46_0_bit | DV_I_49_0_bit | DV_I_50_0_bit
+						| DV_II_48_0_bit | DV_II_49_0_bit));
+		mask &= (((((w[41] ^ w[42]) >>> 29) & 1) - 1)
+				| ~(DV_I_45_0_bit | DV_I_48_0_bit | DV_I_49_0_bit
+						| DV_II_47_0_bit | DV_II_48_0_bit));
+		mask &= (((((w[40] >>> 4) ^ (w[43] >>> 29)) & 1) - 1)
+				| ~(DV_I_44_0_bit | DV_I_46_0_bit | DV_I_50_0_bit
+						| DV_II_49_0_bit | DV_II_56_0_bit));
+		mask &= (((((w[39] >>> 4) ^ (w[42] >>> 29)) & 1) - 1)
+				| ~(DV_I_43_0_bit | DV_I_45_0_bit | DV_I_49_0_bit
+						| DV_II_48_0_bit | DV_II_55_0_bit));
+		if ((mask & (DV_I_44_0_bit | DV_I_48_0_bit | DV_II_47_0_bit
+				| DV_II_54_0_bit | DV_II_56_0_bit)) != 0)
+			mask &= (((((w[38] >>> 4) ^ (w[41] >>> 29)) & 1) - 1)
+					| ~(DV_I_44_0_bit | DV_I_48_0_bit | DV_II_47_0_bit
+							| DV_II_54_0_bit | DV_II_56_0_bit));
+		mask &= (((((w[37] >>> 4) ^ (w[40] >>> 29)) & 1) - 1)
+				| ~(DV_I_43_0_bit | DV_I_47_0_bit | DV_II_46_0_bit
+						| DV_II_53_0_bit | DV_II_55_0_bit));
+		if ((mask & (DV_I_52_0_bit | DV_II_48_0_bit | DV_II_51_0_bit
+				| DV_II_56_0_bit)) != 0)
+			mask &= (((((w[55] ^ w[56]) >>> 29) & 1) - 1) | ~(DV_I_52_0_bit
+					| DV_II_48_0_bit | DV_II_51_0_bit | DV_II_56_0_bit));
+		if ((mask & (DV_I_52_0_bit | DV_II_48_0_bit | DV_II_50_0_bit
+				| DV_II_56_0_bit)) != 0)
+			mask &= ((((w[52] ^ (w[55] >>> 25)) & (1 << 4)) - (1 << 4))
+					| ~(DV_I_52_0_bit | DV_II_48_0_bit | DV_II_50_0_bit
+							| DV_II_56_0_bit));
+		if ((mask & (DV_I_51_0_bit | DV_II_47_0_bit | DV_II_49_0_bit
+				| DV_II_55_0_bit)) != 0)
+			mask &= ((((w[51] ^ (w[54] >>> 25)) & (1 << 4)) - (1 << 4))
+					| ~(DV_I_51_0_bit | DV_II_47_0_bit | DV_II_49_0_bit
+							| DV_II_55_0_bit));
+		if ((mask & (DV_I_48_0_bit | DV_II_47_0_bit | DV_II_52_0_bit
+				| DV_II_53_0_bit)) != 0)
+			mask &= (((((w[51] ^ w[52]) >>> 29) & 1) - 1) | ~(DV_I_48_0_bit
+					| DV_II_47_0_bit | DV_II_52_0_bit | DV_II_53_0_bit));
+		if ((mask & (DV_I_46_0_bit | DV_I_49_0_bit | DV_II_45_0_bit
+				| DV_II_48_0_bit)) != 0)
+			mask &= (((((w[36] >>> 4) ^ (w[40] >>> 29)) & 1) - 1)
+					| ~(DV_I_46_0_bit | DV_I_49_0_bit | DV_II_45_0_bit
+							| DV_II_48_0_bit));
+		if ((mask & (DV_I_52_0_bit | DV_II_48_0_bit | DV_II_49_0_bit)) != 0)
+			mask &= ((0 - (((w[53] ^ w[56]) >>> 29) & 1))
+					| ~(DV_I_52_0_bit | DV_II_48_0_bit | DV_II_49_0_bit));
+		if ((mask & (DV_I_50_0_bit | DV_II_46_0_bit | DV_II_47_0_bit)) != 0)
+			mask &= ((0 - (((w[51] ^ w[54]) >>> 29) & 1))
+					| ~(DV_I_50_0_bit | DV_II_46_0_bit | DV_II_47_0_bit));
+		if ((mask & (DV_I_49_0_bit | DV_I_51_0_bit | DV_II_45_0_bit)) != 0)
+			mask &= ((0 - (((w[50] ^ w[52]) >>> 29) & 1))
+					| ~(DV_I_49_0_bit | DV_I_51_0_bit | DV_II_45_0_bit));
+		if ((mask & (DV_I_48_0_bit | DV_I_50_0_bit | DV_I_52_0_bit)) != 0)
+			mask &= ((0 - (((w[49] ^ w[51]) >>> 29) & 1))
+					| ~(DV_I_48_0_bit | DV_I_50_0_bit | DV_I_52_0_bit));
+		if ((mask & (DV_I_47_0_bit | DV_I_49_0_bit | DV_I_51_0_bit)) != 0)
+			mask &= ((0 - (((w[48] ^ w[50]) >>> 29) & 1))
+					| ~(DV_I_47_0_bit | DV_I_49_0_bit | DV_I_51_0_bit));
+		if ((mask & (DV_I_46_0_bit | DV_I_48_0_bit | DV_I_50_0_bit)) != 0)
+			mask &= ((0 - (((w[47] ^ w[49]) >>> 29) & 1))
+					| ~(DV_I_46_0_bit | DV_I_48_0_bit | DV_I_50_0_bit));
+		if ((mask & (DV_I_45_0_bit | DV_I_47_0_bit | DV_I_49_0_bit)) != 0)
+			mask &= ((0 - (((w[46] ^ w[48]) >>> 29) & 1))
+					| ~(DV_I_45_0_bit | DV_I_47_0_bit | DV_I_49_0_bit));
+		mask &= ((((w[45] ^ w[47]) & (1 << 6)) - (1 << 6))
+				| ~(DV_I_47_2_bit | DV_I_49_2_bit | DV_I_51_2_bit));
+		if ((mask & (DV_I_44_0_bit | DV_I_46_0_bit | DV_I_48_0_bit)) != 0)
+			mask &= ((0 - (((w[45] ^ w[47]) >>> 29) & 1))
+					| ~(DV_I_44_0_bit | DV_I_46_0_bit | DV_I_48_0_bit));
+		mask &= (((((w[44] ^ w[46]) >>> 6) & 1) - 1)
+				| ~(DV_I_46_2_bit | DV_I_48_2_bit | DV_I_50_2_bit));
+		if ((mask & (DV_I_43_0_bit | DV_I_45_0_bit | DV_I_47_0_bit)) != 0)
+			mask &= ((0 - (((w[44] ^ w[46]) >>> 29) & 1))
+					| ~(DV_I_43_0_bit | DV_I_45_0_bit | DV_I_47_0_bit));
+		mask &= ((0 - ((w[41] ^ (w[42] >>> 5)) & (1 << 1)))
+				| ~(DV_I_48_2_bit | DV_II_46_2_bit | DV_II_51_2_bit));
+		mask &= ((0 - ((w[40] ^ (w[41] >>> 5)) & (1 << 1)))
+				| ~(DV_I_47_2_bit | DV_I_51_2_bit | DV_II_50_2_bit));
+		if ((mask & (DV_I_44_0_bit | DV_I_46_0_bit | DV_II_56_0_bit)) != 0)
+			mask &= ((0 - (((w[40] ^ w[42]) >>> 4) & 1))
+					| ~(DV_I_44_0_bit | DV_I_46_0_bit | DV_II_56_0_bit));
+		mask &= ((0 - ((w[39] ^ (w[40] >>> 5)) & (1 << 1)))
+				| ~(DV_I_46_2_bit | DV_I_50_2_bit | DV_II_49_2_bit));
+		if ((mask & (DV_I_43_0_bit | DV_I_45_0_bit | DV_II_55_0_bit)) != 0)
+			mask &= ((0 - (((w[39] ^ w[41]) >>> 4) & 1))
+					| ~(DV_I_43_0_bit | DV_I_45_0_bit | DV_II_55_0_bit));
+		if ((mask & (DV_I_44_0_bit | DV_II_54_0_bit | DV_II_56_0_bit)) != 0)
+			mask &= ((0 - (((w[38] ^ w[40]) >>> 4) & 1))
+					| ~(DV_I_44_0_bit | DV_II_54_0_bit | DV_II_56_0_bit));
+		if ((mask & (DV_I_43_0_bit | DV_II_53_0_bit | DV_II_55_0_bit)) != 0)
+			mask &= ((0 - (((w[37] ^ w[39]) >>> 4) & 1))
+					| ~(DV_I_43_0_bit | DV_II_53_0_bit | DV_II_55_0_bit));
+		mask &= ((0 - ((w[36] ^ (w[37] >>> 5)) & (1 << 1)))
+				| ~(DV_I_47_2_bit | DV_I_50_2_bit | DV_II_46_2_bit));
+		if ((mask & (DV_I_45_0_bit | DV_I_48_0_bit | DV_II_47_0_bit)) != 0)
+			mask &= (((((w[35] >>> 4) ^ (w[39] >>> 29)) & 1) - 1)
+					| ~(DV_I_45_0_bit | DV_I_48_0_bit | DV_II_47_0_bit));
+		if ((mask & (DV_I_48_0_bit | DV_II_48_0_bit)) != 0)
+			mask &= ((0 - ((w[63] ^ (w[64] >>> 5)) & (1 << 0)))
+					| ~(DV_I_48_0_bit | DV_II_48_0_bit));
+		if ((mask & (DV_I_45_0_bit | DV_II_45_0_bit)) != 0)
+			mask &= ((0 - ((w[63] ^ (w[64] >>> 5)) & (1 << 1)))
+					| ~(DV_I_45_0_bit | DV_II_45_0_bit));
+		if ((mask & (DV_I_47_0_bit | DV_II_47_0_bit)) != 0)
+			mask &= ((0 - ((w[62] ^ (w[63] >>> 5)) & (1 << 0)))
+					| ~(DV_I_47_0_bit | DV_II_47_0_bit));
+		if ((mask & (DV_I_46_0_bit | DV_II_46_0_bit)) != 0)
+			mask &= ((0 - ((w[61] ^ (w[62] >>> 5)) & (1 << 0)))
+					| ~(DV_I_46_0_bit | DV_II_46_0_bit));
+		mask &= ((0 - ((w[61] ^ (w[62] >>> 5)) & (1 << 2)))
+				| ~(DV_I_46_2_bit | DV_II_46_2_bit));
+		if ((mask & (DV_I_45_0_bit | DV_II_45_0_bit)) != 0)
+			mask &= ((0 - ((w[60] ^ (w[61] >>> 5)) & (1 << 0)))
+					| ~(DV_I_45_0_bit | DV_II_45_0_bit));
+		if ((mask & (DV_II_51_0_bit | DV_II_54_0_bit)) != 0)
+			mask &= (((((w[58] ^ w[59]) >>> 29) & 1) - 1)
+					| ~(DV_II_51_0_bit | DV_II_54_0_bit));
+		if ((mask & (DV_II_50_0_bit | DV_II_53_0_bit)) != 0)
+			mask &= (((((w[57] ^ w[58]) >>> 29) & 1) - 1)
+					| ~(DV_II_50_0_bit | DV_II_53_0_bit));
+		if ((mask & (DV_II_52_0_bit | DV_II_54_0_bit)) != 0)
+			mask &= ((((w[56] ^ (w[59] >>> 25)) & (1 << 4)) - (1 << 4))
+					| ~(DV_II_52_0_bit | DV_II_54_0_bit));
+		if ((mask & (DV_II_51_0_bit | DV_II_52_0_bit)) != 0)
+			mask &= ((0 - (((w[56] ^ w[59]) >>> 29) & 1))
+					| ~(DV_II_51_0_bit | DV_II_52_0_bit));
+		if ((mask & (DV_II_49_0_bit | DV_II_52_0_bit)) != 0)
+			mask &= (((((w[56] ^ w[57]) >>> 29) & 1) - 1)
+					| ~(DV_II_49_0_bit | DV_II_52_0_bit));
+		if ((mask & (DV_II_51_0_bit | DV_II_53_0_bit)) != 0)
+			mask &= ((((w[55] ^ (w[58] >>> 25)) & (1 << 4)) - (1 << 4))
+					| ~(DV_II_51_0_bit | DV_II_53_0_bit));
+		if ((mask & (DV_II_50_0_bit | DV_II_52_0_bit)) != 0)
+			mask &= ((((w[54] ^ (w[57] >>> 25)) & (1 << 4)) - (1 << 4))
+					| ~(DV_II_50_0_bit | DV_II_52_0_bit));
+		if ((mask & (DV_II_49_0_bit | DV_II_51_0_bit)) != 0)
+			mask &= ((((w[53] ^ (w[56] >>> 25)) & (1 << 4)) - (1 << 4))
+					| ~(DV_II_49_0_bit | DV_II_51_0_bit));
+		mask &= ((((w[51] ^ (w[50] >>> 5)) & (1 << 1)) - (1 << 1))
+				| ~(DV_I_50_2_bit | DV_II_46_2_bit));
+		mask &= ((((w[48] ^ w[50]) & (1 << 6)) - (1 << 6))
+				| ~(DV_I_50_2_bit | DV_II_46_2_bit));
+		if ((mask & (DV_I_51_0_bit | DV_I_52_0_bit)) != 0)
+			mask &= ((0 - (((w[48] ^ w[55]) >>> 29) & 1))
+					| ~(DV_I_51_0_bit | DV_I_52_0_bit));
+		mask &= ((((w[47] ^ w[49]) & (1 << 6)) - (1 << 6))
+				| ~(DV_I_49_2_bit | DV_I_51_2_bit));
+		mask &= ((((w[48] ^ (w[47] >>> 5)) & (1 << 1)) - (1 << 1))
+				| ~(DV_I_47_2_bit | DV_II_51_2_bit));
+		mask &= ((((w[46] ^ w[48]) & (1 << 6)) - (1 << 6))
+				| ~(DV_I_48_2_bit | DV_I_50_2_bit));
+		mask &= ((((w[47] ^ (w[46] >>> 5)) & (1 << 1)) - (1 << 1))
+				| ~(DV_I_46_2_bit | DV_II_50_2_bit));
+		mask &= ((0 - ((w[44] ^ (w[45] >>> 5)) & (1 << 1)))
+				| ~(DV_I_51_2_bit | DV_II_49_2_bit));
+		mask &= ((((w[43] ^ w[45]) & (1 << 6)) - (1 << 6))
+				| ~(DV_I_47_2_bit | DV_I_49_2_bit));
+		mask &= (((((w[42] ^ w[44]) >>> 6) & 1) - 1)
+				| ~(DV_I_46_2_bit | DV_I_48_2_bit));
+		mask &= ((((w[43] ^ (w[42] >>> 5)) & (1 << 1)) - (1 << 1))
+				| ~(DV_II_46_2_bit | DV_II_51_2_bit));
+		mask &= ((((w[42] ^ (w[41] >>> 5)) & (1 << 1)) - (1 << 1))
+				| ~(DV_I_51_2_bit | DV_II_50_2_bit));
+		mask &= ((((w[41] ^ (w[40] >>> 5)) & (1 << 1)) - (1 << 1))
+				| ~(DV_I_50_2_bit | DV_II_49_2_bit));
+		if ((mask & (DV_I_52_0_bit | DV_II_51_0_bit)) != 0)
+			mask &= ((((w[39] ^ (w[43] >>> 25)) & (1 << 4)) - (1 << 4))
+					| ~(DV_I_52_0_bit | DV_II_51_0_bit));
+		if ((mask & (DV_I_51_0_bit | DV_II_50_0_bit)) != 0)
+			mask &= ((((w[38] ^ (w[42] >>> 25)) & (1 << 4)) - (1 << 4))
+					| ~(DV_I_51_0_bit | DV_II_50_0_bit));
+		if ((mask & (DV_I_48_2_bit | DV_I_51_2_bit)) != 0)
+			mask &= ((0 - ((w[37] ^ (w[38] >>> 5)) & (1 << 1)))
+					| ~(DV_I_48_2_bit | DV_I_51_2_bit));
+		if ((mask & (DV_I_50_0_bit | DV_II_49_0_bit)) != 0)
+			mask &= ((((w[37] ^ (w[41] >>> 25)) & (1 << 4)) - (1 << 4))
+					| ~(DV_I_50_0_bit | DV_II_49_0_bit));
+		if ((mask & (DV_II_52_0_bit | DV_II_54_0_bit)) != 0)
+			mask &= ((0 - ((w[36] ^ w[38]) & (1 << 4)))
+					| ~(DV_II_52_0_bit | DV_II_54_0_bit));
+		mask &= ((0 - ((w[35] ^ (w[36] >>> 5)) & (1 << 1)))
+				| ~(DV_I_46_2_bit | DV_I_49_2_bit));
+		if ((mask & (DV_I_51_0_bit | DV_II_47_0_bit)) != 0)
+			mask &= ((((w[35] ^ (w[39] >>> 25)) & (1 << 3)) - (1 << 3))
+					| ~(DV_I_51_0_bit | DV_II_47_0_bit));
+
+		if (mask == 0) {
+			return mask;
+		}
+
+		if ((mask & DV_I_43_0_bit) != 0)
+			if (0 == ((w[61] ^ (w[62] >>> 5)) & (1 << 1))
+					|| 0 != ((w[59] ^ (w[63] >>> 25)) & (1 << 5))
+					|| 0 == ((w[58] ^ (w[63] >>> 30)) & (1 << 0)))
+				mask &= ~DV_I_43_0_bit;
+		if ((mask & DV_I_44_0_bit) != 0)
+			if (0 == ((w[62] ^ (w[63] >>> 5)) & (1 << 1))
+					|| 0 != ((w[60] ^ (w[64] >>> 25)) & (1 << 5))
+					|| 0 == ((w[59] ^ (w[64] >>> 30)) & (1 << 0)))
+				mask &= ~DV_I_44_0_bit;
+		if ((mask & DV_I_46_2_bit) != 0)
+			mask &= ((~((w[40] ^ w[42]) >>> 2)) | ~DV_I_46_2_bit);
+		if ((mask & DV_I_47_2_bit) != 0)
+			if (0 == ((w[62] ^ (w[63] >>> 5)) & (1 << 2))
+					|| 0 != ((w[41] ^ w[43]) & (1 << 6)))
+				mask &= ~DV_I_47_2_bit;
+		if ((mask & DV_I_48_2_bit) != 0)
+			if (0 == ((w[63] ^ (w[64] >>> 5)) & (1 << 2))
+					|| 0 != ((w[48] ^ (w[49] << 5)) & (1 << 6)))
+				mask &= ~DV_I_48_2_bit;
+		if ((mask & DV_I_49_2_bit) != 0)
+			if (0 != ((w[49] ^ (w[50] << 5)) & (1 << 6))
+					|| 0 == ((w[42] ^ w[50]) & (1 << 1))
+					|| 0 != ((w[39] ^ (w[40] << 5)) & (1 << 6))
+					|| 0 == ((w[38] ^ w[40]) & (1 << 1)))
+				mask &= ~DV_I_49_2_bit;
+		if ((mask & DV_I_50_0_bit) != 0)
+			mask &= ((((w[36] ^ w[37]) << 7)) | ~DV_I_50_0_bit);
+		if ((mask & DV_I_50_2_bit) != 0)
+			mask &= ((((w[43] ^ w[51]) << 11)) | ~DV_I_50_2_bit);
+		if ((mask & DV_I_51_0_bit) != 0)
+			mask &= ((((w[37] ^ w[38]) << 9)) | ~DV_I_51_0_bit);
+		if ((mask & DV_I_51_2_bit) != 0)
+			if (0 != ((w[51] ^ (w[52] << 5)) & (1 << 6))
+					|| 0 != ((w[49] ^ w[51]) & (1 << 6))
+					|| 0 != ((w[37] ^ (w[37] >>> 5)) & (1 << 1))
+					|| 0 != ((w[35] ^ (w[39] >>> 25)) & (1 << 5)))
+				mask &= ~DV_I_51_2_bit;
+		if ((mask & DV_I_52_0_bit) != 0)
+			mask &= ((((w[38] ^ w[39]) << 11)) | ~DV_I_52_0_bit);
+		if ((mask & DV_II_46_2_bit) != 0)
+			mask &= ((((w[47] ^ w[51]) << 17)) | ~DV_II_46_2_bit);
+		if ((mask & DV_II_48_0_bit) != 0)
+			if (0 != ((w[36] ^ (w[40] >>> 25)) & (1 << 3))
+					|| 0 == ((w[35] ^ (w[40] << 2)) & (1 << 30)))
+				mask &= ~DV_II_48_0_bit;
+		if ((mask & DV_II_49_0_bit) != 0)
+			if (0 != ((w[37] ^ (w[41] >>> 25)) & (1 << 3))
+					|| 0 == ((w[36] ^ (w[41] << 2)) & (1 << 30)))
+				mask &= ~DV_II_49_0_bit;
+		if ((mask & DV_II_49_2_bit) != 0)
+			if (0 != ((w[53] ^ (w[54] << 5)) & (1 << 6))
+					|| 0 != ((w[51] ^ w[53]) & (1 << 6))
+					|| 0 == ((w[50] ^ w[54]) & (1 << 1))
+					|| 0 != ((w[45] ^ (w[46] << 5)) & (1 << 6))
+					|| 0 != ((w[37] ^ (w[41] >>> 25)) & (1 << 5))
+					|| 0 == ((w[36] ^ (w[41] >>> 30)) & (1 << 0)))
+				mask &= ~DV_II_49_2_bit;
+		if ((mask & DV_II_50_0_bit) != 0)
+			if (0 == ((w[55] ^ w[58]) & (1 << 29))
+					|| 0 != ((w[38] ^ (w[42] >>> 25)) & (1 << 3))
+					|| 0 == ((w[37] ^ (w[42] << 2)) & (1 << 30)))
+				mask &= ~DV_II_50_0_bit;
+		if ((mask & DV_II_50_2_bit) != 0)
+			if (0 != ((w[54] ^ (w[55] << 5)) & (1 << 6))
+					|| 0!=((w[52] ^ w[54]) & (1 << 6))
+					|| 0==((w[51] ^ w[55]) & (1 << 1))
+					|| 0==((w[45] ^ w[47]) & (1 << 1))
+					|| 0!=((w[38] ^ (w[42] >>> 25)) & (1 << 5))
+					|| 0==((w[37] ^ (w[42] >>> 30)) & (1 << 0)))
+				mask &= ~DV_II_50_2_bit;
+		if ((mask & DV_II_51_0_bit) != 0)
+			if (0 != ((w[39] ^ (w[43] >>> 25)) & (1 << 3))
+					|| 0 == ((w[38] ^ (w[43] << 2)) & (1 << 30)))
+				mask &= ~DV_II_51_0_bit;
+		if ((mask & DV_II_51_2_bit) != 0)
+			if (0 != ((w[55] ^ (w[56] << 5)) & (1 << 6))
+					|| 0 != ((w[53] ^ w[55]) & (1 << 6))
+					|| 0 == ((w[52] ^ w[56]) & (1 << 1))
+					|| 0 == ((w[46] ^ w[48]) & (1 << 1))
+					|| 0 != ((w[39] ^ (w[43] >>> 25)) & (1 << 5))
+					|| 0 == ((w[38] ^ (w[43] >>> 30)) & (1 << 0)))
+				mask &= ~DV_II_51_2_bit;
+		if ((mask & DV_II_52_0_bit) != 0)
+			if (0 != ((w[59] ^ w[60]) & (1 << 29))
+					|| 0 != ((w[40] ^ (w[44] >>> 25)) & (1 << 3))
+					|| 0 != ((w[40] ^ (w[44] >>> 25)) & (1 << 4))
+					|| 0==((w[39] ^ (w[44] << 2)) & (1 << 30)))
+				mask &= ~DV_II_52_0_bit;
+		if ((mask & DV_II_53_0_bit) != 0)
+			if (0==((w[58] ^ w[61]) & (1 << 29))
+					|| 0!=((w[57] ^ (w[61] >>> 25)) & (1 << 4))
+					|| 0!=((w[41] ^ (w[45] >>> 25)) & (1 << 3))
+					|| 0!=((w[41] ^ (w[45] >>> 25)) & (1 << 4)))
+				mask &= ~DV_II_53_0_bit;
+		if ((mask & DV_II_54_0_bit) != 0)
+			if (0 != ((w[58] ^ (w[62] >>> 25)) & (1 << 4))
+					|| 0 != ((w[42] ^ (w[46] >>> 25)) & (1 << 3))
+					|| 0 != ((w[42] ^ (w[46] >>> 25)) & (1 << 4)))
+				mask &= ~DV_II_54_0_bit;
+		if ((mask & DV_II_55_0_bit) != 0)
+			if (0 != ((w[59] ^ (w[63] >>> 25)) & (1 << 4))
+					|| 0 != ((w[57] ^ (w[59] >>> 25)) & (1 << 4))
+					|| 0 != ((w[43] ^ (w[47] >>> 25)) & (1 << 3))
+					|| 0 != ((w[43] ^ (w[47] >>> 25)) & (1 << 4)))
+				mask &= ~DV_II_55_0_bit;
+		if ((mask & DV_II_56_0_bit) != 0)
+			if (0 != ((w[60] ^ (w[64] >>> 25)) & (1 << 4))
+					|| 0 != ((w[44] ^ (w[48] >>> 25)) & (1 << 3))
+					|| 0 != ((w[44] ^ (w[48] >>> 25)) & (1 << 4)))
+				mask &= ~DV_II_56_0_bit;
+		return mask;
+	}
+
+	private UbcCheck() {
+	}
+
+	static final class DvInfo {
+		final int testt;
+		final int maskb;
+		final int[] dm;
+
+		@SuppressWarnings("unused")
+		DvInfo(int dvType, int dvK, int dvB, int testt, int maskb, int[] dm) {
+			this.testt = testt;
+			this.maskb = maskb;
+			this.dm = dm;
+
+			// Only states 58 and 65 are saved.
+			if (testt != 58 && testt != 65) {
+				throw new IllegalArgumentException();
+			}
+		}
+	}
+
+	static final DvInfo[] DV = new DvInfo[] {
+			new DvInfo(1, 43, 0, 58, 0, new int[] { 0x08000000, 0x9800000c,
+					0xd8000010, 0x08000010, 0xb8000010, 0x98000000, 0x60000000,
+					0x00000008, 0xc0000000, 0x90000014, 0x10000010, 0xb8000014,
+					0x28000000, 0x20000010, 0x48000000, 0x08000018, 0x60000000,
+					0x90000010, 0xf0000010, 0x90000008, 0xc0000000, 0x90000010,
+					0xf0000010, 0xb0000008, 0x40000000, 0x90000000, 0xf0000010,
+					0x90000018, 0x60000000, 0x90000010, 0x90000010, 0x90000000,
+					0x80000000, 0x00000010, 0xa0000000, 0x20000000, 0xa0000000,
+					0x20000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+					0x20000000, 0x00000010, 0xa0000000, 0x00000000, 0x20000000,
+					0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002,
+					0x40000040, 0x40000002, 0x80000004, 0x80000080, 0x80000006,
+					0x00000049, 0x00000103, 0x80000009, 0x80000012, 0x80000202,
+					0x00000018, 0x00000164, 0x00000408, 0x800000e6, 0x8000004c,
+					0x00000803, 0x80000161, 0x80000599 }),
+			new DvInfo(1, 44, 0, 58, 1, new int[] { 0xb4000008, 0x08000000,
+					0x9800000c, 0xd8000010, 0x08000010, 0xb8000010, 0x98000000,
+					0x60000000, 0x00000008, 0xc0000000, 0x90000014, 0x10000010,
+					0xb8000014, 0x28000000, 0x20000010, 0x48000000, 0x08000018,
+					0x60000000, 0x90000010, 0xf0000010, 0x90000008, 0xc0000000,
+					0x90000010, 0xf0000010, 0xb0000008, 0x40000000, 0x90000000,
+					0xf0000010, 0x90000018, 0x60000000, 0x90000010, 0x90000010,
+					0x90000000, 0x80000000, 0x00000010, 0xa0000000, 0x20000000,
+					0xa0000000, 0x20000010, 0x00000000, 0x20000010, 0x20000000,
+					0x00000010, 0x20000000, 0x00000010, 0xa0000000, 0x00000000,
+					0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001,
+					0x40000002, 0x40000040, 0x40000002, 0x80000004, 0x80000080,
+					0x80000006, 0x00000049, 0x00000103, 0x80000009, 0x80000012,
+					0x80000202, 0x00000018, 0x00000164, 0x00000408, 0x800000e6,
+					0x8000004c, 0x00000803, 0x80000161 }),
+			new DvInfo(1, 45, 0, 58, 2, new int[] { 0xf4000014, 0xb4000008,
+					0x08000000, 0x9800000c, 0xd8000010, 0x08000010, 0xb8000010,
+					0x98000000, 0x60000000, 0x00000008, 0xc0000000, 0x90000014,
+					0x10000010, 0xb8000014, 0x28000000, 0x20000010, 0x48000000,
+					0x08000018, 0x60000000, 0x90000010, 0xf0000010, 0x90000008,
+					0xc0000000, 0x90000010, 0xf0000010, 0xb0000008, 0x40000000,
+					0x90000000, 0xf0000010, 0x90000018, 0x60000000, 0x90000010,
+					0x90000010, 0x90000000, 0x80000000, 0x00000010, 0xa0000000,
+					0x20000000, 0xa0000000, 0x20000010, 0x00000000, 0x20000010,
+					0x20000000, 0x00000010, 0x20000000, 0x00000010, 0xa0000000,
+					0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020,
+					0x00000001, 0x40000002, 0x40000040, 0x40000002, 0x80000004,
+					0x80000080, 0x80000006, 0x00000049, 0x00000103, 0x80000009,
+					0x80000012, 0x80000202, 0x00000018, 0x00000164, 0x00000408,
+					0x800000e6, 0x8000004c, 0x00000803 }),
+			new DvInfo(1, 46, 0, 58, 3, new int[] { 0x2c000010, 0xf4000014,
+					0xb4000008, 0x08000000, 0x9800000c, 0xd8000010, 0x08000010,
+					0xb8000010, 0x98000000, 0x60000000, 0x00000008, 0xc0000000,
+					0x90000014, 0x10000010, 0xb8000014, 0x28000000, 0x20000010,
+					0x48000000, 0x08000018, 0x60000000, 0x90000010, 0xf0000010,
+					0x90000008, 0xc0000000, 0x90000010, 0xf0000010, 0xb0000008,
+					0x40000000, 0x90000000, 0xf0000010, 0x90000018, 0x60000000,
+					0x90000010, 0x90000010, 0x90000000, 0x80000000, 0x00000010,
+					0xa0000000, 0x20000000, 0xa0000000, 0x20000010, 0x00000000,
+					0x20000010, 0x20000000, 0x00000010, 0x20000000, 0x00000010,
+					0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+					0x00000020, 0x00000001, 0x40000002, 0x40000040, 0x40000002,
+					0x80000004, 0x80000080, 0x80000006, 0x00000049, 0x00000103,
+					0x80000009, 0x80000012, 0x80000202, 0x00000018, 0x00000164,
+					0x00000408, 0x800000e6, 0x8000004c }),
+			new DvInfo(1, 46, 2, 58, 4, new int[] { 0xb0000040, 0xd0000053,
+					0xd0000022, 0x20000000, 0x60000032, 0x60000043, 0x20000040,
+					0xe0000042, 0x60000002, 0x80000001, 0x00000020, 0x00000003,
+					0x40000052, 0x40000040, 0xe0000052, 0xa0000000, 0x80000040,
+					0x20000001, 0x20000060, 0x80000001, 0x40000042, 0xc0000043,
+					0x40000022, 0x00000003, 0x40000042, 0xc0000043, 0xc0000022,
+					0x00000001, 0x40000002, 0xc0000043, 0x40000062, 0x80000001,
+					0x40000042, 0x40000042, 0x40000002, 0x00000002, 0x00000040,
+					0x80000002, 0x80000000, 0x80000002, 0x80000040, 0x00000000,
+					0x80000040, 0x80000000, 0x00000040, 0x80000000, 0x00000040,
+					0x80000002, 0x00000000, 0x80000000, 0x80000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004,
+					0x00000080, 0x00000004, 0x00000009, 0x00000101, 0x00000009,
+					0x00000012, 0x00000202, 0x0000001a, 0x00000124, 0x0000040c,
+					0x00000026, 0x0000004a, 0x0000080a, 0x00000060, 0x00000590,
+					0x00001020, 0x0000039a, 0x00000132 }),
+			new DvInfo(1, 47, 0, 58, 5, new int[] { 0xc8000010, 0x2c000010,
+					0xf4000014, 0xb4000008, 0x08000000, 0x9800000c, 0xd8000010,
+					0x08000010, 0xb8000010, 0x98000000, 0x60000000, 0x00000008,
+					0xc0000000, 0x90000014, 0x10000010, 0xb8000014, 0x28000000,
+					0x20000010, 0x48000000, 0x08000018, 0x60000000, 0x90000010,
+					0xf0000010, 0x90000008, 0xc0000000, 0x90000010, 0xf0000010,
+					0xb0000008, 0x40000000, 0x90000000, 0xf0000010, 0x90000018,
+					0x60000000, 0x90000010, 0x90000010, 0x90000000, 0x80000000,
+					0x00000010, 0xa0000000, 0x20000000, 0xa0000000, 0x20000010,
+					0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x20000000,
+					0x00000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000040,
+					0x40000002, 0x80000004, 0x80000080, 0x80000006, 0x00000049,
+					0x00000103, 0x80000009, 0x80000012, 0x80000202, 0x00000018,
+					0x00000164, 0x00000408, 0x800000e6 }),
+			new DvInfo(1, 47, 2, 58, 6, new int[] { 0x20000043, 0xb0000040,
+					0xd0000053, 0xd0000022, 0x20000000, 0x60000032, 0x60000043,
+					0x20000040, 0xe0000042, 0x60000002, 0x80000001, 0x00000020,
+					0x00000003, 0x40000052, 0x40000040, 0xe0000052, 0xa0000000,
+					0x80000040, 0x20000001, 0x20000060, 0x80000001, 0x40000042,
+					0xc0000043, 0x40000022, 0x00000003, 0x40000042, 0xc0000043,
+					0xc0000022, 0x00000001, 0x40000002, 0xc0000043, 0x40000062,
+					0x80000001, 0x40000042, 0x40000042, 0x40000002, 0x00000002,
+					0x00000040, 0x80000002, 0x80000000, 0x80000002, 0x80000040,
+					0x00000000, 0x80000040, 0x80000000, 0x00000040, 0x80000000,
+					0x00000040, 0x80000002, 0x00000000, 0x80000000, 0x80000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000004, 0x00000080, 0x00000004, 0x00000009, 0x00000101,
+					0x00000009, 0x00000012, 0x00000202, 0x0000001a, 0x00000124,
+					0x0000040c, 0x00000026, 0x0000004a, 0x0000080a, 0x00000060,
+					0x00000590, 0x00001020, 0x0000039a }),
+			new DvInfo(1, 48, 0, 58, 7, new int[] { 0xb800000a, 0xc8000010,
+					0x2c000010, 0xf4000014, 0xb4000008, 0x08000000, 0x9800000c,
+					0xd8000010, 0x08000010, 0xb8000010, 0x98000000, 0x60000000,
+					0x00000008, 0xc0000000, 0x90000014, 0x10000010, 0xb8000014,
+					0x28000000, 0x20000010, 0x48000000, 0x08000018, 0x60000000,
+					0x90000010, 0xf0000010, 0x90000008, 0xc0000000, 0x90000010,
+					0xf0000010, 0xb0000008, 0x40000000, 0x90000000, 0xf0000010,
+					0x90000018, 0x60000000, 0x90000010, 0x90000010, 0x90000000,
+					0x80000000, 0x00000010, 0xa0000000, 0x20000000, 0xa0000000,
+					0x20000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+					0x20000000, 0x00000010, 0xa0000000, 0x00000000, 0x20000000,
+					0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002,
+					0x40000040, 0x40000002, 0x80000004, 0x80000080, 0x80000006,
+					0x00000049, 0x00000103, 0x80000009, 0x80000012, 0x80000202,
+					0x00000018, 0x00000164, 0x00000408 }),
+			new DvInfo(1, 48, 2, 58, 8, new int[] { 0xe000002a, 0x20000043,
+					0xb0000040, 0xd0000053, 0xd0000022, 0x20000000, 0x60000032,
+					0x60000043, 0x20000040, 0xe0000042, 0x60000002, 0x80000001,
+					0x00000020, 0x00000003, 0x40000052, 0x40000040, 0xe0000052,
+					0xa0000000, 0x80000040, 0x20000001, 0x20000060, 0x80000001,
+					0x40000042, 0xc0000043, 0x40000022, 0x00000003, 0x40000042,
+					0xc0000043, 0xc0000022, 0x00000001, 0x40000002, 0xc0000043,
+					0x40000062, 0x80000001, 0x40000042, 0x40000042, 0x40000002,
+					0x00000002, 0x00000040, 0x80000002, 0x80000000, 0x80000002,
+					0x80000040, 0x00000000, 0x80000040, 0x80000000, 0x00000040,
+					0x80000000, 0x00000040, 0x80000002, 0x00000000, 0x80000000,
+					0x80000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000004, 0x00000080, 0x00000004, 0x00000009,
+					0x00000101, 0x00000009, 0x00000012, 0x00000202, 0x0000001a,
+					0x00000124, 0x0000040c, 0x00000026, 0x0000004a, 0x0000080a,
+					0x00000060, 0x00000590, 0x00001020 }),
+			new DvInfo(1, 49, 0, 58, 9, new int[] { 0x18000000, 0xb800000a,
+					0xc8000010, 0x2c000010, 0xf4000014, 0xb4000008, 0x08000000,
+					0x9800000c, 0xd8000010, 0x08000010, 0xb8000010, 0x98000000,
+					0x60000000, 0x00000008, 0xc0000000, 0x90000014, 0x10000010,
+					0xb8000014, 0x28000000, 0x20000010, 0x48000000, 0x08000018,
+					0x60000000, 0x90000010, 0xf0000010, 0x90000008, 0xc0000000,
+					0x90000010, 0xf0000010, 0xb0000008, 0x40000000, 0x90000000,
+					0xf0000010, 0x90000018, 0x60000000, 0x90000010, 0x90000010,
+					0x90000000, 0x80000000, 0x00000010, 0xa0000000, 0x20000000,
+					0xa0000000, 0x20000010, 0x00000000, 0x20000010, 0x20000000,
+					0x00000010, 0x20000000, 0x00000010, 0xa0000000, 0x00000000,
+					0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001,
+					0x40000002, 0x40000040, 0x40000002, 0x80000004, 0x80000080,
+					0x80000006, 0x00000049, 0x00000103, 0x80000009, 0x80000012,
+					0x80000202, 0x00000018, 0x00000164 }),
+			new DvInfo(1, 49, 2, 58, 10, new int[] { 0x60000000, 0xe000002a,
+					0x20000043, 0xb0000040, 0xd0000053, 0xd0000022, 0x20000000,
+					0x60000032, 0x60000043, 0x20000040, 0xe0000042, 0x60000002,
+					0x80000001, 0x00000020, 0x00000003, 0x40000052, 0x40000040,
+					0xe0000052, 0xa0000000, 0x80000040, 0x20000001, 0x20000060,
+					0x80000001, 0x40000042, 0xc0000043, 0x40000022, 0x00000003,
+					0x40000042, 0xc0000043, 0xc0000022, 0x00000001, 0x40000002,
+					0xc0000043, 0x40000062, 0x80000001, 0x40000042, 0x40000042,
+					0x40000002, 0x00000002, 0x00000040, 0x80000002, 0x80000000,
+					0x80000002, 0x80000040, 0x00000000, 0x80000040, 0x80000000,
+					0x00000040, 0x80000000, 0x00000040, 0x80000002, 0x00000000,
+					0x80000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000004, 0x00000080, 0x00000004,
+					0x00000009, 0x00000101, 0x00000009, 0x00000012, 0x00000202,
+					0x0000001a, 0x00000124, 0x0000040c, 0x00000026, 0x0000004a,
+					0x0000080a, 0x00000060, 0x00000590 }),
+			new DvInfo(1, 50, 0, 65, 11, new int[] { 0x0800000c, 0x18000000,
+					0xb800000a, 0xc8000010, 0x2c000010, 0xf4000014, 0xb4000008,
+					0x08000000, 0x9800000c, 0xd8000010, 0x08000010, 0xb8000010,
+					0x98000000, 0x60000000, 0x00000008, 0xc0000000, 0x90000014,
+					0x10000010, 0xb8000014, 0x28000000, 0x20000010, 0x48000000,
+					0x08000018, 0x60000000, 0x90000010, 0xf0000010, 0x90000008,
+					0xc0000000, 0x90000010, 0xf0000010, 0xb0000008, 0x40000000,
+					0x90000000, 0xf0000010, 0x90000018, 0x60000000, 0x90000010,
+					0x90000010, 0x90000000, 0x80000000, 0x00000010, 0xa0000000,
+					0x20000000, 0xa0000000, 0x20000010, 0x00000000, 0x20000010,
+					0x20000000, 0x00000010, 0x20000000, 0x00000010, 0xa0000000,
+					0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020,
+					0x00000001, 0x40000002, 0x40000040, 0x40000002, 0x80000004,
+					0x80000080, 0x80000006, 0x00000049, 0x00000103, 0x80000009,
+					0x80000012, 0x80000202, 0x00000018 }),
+			new DvInfo(1, 50, 2, 65, 12, new int[] { 0x20000030, 0x60000000,
+					0xe000002a, 0x20000043, 0xb0000040, 0xd0000053, 0xd0000022,
+					0x20000000, 0x60000032, 0x60000043, 0x20000040, 0xe0000042,
+					0x60000002, 0x80000001, 0x00000020, 0x00000003, 0x40000052,
+					0x40000040, 0xe0000052, 0xa0000000, 0x80000040, 0x20000001,
+					0x20000060, 0x80000001, 0x40000042, 0xc0000043, 0x40000022,
+					0x00000003, 0x40000042, 0xc0000043, 0xc0000022, 0x00000001,
+					0x40000002, 0xc0000043, 0x40000062, 0x80000001, 0x40000042,
+					0x40000042, 0x40000002, 0x00000002, 0x00000040, 0x80000002,
+					0x80000000, 0x80000002, 0x80000040, 0x00000000, 0x80000040,
+					0x80000000, 0x00000040, 0x80000000, 0x00000040, 0x80000002,
+					0x00000000, 0x80000000, 0x80000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000004, 0x00000080,
+					0x00000004, 0x00000009, 0x00000101, 0x00000009, 0x00000012,
+					0x00000202, 0x0000001a, 0x00000124, 0x0000040c, 0x00000026,
+					0x0000004a, 0x0000080a, 0x00000060 }),
+			new DvInfo(1, 51, 0, 65, 13, new int[] { 0xe8000000, 0x0800000c,
+					0x18000000, 0xb800000a, 0xc8000010, 0x2c000010, 0xf4000014,
+					0xb4000008, 0x08000000, 0x9800000c, 0xd8000010, 0x08000010,
+					0xb8000010, 0x98000000, 0x60000000, 0x00000008, 0xc0000000,
+					0x90000014, 0x10000010, 0xb8000014, 0x28000000, 0x20000010,
+					0x48000000, 0x08000018, 0x60000000, 0x90000010, 0xf0000010,
+					0x90000008, 0xc0000000, 0x90000010, 0xf0000010, 0xb0000008,
+					0x40000000, 0x90000000, 0xf0000010, 0x90000018, 0x60000000,
+					0x90000010, 0x90000010, 0x90000000, 0x80000000, 0x00000010,
+					0xa0000000, 0x20000000, 0xa0000000, 0x20000010, 0x00000000,
+					0x20000010, 0x20000000, 0x00000010, 0x20000000, 0x00000010,
+					0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+					0x00000020, 0x00000001, 0x40000002, 0x40000040, 0x40000002,
+					0x80000004, 0x80000080, 0x80000006, 0x00000049, 0x00000103,
+					0x80000009, 0x80000012, 0x80000202 }),
+			new DvInfo(1, 51, 2, 65, 14, new int[] { 0xa0000003, 0x20000030,
+					0x60000000, 0xe000002a, 0x20000043, 0xb0000040, 0xd0000053,
+					0xd0000022, 0x20000000, 0x60000032, 0x60000043, 0x20000040,
+					0xe0000042, 0x60000002, 0x80000001, 0x00000020, 0x00000003,
+					0x40000052, 0x40000040, 0xe0000052, 0xa0000000, 0x80000040,
+					0x20000001, 0x20000060, 0x80000001, 0x40000042, 0xc0000043,
+					0x40000022, 0x00000003, 0x40000042, 0xc0000043, 0xc0000022,
+					0x00000001, 0x40000002, 0xc0000043, 0x40000062, 0x80000001,
+					0x40000042, 0x40000042, 0x40000002, 0x00000002, 0x00000040,
+					0x80000002, 0x80000000, 0x80000002, 0x80000040, 0x00000000,
+					0x80000040, 0x80000000, 0x00000040, 0x80000000, 0x00000040,
+					0x80000002, 0x00000000, 0x80000000, 0x80000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004,
+					0x00000080, 0x00000004, 0x00000009, 0x00000101, 0x00000009,
+					0x00000012, 0x00000202, 0x0000001a, 0x00000124, 0x0000040c,
+					0x00000026, 0x0000004a, 0x0000080a }),
+			new DvInfo(1, 52, 0, 65, 15, new int[] { 0x04000010, 0xe8000000,
+					0x0800000c, 0x18000000, 0xb800000a, 0xc8000010, 0x2c000010,
+					0xf4000014, 0xb4000008, 0x08000000, 0x9800000c, 0xd8000010,
+					0x08000010, 0xb8000010, 0x98000000, 0x60000000, 0x00000008,
+					0xc0000000, 0x90000014, 0x10000010, 0xb8000014, 0x28000000,
+					0x20000010, 0x48000000, 0x08000018, 0x60000000, 0x90000010,
+					0xf0000010, 0x90000008, 0xc0000000, 0x90000010, 0xf0000010,
+					0xb0000008, 0x40000000, 0x90000000, 0xf0000010, 0x90000018,
+					0x60000000, 0x90000010, 0x90000010, 0x90000000, 0x80000000,
+					0x00000010, 0xa0000000, 0x20000000, 0xa0000000, 0x20000010,
+					0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x20000000,
+					0x00000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000040,
+					0x40000002, 0x80000004, 0x80000080, 0x80000006, 0x00000049,
+					0x00000103, 0x80000009, 0x80000012 }),
+			new DvInfo(2, 45, 0, 58, 16, new int[] { 0xec000014, 0x0c000002,
+					0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010,
+					0x0000000c, 0xb8000010, 0x08000018, 0x78000010, 0x08000014,
+					0x70000010, 0xb800001c, 0xe8000000, 0xb0000004, 0x58000010,
+					0xb000000c, 0x48000000, 0xb0000000, 0xb8000010, 0x98000010,
+					0xa0000000, 0x00000000, 0x00000000, 0x20000000, 0x80000000,
+					0x00000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+					0x60000000, 0x00000018, 0xe0000000, 0x90000000, 0x30000010,
+					0xb0000000, 0x20000000, 0x20000000, 0xa0000000, 0x00000010,
+					0x80000000, 0x20000000, 0x20000000, 0x20000000, 0x80000000,
+					0x00000010, 0x00000000, 0x20000010, 0xa0000000, 0x00000000,
+					0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020,
+					0x00000001, 0x40000002, 0x40000041, 0x40000022, 0x80000005,
+					0xc0000082, 0xc0000046, 0x4000004b, 0x80000107, 0x00000089,
+					0x00000014, 0x8000024b, 0x0000011b, 0x8000016d, 0x8000041a,
+					0x000002e4, 0x80000054, 0x00000967 }),
+			new DvInfo(2, 46, 0, 58, 17, new int[] { 0x2400001c, 0xec000014,
+					0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018,
+					0xb0000010, 0x0000000c, 0xb8000010, 0x08000018, 0x78000010,
+					0x08000014, 0x70000010, 0xb800001c, 0xe8000000, 0xb0000004,
+					0x58000010, 0xb000000c, 0x48000000, 0xb0000000, 0xb8000010,
+					0x98000010, 0xa0000000, 0x00000000, 0x00000000, 0x20000000,
+					0x80000000, 0x00000010, 0x00000000, 0x20000010, 0x20000000,
+					0x00000010, 0x60000000, 0x00000018, 0xe0000000, 0x90000000,
+					0x30000010, 0xb0000000, 0x20000000, 0x20000000, 0xa0000000,
+					0x00000010, 0x80000000, 0x20000000, 0x20000000, 0x20000000,
+					0x80000000, 0x00000010, 0x00000000, 0x20000010, 0xa0000000,
+					0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+					0x00000020, 0x00000001, 0x40000002, 0x40000041, 0x40000022,
+					0x80000005, 0xc0000082, 0xc0000046, 0x4000004b, 0x80000107,
+					0x00000089, 0x00000014, 0x8000024b, 0x0000011b, 0x8000016d,
+					0x8000041a, 0x000002e4, 0x80000054 }),
+			new DvInfo(2, 46, 2, 58, 18, new int[] { 0x90000070, 0xb0000053,
+					0x30000008, 0x00000043, 0xd0000072, 0xb0000010, 0xf0000062,
+					0xc0000042, 0x00000030, 0xe0000042, 0x20000060, 0xe0000041,
+					0x20000050, 0xc0000041, 0xe0000072, 0xa0000003, 0xc0000012,
+					0x60000041, 0xc0000032, 0x20000001, 0xc0000002, 0xe0000042,
+					0x60000042, 0x80000002, 0x00000000, 0x00000000, 0x80000000,
+					0x00000002, 0x00000040, 0x00000000, 0x80000040, 0x80000000,
+					0x00000040, 0x80000001, 0x00000060, 0x80000003, 0x40000002,
+					0xc0000040, 0xc0000002, 0x80000000, 0x80000000, 0x80000002,
+					0x00000040, 0x00000002, 0x80000000, 0x80000000, 0x80000000,
+					0x00000002, 0x00000040, 0x00000000, 0x80000040, 0x80000002,
+					0x00000000, 0x80000000, 0x80000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004,
+					0x00000080, 0x00000004, 0x00000009, 0x00000105, 0x00000089,
+					0x00000016, 0x0000020b, 0x0000011b, 0x0000012d, 0x0000041e,
+					0x00000224, 0x00000050, 0x0000092e, 0x0000046c, 0x000005b6,
+					0x0000106a, 0x00000b90, 0x00000152 }),
+			new DvInfo(2, 47, 0, 58, 19, new int[] { 0x20000010, 0x2400001c,
+					0xec000014, 0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004,
+					0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010, 0x08000018,
+					0x78000010, 0x08000014, 0x70000010, 0xb800001c, 0xe8000000,
+					0xb0000004, 0x58000010, 0xb000000c, 0x48000000, 0xb0000000,
+					0xb8000010, 0x98000010, 0xa0000000, 0x00000000, 0x00000000,
+					0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+					0x20000000, 0x00000010, 0x60000000, 0x00000018, 0xe0000000,
+					0x90000000, 0x30000010, 0xb0000000, 0x20000000, 0x20000000,
+					0xa0000000, 0x00000010, 0x80000000, 0x20000000, 0x20000000,
+					0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+					0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000041,
+					0x40000022, 0x80000005, 0xc0000082, 0xc0000046, 0x4000004b,
+					0x80000107, 0x00000089, 0x00000014, 0x8000024b, 0x0000011b,
+					0x8000016d, 0x8000041a, 0x000002e4 }),
+			new DvInfo(2, 48, 0, 58, 20, new int[] { 0xbc00001a, 0x20000010,
+					0x2400001c, 0xec000014, 0x0c000002, 0xc0000010, 0xb400001c,
+					0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010,
+					0x08000018, 0x78000010, 0x08000014, 0x70000010, 0xb800001c,
+					0xe8000000, 0xb0000004, 0x58000010, 0xb000000c, 0x48000000,
+					0xb0000000, 0xb8000010, 0x98000010, 0xa0000000, 0x00000000,
+					0x00000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+					0x20000010, 0x20000000, 0x00000010, 0x60000000, 0x00000018,
+					0xe0000000, 0x90000000, 0x30000010, 0xb0000000, 0x20000000,
+					0x20000000, 0xa0000000, 0x00000010, 0x80000000, 0x20000000,
+					0x20000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+					0x20000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002,
+					0x40000041, 0x40000022, 0x80000005, 0xc0000082, 0xc0000046,
+					0x4000004b, 0x80000107, 0x00000089, 0x00000014, 0x8000024b,
+					0x0000011b, 0x8000016d, 0x8000041a }),
+			new DvInfo(2, 49, 0, 58, 21, new int[] { 0x3c000004, 0xbc00001a,
+					0x20000010, 0x2400001c, 0xec000014, 0x0c000002, 0xc0000010,
+					0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c,
+					0xb8000010, 0x08000018, 0x78000010, 0x08000014, 0x70000010,
+					0xb800001c, 0xe8000000, 0xb0000004, 0x58000010, 0xb000000c,
+					0x48000000, 0xb0000000, 0xb8000010, 0x98000010, 0xa0000000,
+					0x00000000, 0x00000000, 0x20000000, 0x80000000, 0x00000010,
+					0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x60000000,
+					0x00000018, 0xe0000000, 0x90000000, 0x30000010, 0xb0000000,
+					0x20000000, 0x20000000, 0xa0000000, 0x00000010, 0x80000000,
+					0x20000000, 0x20000000, 0x20000000, 0x80000000, 0x00000010,
+					0x00000000, 0x20000010, 0xa0000000, 0x00000000, 0x20000000,
+					0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001,
+					0x40000002, 0x40000041, 0x40000022, 0x80000005, 0xc0000082,
+					0xc0000046, 0x4000004b, 0x80000107, 0x00000089, 0x00000014,
+					0x8000024b, 0x0000011b, 0x8000016d }),
+			new DvInfo(2, 49, 2, 58, 22, new int[] { 0xf0000010, 0xf000006a,
+					0x80000040, 0x90000070, 0xb0000053, 0x30000008, 0x00000043,
+					0xd0000072, 0xb0000010, 0xf0000062, 0xc0000042, 0x00000030,
+					0xe0000042, 0x20000060, 0xe0000041, 0x20000050, 0xc0000041,
+					0xe0000072, 0xa0000003, 0xc0000012, 0x60000041, 0xc0000032,
+					0x20000001, 0xc0000002, 0xe0000042, 0x60000042, 0x80000002,
+					0x00000000, 0x00000000, 0x80000000, 0x00000002, 0x00000040,
+					0x00000000, 0x80000040, 0x80000000, 0x00000040, 0x80000001,
+					0x00000060, 0x80000003, 0x40000002, 0xc0000040, 0xc0000002,
+					0x80000000, 0x80000000, 0x80000002, 0x00000040, 0x00000002,
+					0x80000000, 0x80000000, 0x80000000, 0x00000002, 0x00000040,
+					0x00000000, 0x80000040, 0x80000002, 0x00000000, 0x80000000,
+					0x80000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000004, 0x00000080, 0x00000004,
+					0x00000009, 0x00000105, 0x00000089, 0x00000016, 0x0000020b,
+					0x0000011b, 0x0000012d, 0x0000041e, 0x00000224, 0x00000050,
+					0x0000092e, 0x0000046c, 0x000005b6 }),
+			new DvInfo(2, 50, 0, 65, 23, new int[] { 0xb400001c, 0x3c000004,
+					0xbc00001a, 0x20000010, 0x2400001c, 0xec000014, 0x0c000002,
+					0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010,
+					0x0000000c, 0xb8000010, 0x08000018, 0x78000010, 0x08000014,
+					0x70000010, 0xb800001c, 0xe8000000, 0xb0000004, 0x58000010,
+					0xb000000c, 0x48000000, 0xb0000000, 0xb8000010, 0x98000010,
+					0xa0000000, 0x00000000, 0x00000000, 0x20000000, 0x80000000,
+					0x00000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+					0x60000000, 0x00000018, 0xe0000000, 0x90000000, 0x30000010,
+					0xb0000000, 0x20000000, 0x20000000, 0xa0000000, 0x00000010,
+					0x80000000, 0x20000000, 0x20000000, 0x20000000, 0x80000000,
+					0x00000010, 0x00000000, 0x20000010, 0xa0000000, 0x00000000,
+					0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020,
+					0x00000001, 0x40000002, 0x40000041, 0x40000022, 0x80000005,
+					0xc0000082, 0xc0000046, 0x4000004b, 0x80000107, 0x00000089,
+					0x00000014, 0x8000024b, 0x0000011b }),
+			new DvInfo(2, 50, 2, 65, 24, new int[] { 0xd0000072, 0xf0000010,
+					0xf000006a, 0x80000040, 0x90000070, 0xb0000053, 0x30000008,
+					0x00000043, 0xd0000072, 0xb0000010, 0xf0000062, 0xc0000042,
+					0x00000030, 0xe0000042, 0x20000060, 0xe0000041, 0x20000050,
+					0xc0000041, 0xe0000072, 0xa0000003, 0xc0000012, 0x60000041,
+					0xc0000032, 0x20000001, 0xc0000002, 0xe0000042, 0x60000042,
+					0x80000002, 0x00000000, 0x00000000, 0x80000000, 0x00000002,
+					0x00000040, 0x00000000, 0x80000040, 0x80000000, 0x00000040,
+					0x80000001, 0x00000060, 0x80000003, 0x40000002, 0xc0000040,
+					0xc0000002, 0x80000000, 0x80000000, 0x80000002, 0x00000040,
+					0x00000002, 0x80000000, 0x80000000, 0x80000000, 0x00000002,
+					0x00000040, 0x00000000, 0x80000040, 0x80000002, 0x00000000,
+					0x80000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000004, 0x00000080,
+					0x00000004, 0x00000009, 0x00000105, 0x00000089, 0x00000016,
+					0x0000020b, 0x0000011b, 0x0000012d, 0x0000041e, 0x00000224,
+					0x00000050, 0x0000092e, 0x0000046c }),
+			new DvInfo(2, 51, 0, 65, 25, new int[] { 0xc0000010, 0xb400001c,
+					0x3c000004, 0xbc00001a, 0x20000010, 0x2400001c, 0xec000014,
+					0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018,
+					0xb0000010, 0x0000000c, 0xb8000010, 0x08000018, 0x78000010,
+					0x08000014, 0x70000010, 0xb800001c, 0xe8000000, 0xb0000004,
+					0x58000010, 0xb000000c, 0x48000000, 0xb0000000, 0xb8000010,
+					0x98000010, 0xa0000000, 0x00000000, 0x00000000, 0x20000000,
+					0x80000000, 0x00000010, 0x00000000, 0x20000010, 0x20000000,
+					0x00000010, 0x60000000, 0x00000018, 0xe0000000, 0x90000000,
+					0x30000010, 0xb0000000, 0x20000000, 0x20000000, 0xa0000000,
+					0x00000010, 0x80000000, 0x20000000, 0x20000000, 0x20000000,
+					0x80000000, 0x00000010, 0x00000000, 0x20000010, 0xa0000000,
+					0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+					0x00000020, 0x00000001, 0x40000002, 0x40000041, 0x40000022,
+					0x80000005, 0xc0000082, 0xc0000046, 0x4000004b, 0x80000107,
+					0x00000089, 0x00000014, 0x8000024b }),
+			new DvInfo(2, 51, 2, 65, 26, new int[] { 0x00000043, 0xd0000072,
+					0xf0000010, 0xf000006a, 0x80000040, 0x90000070, 0xb0000053,
+					0x30000008, 0x00000043, 0xd0000072, 0xb0000010, 0xf0000062,
+					0xc0000042, 0x00000030, 0xe0000042, 0x20000060, 0xe0000041,
+					0x20000050, 0xc0000041, 0xe0000072, 0xa0000003, 0xc0000012,
+					0x60000041, 0xc0000032, 0x20000001, 0xc0000002, 0xe0000042,
+					0x60000042, 0x80000002, 0x00000000, 0x00000000, 0x80000000,
+					0x00000002, 0x00000040, 0x00000000, 0x80000040, 0x80000000,
+					0x00000040, 0x80000001, 0x00000060, 0x80000003, 0x40000002,
+					0xc0000040, 0xc0000002, 0x80000000, 0x80000000, 0x80000002,
+					0x00000040, 0x00000002, 0x80000000, 0x80000000, 0x80000000,
+					0x00000002, 0x00000040, 0x00000000, 0x80000040, 0x80000002,
+					0x00000000, 0x80000000, 0x80000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004,
+					0x00000080, 0x00000004, 0x00000009, 0x00000105, 0x00000089,
+					0x00000016, 0x0000020b, 0x0000011b, 0x0000012d, 0x0000041e,
+					0x00000224, 0x00000050, 0x0000092e }),
+			new DvInfo(2, 52, 0, 65, 27, new int[] { 0x0c000002, 0xc0000010,
+					0xb400001c, 0x3c000004, 0xbc00001a, 0x20000010, 0x2400001c,
+					0xec000014, 0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004,
+					0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010, 0x08000018,
+					0x78000010, 0x08000014, 0x70000010, 0xb800001c, 0xe8000000,
+					0xb0000004, 0x58000010, 0xb000000c, 0x48000000, 0xb0000000,
+					0xb8000010, 0x98000010, 0xa0000000, 0x00000000, 0x00000000,
+					0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+					0x20000000, 0x00000010, 0x60000000, 0x00000018, 0xe0000000,
+					0x90000000, 0x30000010, 0xb0000000, 0x20000000, 0x20000000,
+					0xa0000000, 0x00000010, 0x80000000, 0x20000000, 0x20000000,
+					0x20000000, 0x80000000, 0x00000010, 0x00000000, 0x20000010,
+					0xa0000000, 0x00000000, 0x20000000, 0x20000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000001, 0x00000020, 0x00000001, 0x40000002, 0x40000041,
+					0x40000022, 0x80000005, 0xc0000082, 0xc0000046, 0x4000004b,
+					0x80000107, 0x00000089, 0x00000014 }),
+			new DvInfo(2, 53, 0, 65, 28, new int[] { 0xcc000014, 0x0c000002,
+					0xc0000010, 0xb400001c, 0x3c000004, 0xbc00001a, 0x20000010,
+					0x2400001c, 0xec000014, 0x0c000002, 0xc0000010, 0xb400001c,
+					0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c, 0xb8000010,
+					0x08000018, 0x78000010, 0x08000014, 0x70000010, 0xb800001c,
+					0xe8000000, 0xb0000004, 0x58000010, 0xb000000c, 0x48000000,
+					0xb0000000, 0xb8000010, 0x98000010, 0xa0000000, 0x00000000,
+					0x00000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+					0x20000010, 0x20000000, 0x00000010, 0x60000000, 0x00000018,
+					0xe0000000, 0x90000000, 0x30000010, 0xb0000000, 0x20000000,
+					0x20000000, 0xa0000000, 0x00000010, 0x80000000, 0x20000000,
+					0x20000000, 0x20000000, 0x80000000, 0x00000010, 0x00000000,
+					0x20000010, 0xa0000000, 0x00000000, 0x20000000, 0x20000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000001, 0x00000020, 0x00000001, 0x40000002,
+					0x40000041, 0x40000022, 0x80000005, 0xc0000082, 0xc0000046,
+					0x4000004b, 0x80000107, 0x00000089 }),
+			new DvInfo(2, 54, 0, 65, 29, new int[] { 0x0400001c, 0xcc000014,
+					0x0c000002, 0xc0000010, 0xb400001c, 0x3c000004, 0xbc00001a,
+					0x20000010, 0x2400001c, 0xec000014, 0x0c000002, 0xc0000010,
+					0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010, 0x0000000c,
+					0xb8000010, 0x08000018, 0x78000010, 0x08000014, 0x70000010,
+					0xb800001c, 0xe8000000, 0xb0000004, 0x58000010, 0xb000000c,
+					0x48000000, 0xb0000000, 0xb8000010, 0x98000010, 0xa0000000,
+					0x00000000, 0x00000000, 0x20000000, 0x80000000, 0x00000010,
+					0x00000000, 0x20000010, 0x20000000, 0x00000010, 0x60000000,
+					0x00000018, 0xe0000000, 0x90000000, 0x30000010, 0xb0000000,
+					0x20000000, 0x20000000, 0xa0000000, 0x00000010, 0x80000000,
+					0x20000000, 0x20000000, 0x20000000, 0x80000000, 0x00000010,
+					0x00000000, 0x20000010, 0xa0000000, 0x00000000, 0x20000000,
+					0x20000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000001, 0x00000020, 0x00000001,
+					0x40000002, 0x40000041, 0x40000022, 0x80000005, 0xc0000082,
+					0xc0000046, 0x4000004b, 0x80000107 }),
+			new DvInfo(2, 55, 0, 65, 30, new int[] { 0x00000010, 0x0400001c,
+					0xcc000014, 0x0c000002, 0xc0000010, 0xb400001c, 0x3c000004,
+					0xbc00001a, 0x20000010, 0x2400001c, 0xec000014, 0x0c000002,
+					0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018, 0xb0000010,
+					0x0000000c, 0xb8000010, 0x08000018, 0x78000010, 0x08000014,
+					0x70000010, 0xb800001c, 0xe8000000, 0xb0000004, 0x58000010,
+					0xb000000c, 0x48000000, 0xb0000000, 0xb8000010, 0x98000010,
+					0xa0000000, 0x00000000, 0x00000000, 0x20000000, 0x80000000,
+					0x00000010, 0x00000000, 0x20000010, 0x20000000, 0x00000010,
+					0x60000000, 0x00000018, 0xe0000000, 0x90000000, 0x30000010,
+					0xb0000000, 0x20000000, 0x20000000, 0xa0000000, 0x00000010,
+					0x80000000, 0x20000000, 0x20000000, 0x20000000, 0x80000000,
+					0x00000010, 0x00000000, 0x20000010, 0xa0000000, 0x00000000,
+					0x20000000, 0x20000000, 0x00000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000020,
+					0x00000001, 0x40000002, 0x40000041, 0x40000022, 0x80000005,
+					0xc0000082, 0xc0000046, 0x4000004b }),
+			new DvInfo(2, 56, 0, 65, 31, new int[] { 0x2600001a, 0x00000010,
+					0x0400001c, 0xcc000014, 0x0c000002, 0xc0000010, 0xb400001c,
+					0x3c000004, 0xbc00001a, 0x20000010, 0x2400001c, 0xec000014,
+					0x0c000002, 0xc0000010, 0xb400001c, 0x2c000004, 0xbc000018,
+					0xb0000010, 0x0000000c, 0xb8000010, 0x08000018, 0x78000010,
+					0x08000014, 0x70000010, 0xb800001c, 0xe8000000, 0xb0000004,
+					0x58000010, 0xb000000c, 0x48000000, 0xb0000000, 0xb8000010,
+					0x98000010, 0xa0000000, 0x00000000, 0x00000000, 0x20000000,
+					0x80000000, 0x00000010, 0x00000000, 0x20000010, 0x20000000,
+					0x00000010, 0x60000000, 0x00000018, 0xe0000000, 0x90000000,
+					0x30000010, 0xb0000000, 0x20000000, 0x20000000, 0xa0000000,
+					0x00000010, 0x80000000, 0x20000000, 0x20000000, 0x20000000,
+					0x80000000, 0x00000010, 0x00000000, 0x20000010, 0xa0000000,
+					0x00000000, 0x20000000, 0x20000000, 0x00000000, 0x00000000,
+					0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+					0x00000020, 0x00000001, 0x40000002, 0x40000041, 0x40000022,
+					0x80000005, 0xc0000082, 0xc0000046 }), };
+
+	static {
+		// Assert the DV array is indexed by maskb; that is DV block using
+		// maskb = N must be at array index N.
+		for (int i = 0; i < DV.length; i++) {
+			if (i != DV[i].maskb) {
+				throw new IllegalStateException("must be indexed by maskb"); //$NON-NLS-1$
+			}
+		}
+	}
+}
diff --git a/pom.xml b/pom.xml
index b8154c1..76ea009 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.2-SNAPSHOT</version>
+  <version>4.7.2-SNAPSHOT</version>
 
   <name>JGit - Parent</name>
   <url>${jgit-url}</url>
@@ -191,8 +191,8 @@
     <maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format>
     <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
 
-    <jgit-last-release-version>4.2.0.201601211800-r</jgit-last-release-version>
-    <jsch-version>0.1.53</jsch-version>
+    <jgit-last-release-version>4.6.0.201612231935-r</jgit-last-release-version>
+    <jsch-version>0.1.54</jsch-version>
     <javaewah-version>1.1.6</javaewah-version>
     <junit-version>4.12</junit-version>
     <test-fork-count>1C</test-fork-count>
@@ -208,6 +208,7 @@
     <maven-javadoc-plugin-version>2.10.4</maven-javadoc-plugin-version>
     <tycho-extras-version>0.26.0</tycho-extras-version>
     <gson-version>2.2.4</gson-version>
+    <findbugs-maven-plugin-version>3.0.4</findbugs-maven-plugin-version>
 
     <!-- Properties to enable jacoco code coverage analysis -->
     <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
@@ -258,7 +259,7 @@
 
         <plugin>
           <artifactId>maven-compiler-plugin</artifactId>
-          <version>3.6.0</version>
+          <version>3.6.1</version>
           <configuration>
             <encoding>UTF-8</encoding>
             <source>1.8</source>
@@ -308,7 +309,7 @@
             <dependency>
               <groupId>com.google.errorprone</groupId>
               <artifactId>error_prone_core</artifactId>
-              <version>2.0.15</version>
+              <version>2.0.19</version>
             </dependency>
           </dependencies>
         </plugin>
@@ -321,7 +322,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-shade-plugin</artifactId>
-          <version>2.4.3</version>
+          <version>3.0.0</version>
         </plugin>
 
         <plugin>
@@ -361,13 +362,13 @@
         <plugin>
           <groupId>org.codehaus.mojo</groupId>
           <artifactId>build-helper-maven-plugin</artifactId>
-          <version>1.12</version>
+          <version>3.0.0</version>
         </plugin>
 
         <plugin>
           <groupId>org.codehaus.mojo</groupId>
           <artifactId>findbugs-maven-plugin</artifactId>
-          <version>3.0.4</version>
+          <version>${findbugs-maven-plugin-version}</version>
           <configuration>
             <findbugsXmlOutput>true</findbugsXmlOutput>
             <failOnError>false</failOnError>
@@ -419,7 +420,7 @@
         <plugin>
           <groupId>org.jacoco</groupId>
           <artifactId>jacoco-maven-plugin</artifactId>
-          <version>0.7.8</version>
+          <version>0.7.9</version>
         </plugin>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
@@ -572,7 +573,7 @@
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>findbugs-maven-plugin</artifactId>
-        <version>3.0.3</version>
+        <version>${findbugs-maven-plugin-version}</version>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
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,
+      )